enhance typeofs (#3556)
This commit is contained in:
@@ -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");
|
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) {
|
AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
|
||||||
return !this.definition().undeclared
|
return this.defined
|
||||||
|
|| !this.definition().undeclared
|
||||||
|| compressor.option("unsafe") && global_names[this.name];
|
|| compressor.option("unsafe") && global_names[this.name];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4570,6 +4571,49 @@ merge(Compressor.prototype, {
|
|||||||
return if_break_in_loop(self, compressor);
|
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) {
|
OPT(AST_If, function(self, compressor) {
|
||||||
if (is_empty(self.alternative)) self.alternative = null;
|
if (is_empty(self.alternative)) self.alternative = null;
|
||||||
|
|
||||||
@@ -4711,6 +4755,7 @@ merge(Compressor.prototype, {
|
|||||||
body: [ self, body ]
|
body: [ self, body ]
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
|
if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -5698,7 +5743,7 @@ merge(Compressor.prototype, {
|
|||||||
// "undefined" == typeof x => undefined === x
|
// "undefined" == typeof x => undefined === x
|
||||||
else if (compressor.option("typeofs")
|
else if (compressor.option("typeofs")
|
||||||
&& self.left instanceof AST_String
|
&& self.left instanceof AST_String
|
||||||
&& self.left.value == "undefined"
|
&& self.left.getValue() == "undefined"
|
||||||
&& self.right instanceof AST_UnaryPrefix
|
&& self.right instanceof AST_UnaryPrefix
|
||||||
&& self.right.operator == "typeof") {
|
&& self.right.operator == "typeof") {
|
||||||
var expr = self.right.expression;
|
var expr = self.right.expression;
|
||||||
@@ -6089,6 +6134,14 @@ merge(Compressor.prototype, {
|
|||||||
break;
|
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")) {
|
if (compressor.option("unsafe")) {
|
||||||
var indexRight = is_indexFn(self.right);
|
var indexRight = is_indexFn(self.right);
|
||||||
if (in_bool
|
if (in_bool
|
||||||
@@ -6651,8 +6704,8 @@ merge(Compressor.prototype, {
|
|||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
|
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
|
||||||
if (is_true(self.consequent)) {
|
if (is_true(consequent)) {
|
||||||
if (is_false(self.alternative)) {
|
if (is_false(alternative)) {
|
||||||
// c ? true : false ---> !!c
|
// c ? true : false ---> !!c
|
||||||
return booleanize(condition);
|
return booleanize(condition);
|
||||||
}
|
}
|
||||||
@@ -6660,11 +6713,11 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "||",
|
operator: "||",
|
||||||
left: booleanize(condition),
|
left: booleanize(condition),
|
||||||
right: self.alternative
|
right: alternative
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_false(self.consequent)) {
|
if (is_false(consequent)) {
|
||||||
if (is_true(self.alternative)) {
|
if (is_true(alternative)) {
|
||||||
// c ? false : true ---> !c
|
// c ? false : true ---> !c
|
||||||
return booleanize(condition.negate(compressor));
|
return booleanize(condition.negate(compressor));
|
||||||
}
|
}
|
||||||
@@ -6672,26 +6725,26 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: booleanize(condition.negate(compressor)),
|
left: booleanize(condition.negate(compressor)),
|
||||||
right: self.alternative
|
right: alternative
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_true(self.alternative)) {
|
if (is_true(alternative)) {
|
||||||
// c ? x : true ---> !c || x
|
// c ? x : true ---> !c || x
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "||",
|
operator: "||",
|
||||||
left: booleanize(condition.negate(compressor)),
|
left: booleanize(condition.negate(compressor)),
|
||||||
right: self.consequent
|
right: consequent
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_false(self.alternative)) {
|
if (is_false(alternative)) {
|
||||||
// c ? x : false ---> !!c && x
|
// c ? x : false ---> !!c && x
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "&&",
|
operator: "&&",
|
||||||
left: booleanize(condition),
|
left: booleanize(condition),
|
||||||
right: self.consequent
|
right: consequent
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
function booleanize(node) {
|
function booleanize(node) {
|
||||||
|
|||||||
@@ -295,3 +295,117 @@ issue_2728_6: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "function undefined"
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user