@@ -2365,7 +2365,11 @@ merge(Compressor.prototype, {
|
|||||||
// descendant of AST_Node.
|
// descendant of AST_Node.
|
||||||
AST_Node.DEFMETHOD("evaluate", function(compressor){
|
AST_Node.DEFMETHOD("evaluate", function(compressor){
|
||||||
if (!compressor.option("evaluate")) return this;
|
if (!compressor.option("evaluate")) return this;
|
||||||
var val = this._eval(compressor, 1);
|
var cached = [];
|
||||||
|
var val = this._eval(compressor, cached, 1);
|
||||||
|
cached.forEach(function(node) {
|
||||||
|
delete node._eval;
|
||||||
|
});
|
||||||
if (!val || val instanceof RegExp) return val;
|
if (!val || val instanceof RegExp) return val;
|
||||||
if (typeof val == "function" || typeof val == "object") return this;
|
if (typeof val == "function" || typeof val == "object") return this;
|
||||||
return val;
|
return val;
|
||||||
@@ -2401,12 +2405,12 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
def(AST_Array, function(compressor, depth) {
|
def(AST_Array, function(compressor, cached, depth) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var elements = [];
|
var elements = [];
|
||||||
for (var i = 0, len = this.elements.length; i < len; i++) {
|
for (var i = 0, len = this.elements.length; i < len; i++) {
|
||||||
var element = this.elements[i];
|
var element = this.elements[i];
|
||||||
var value = element._eval(compressor, depth);
|
var value = element._eval(compressor, cached, depth);
|
||||||
if (element === value) return this;
|
if (element === value) return this;
|
||||||
elements.push(value);
|
elements.push(value);
|
||||||
}
|
}
|
||||||
@@ -2414,7 +2418,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
def(AST_Object, function(compressor, depth) {
|
def(AST_Object, function(compressor, cached, depth) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var val = {};
|
var val = {};
|
||||||
for (var i = 0, len = this.properties.length; i < len; i++) {
|
for (var i = 0, len = this.properties.length; i < len; i++) {
|
||||||
@@ -2423,14 +2427,14 @@ merge(Compressor.prototype, {
|
|||||||
if (key instanceof AST_Symbol) {
|
if (key instanceof AST_Symbol) {
|
||||||
key = key.name;
|
key = key.name;
|
||||||
} else if (key instanceof AST_Node) {
|
} else if (key instanceof AST_Node) {
|
||||||
key = key._eval(compressor, depth);
|
key = key._eval(compressor, cached, depth);
|
||||||
if (key === prop.key) return this;
|
if (key === prop.key) return this;
|
||||||
}
|
}
|
||||||
if (typeof Object.prototype[key] === 'function') {
|
if (typeof Object.prototype[key] === 'function') {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (prop.value instanceof AST_Function) continue;
|
if (prop.value instanceof AST_Function) continue;
|
||||||
val[key] = prop.value._eval(compressor, depth);
|
val[key] = prop.value._eval(compressor, cached, depth);
|
||||||
if (val[key] === prop.value) return this;
|
if (val[key] === prop.value) return this;
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
@@ -2438,7 +2442,7 @@ merge(Compressor.prototype, {
|
|||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
var non_converting_unary = makePredicate("! typeof void");
|
var non_converting_unary = makePredicate("! typeof void");
|
||||||
def(AST_UnaryPrefix, function(compressor, depth) {
|
def(AST_UnaryPrefix, function(compressor, cached, depth) {
|
||||||
var e = this.expression;
|
var e = this.expression;
|
||||||
// Function would be evaluated to an array and so typeof would
|
// Function would be evaluated to an array and so typeof would
|
||||||
// incorrectly return 'object'. Hence making is a special case.
|
// incorrectly return 'object'. Hence making is a special case.
|
||||||
@@ -2450,7 +2454,7 @@ merge(Compressor.prototype, {
|
|||||||
return typeof function(){};
|
return typeof function(){};
|
||||||
}
|
}
|
||||||
if (!non_converting_unary(this.operator)) depth++;
|
if (!non_converting_unary(this.operator)) depth++;
|
||||||
e = e._eval(compressor, depth);
|
e = e._eval(compressor, cached, depth);
|
||||||
if (e === this.expression) return this;
|
if (e === this.expression) return this;
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
case "!": return !e;
|
case "!": return !e;
|
||||||
@@ -2467,11 +2471,11 @@ merge(Compressor.prototype, {
|
|||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
var non_converting_binary = makePredicate("&& || === !==");
|
var non_converting_binary = makePredicate("&& || === !==");
|
||||||
def(AST_Binary, function(compressor, depth) {
|
def(AST_Binary, function(compressor, cached, depth) {
|
||||||
if (!non_converting_binary(this.operator)) depth++;
|
if (!non_converting_binary(this.operator)) depth++;
|
||||||
var left = this.left._eval(compressor, depth);
|
var left = this.left._eval(compressor, cached, depth);
|
||||||
if (left === this.left) return this;
|
if (left === this.left) return this;
|
||||||
var right = this.right._eval(compressor, depth);
|
var right = this.right._eval(compressor, cached, depth);
|
||||||
if (right === this.right) return this;
|
if (right === this.right) return this;
|
||||||
var result;
|
var result;
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
@@ -2505,27 +2509,28 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
def(AST_Conditional, function(compressor, depth) {
|
def(AST_Conditional, function(compressor, cached, depth) {
|
||||||
var condition = this.condition._eval(compressor, depth);
|
var condition = this.condition._eval(compressor, cached, depth);
|
||||||
if (condition === this.condition) return this;
|
if (condition === this.condition) return this;
|
||||||
var node = condition ? this.consequent : this.alternative;
|
var node = condition ? this.consequent : this.alternative;
|
||||||
var value = node._eval(compressor, depth);
|
var value = node._eval(compressor, cached, depth);
|
||||||
return value === node ? this : value;
|
return value === node ? this : value;
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(compressor, depth) {
|
def(AST_SymbolRef, function(compressor, cached, depth) {
|
||||||
var fixed = this.fixed_value();
|
var fixed = this.fixed_value();
|
||||||
if (!fixed) return this;
|
if (!fixed) return this;
|
||||||
var value;
|
var value;
|
||||||
if (HOP(fixed, "_eval")) {
|
if (cached.indexOf(fixed) >= 0) {
|
||||||
value = fixed._eval();
|
value = fixed._eval();
|
||||||
} else {
|
} else {
|
||||||
this._eval = return_this;
|
this._eval = return_this;
|
||||||
value = fixed._eval(compressor, depth);
|
value = fixed._eval(compressor, cached, depth);
|
||||||
delete this._eval;
|
delete this._eval;
|
||||||
if (value === fixed) return this;
|
if (value === fixed) return this;
|
||||||
fixed._eval = function() {
|
fixed._eval = function() {
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
cached.push(fixed);
|
||||||
}
|
}
|
||||||
if (value && typeof value == "object") {
|
if (value && typeof value == "object") {
|
||||||
var escaped = this.definition().escaped;
|
var escaped = this.definition().escaped;
|
||||||
@@ -2560,11 +2565,11 @@ merge(Compressor.prototype, {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
convert_to_predicate(static_values);
|
convert_to_predicate(static_values);
|
||||||
def(AST_PropAccess, function(compressor, depth) {
|
def(AST_PropAccess, function(compressor, cached, depth) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var key = this.property;
|
var key = this.property;
|
||||||
if (key instanceof AST_Node) {
|
if (key instanceof AST_Node) {
|
||||||
key = key._eval(compressor, depth);
|
key = key._eval(compressor, cached, depth);
|
||||||
if (key === this.property) return this;
|
if (key === this.property) return this;
|
||||||
}
|
}
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
@@ -2573,7 +2578,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!(static_values[exp.name] || return_false)(key)) return this;
|
if (!(static_values[exp.name] || return_false)(key)) return this;
|
||||||
val = global_objs[exp.name];
|
val = global_objs[exp.name];
|
||||||
} else {
|
} else {
|
||||||
val = exp._eval(compressor, depth + 1);
|
val = exp._eval(compressor, cached, depth + 1);
|
||||||
if (!val || val === exp || !HOP(val, key)) return this;
|
if (!val || val === exp || !HOP(val, key)) return this;
|
||||||
if (typeof val == "function") switch (key) {
|
if (typeof val == "function") switch (key) {
|
||||||
case "name":
|
case "name":
|
||||||
@@ -2588,12 +2593,12 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
def(AST_Call, function(compressor, depth) {
|
def(AST_Call, function(compressor, cached, depth) {
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||||
var key = exp.property;
|
var key = exp.property;
|
||||||
if (key instanceof AST_Node) {
|
if (key instanceof AST_Node) {
|
||||||
key = key._eval(compressor, depth);
|
key = key._eval(compressor, cached, depth);
|
||||||
if (key === exp.property) return this;
|
if (key === exp.property) return this;
|
||||||
}
|
}
|
||||||
var val;
|
var val;
|
||||||
@@ -2602,13 +2607,13 @@ merge(Compressor.prototype, {
|
|||||||
if (!(static_fns[e.name] || return_false)(key)) return this;
|
if (!(static_fns[e.name] || return_false)(key)) return this;
|
||||||
val = global_objs[e.name];
|
val = global_objs[e.name];
|
||||||
} else {
|
} else {
|
||||||
val = e._eval(compressor, depth + 1);
|
val = e._eval(compressor, cached, depth + 1);
|
||||||
if (val === e || !(val && native_fns[val.constructor.name] || return_false)(key)) return this;
|
if (val === e || !(val && native_fns[val.constructor.name] || return_false)(key)) return this;
|
||||||
}
|
}
|
||||||
var args = [];
|
var args = [];
|
||||||
for (var i = 0, len = this.args.length; i < len; i++) {
|
for (var i = 0, len = this.args.length; i < len; i++) {
|
||||||
var arg = this.args[i];
|
var arg = this.args[i];
|
||||||
var value = arg._eval(compressor, depth);
|
var value = arg._eval(compressor, cached, depth);
|
||||||
if (arg === value) return this;
|
if (arg === value) return this;
|
||||||
args.push(value);
|
args.push(value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1535,3 +1535,34 @@ issue_2926_2: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "function"
|
expect_stdout: "function"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_2968: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
evaluate: true,
|
||||||
|
inline: true,
|
||||||
|
passes: 2,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var c = "FAIL";
|
||||||
|
(function() {
|
||||||
|
(function(a, b) {
|
||||||
|
a <<= 0;
|
||||||
|
a && (a[(c = "PASS", 0 >>> (b += 1))] = 0);
|
||||||
|
})(42, -42);
|
||||||
|
})();
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var c = "FAIL";
|
||||||
|
(function() {
|
||||||
|
b = -(a = 42),
|
||||||
|
void ((a <<= 0) && (a[(c = "PASS", 0 >>> (b += 1))] = 0));
|
||||||
|
var a, b;
|
||||||
|
})();
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user