Skip to content

Commit

Permalink
enhance typeofs (#3556)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexlamsl committed Oct 31, 2019
1 parent ec7f071 commit 1858c20
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 13 deletions.
79 changes: 66 additions & 13 deletions lib/compress.js
Expand Up @@ -1042,7 +1042,8 @@ merge(Compressor.prototype, {

var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Math Number parseFloat parseInt RangeError ReferenceError RegExp Object setInterval setTimeout String SyntaxError TypeError unescape URIError");
AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
return !this.definition().undeclared
return this.defined
|| !this.definition().undeclared
|| compressor.option("unsafe") && global_names[this.name];
});

Expand Down Expand Up @@ -4570,6 +4571,49 @@ merge(Compressor.prototype, {
return if_break_in_loop(self, compressor);
});

function mark_locally_defined(condition, consequent, alternative, operator) {
if (!(condition instanceof AST_Binary)) return;
if (!(condition.left instanceof AST_String)) {
if (!operator) operator = condition.operator;
if (condition.operator != operator) return;
switch (operator) {
case "&&":
case "||":
mark_locally_defined(condition.left, consequent, alternative, operator);
mark_locally_defined(condition.right, consequent, alternative, operator);
break;
}
return;
}
if (!(condition.right instanceof AST_UnaryPrefix)) return;
if (condition.right.operator != "typeof") return;
var sym = condition.right.expression;
if (!is_undeclared_ref(sym)) return;
var body;
var undef = condition.left.getValue() == "undefined";
switch (condition.operator) {
case "==":
body = undef ? alternative : consequent;
break;
case "!=":
body = undef ? consequent : alternative;
break;
default:
return;
}
if (!body) return;
var def = sym.definition();
var tw = new TreeWalker(function(node) {
if (node instanceof AST_Scope) {
var parent = tw.parent();
if (parent instanceof AST_Call && parent.expression === node) return;
return true;
}
if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
});
body.walk(tw);
}

OPT(AST_If, function(self, compressor) {
if (is_empty(self.alternative)) self.alternative = null;

Expand Down Expand Up @@ -4711,6 +4755,7 @@ merge(Compressor.prototype, {
body: [ self, body ]
}).optimize(compressor);
}
if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
return self;
});

Expand Down Expand Up @@ -5698,7 +5743,7 @@ merge(Compressor.prototype, {
// "undefined" == typeof x => undefined === x
else if (compressor.option("typeofs")
&& self.left instanceof AST_String
&& self.left.value == "undefined"
&& self.left.getValue() == "undefined"
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "typeof") {
var expr = self.right.expression;
Expand Down Expand Up @@ -6089,6 +6134,14 @@ merge(Compressor.prototype, {
break;
}
}
if (compressor.option("typeofs")) switch (self.operator) {
case "&&":
mark_locally_defined(self.left, self.right, null, "&&");
break;
case "||":
mark_locally_defined(self.left, null, self.right, "||");
break;
}
if (compressor.option("unsafe")) {
var indexRight = is_indexFn(self.right);
if (in_bool
Expand Down Expand Up @@ -6651,47 +6704,47 @@ merge(Compressor.prototype, {
}).optimize(compressor);
}
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
if (is_true(self.consequent)) {
if (is_false(self.alternative)) {
if (is_true(consequent)) {
if (is_false(alternative)) {
// c ? true : false ---> !!c
return booleanize(condition);
}
// c ? true : x ---> !!c || x
return make_node(AST_Binary, self, {
operator: "||",
left: booleanize(condition),
right: self.alternative
right: alternative
});
}
if (is_false(self.consequent)) {
if (is_true(self.alternative)) {
if (is_false(consequent)) {
if (is_true(alternative)) {
// c ? false : true ---> !c
return booleanize(condition.negate(compressor));
}
// c ? false : x ---> !c && x
return make_node(AST_Binary, self, {
operator: "&&",
left: booleanize(condition.negate(compressor)),
right: self.alternative
right: alternative
});
}
if (is_true(self.alternative)) {
if (is_true(alternative)) {
// c ? x : true ---> !c || x
return make_node(AST_Binary, self, {
operator: "||",
left: booleanize(condition.negate(compressor)),
right: self.consequent
right: consequent
});
}
if (is_false(self.alternative)) {
if (is_false(alternative)) {
// c ? x : false ---> !!c && x
return make_node(AST_Binary, self, {
operator: "&&",
left: booleanize(condition),
right: self.consequent
right: consequent
});
}

if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
return self;

function booleanize(node) {
Expand Down
114 changes: 114 additions & 0 deletions test/compress/typeof.js
Expand Up @@ -295,3 +295,117 @@ issue_2728_6: {
}
expect_stdout: "function undefined"
}

typeof_defined_1: {
options = {
side_effects: true,
typeofs: true,
}
input: {
"undefined" == typeof A && A;
"undefined" != typeof A && A;
"undefined" == typeof A || A;
"undefined" != typeof A || A;
}
expect: {
"undefined" == typeof A && A;
"undefined" != typeof A || A;
}
}

typeof_defined_2: {
options = {
side_effects: true,
typeofs: true,
}
input: {
"function" == typeof A && A;
"function" != typeof A && A;
"function" == typeof A || A;
"function" != typeof A || A;
}
expect: {
"function" != typeof A && A;
"function" == typeof A || A;
}
}

typeof_defined_3: {
options = {
side_effects: true,
typeofs: true,
}
input: {
"undefined" == typeof A && "undefined" == typeof B && (A, B);
"undefined" == typeof A && "undefined" != typeof B && (A, B);
"undefined" != typeof A && "undefined" == typeof B && (A, B);
"undefined" != typeof A && "undefined" != typeof B && (A, B);
"undefined" == typeof A && "undefined" == typeof B || (A, B);
"undefined" == typeof A && "undefined" != typeof B || (A, B);
"undefined" != typeof A && "undefined" == typeof B || (A, B);
"undefined" != typeof A && "undefined" != typeof B || (A, B);
"undefined" == typeof A || "undefined" == typeof B && (A, B);
"undefined" == typeof A || "undefined" != typeof B && (A, B);
"undefined" != typeof A || "undefined" == typeof B && (A, B);
"undefined" != typeof A || "undefined" != typeof B && (A, B);
"undefined" == typeof A || "undefined" == typeof B || (A, B);
"undefined" == typeof A || "undefined" != typeof B || (A, B);
"undefined" != typeof A || "undefined" == typeof B || (A, B);
"undefined" != typeof A || "undefined" != typeof B || (A, B);
}
expect: {
"undefined" == typeof A && "undefined" == typeof B && (A, B);
"undefined" == typeof A && "undefined" != typeof B && A;
"undefined" != typeof A && "undefined" == typeof B && B;
"undefined" == typeof A && "undefined" == typeof B || (A, B);
"undefined" == typeof A && "undefined" != typeof B || (A, B);
"undefined" != typeof A && "undefined" == typeof B || (A, B);
"undefined" != typeof A && "undefined" != typeof B || (A, B);
"undefined" == typeof A || "undefined" == typeof B && B;
"undefined" != typeof A || "undefined" == typeof B && (A, B);
"undefined" != typeof A || "undefined" != typeof B && A;
"undefined" == typeof A || "undefined" != typeof B || B;
"undefined" != typeof A || "undefined" == typeof B || A;
"undefined" != typeof A || "undefined" != typeof B || (A, B);
}
}

typeof_defined_4: {
options = {
side_effects: true,
typeofs: true,
}
input: {
"object" == typeof A && "object" == typeof B && (A, B);
"object" == typeof A && "object" != typeof B && (A, B);
"object" != typeof A && "object" == typeof B && (A, B);
"object" != typeof A && "object" != typeof B && (A, B);
"object" == typeof A && "object" == typeof B || (A, B);
"object" == typeof A && "object" != typeof B || (A, B);
"object" != typeof A && "object" == typeof B || (A, B);
"object" != typeof A && "object" != typeof B || (A, B);
"object" == typeof A || "object" == typeof B && (A, B);
"object" == typeof A || "object" != typeof B && (A, B);
"object" != typeof A || "object" == typeof B && (A, B);
"object" != typeof A || "object" != typeof B && (A, B);
"object" == typeof A || "object" == typeof B || (A, B);
"object" == typeof A || "object" != typeof B || (A, B);
"object" != typeof A || "object" == typeof B || (A, B);
"object" != typeof A || "object" != typeof B || (A, B);
}
expect: {
"object" == typeof A && "object" != typeof B && B;
"object" != typeof A && "object" == typeof B && A;
"object" != typeof A && "object" != typeof B && (A, B);
"object" == typeof A && "object" == typeof B || (A, B);
"object" == typeof A && "object" != typeof B || (A, B);
"object" != typeof A && "object" == typeof B || (A, B);
"object" != typeof A && "object" != typeof B || (A, B);
"object" == typeof A || "object" == typeof B && A;
"object" == typeof A || "object" != typeof B && (A, B);
"object" != typeof A || "object" != typeof B && B;
"object" == typeof A || "object" == typeof B || (A, B);
"object" == typeof A || "object" != typeof B || A;
"object" != typeof A || "object" == typeof B || B;
}
}

0 comments on commit 1858c20

Please sign in to comment.