aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2020-05-11 15:46:00 +0100
committerGitHub <noreply@github.com>2020-05-11 22:46:00 +0800
commit2b24dc25fbe78d25b41b2e48bc38fedc6965d572 (patch)
tree53865d8246ecdfcb66c95d0074f26c8b68ffcd72
parent35cc5aa06fd05437b6fa538ca3f32a876156d4da (diff)
downloadtracifyjs-2b24dc25fbe78d25b41b2e48bc38fedc6965d572.tar.gz
tracifyjs-2b24dc25fbe78d25b41b2e48bc38fedc6965d572.zip
fix corner cases in `evaluate` & `reduce_vars` (#3879)
fixes #3878
-rw-r--r--lib/compress.js45
-rw-r--r--test/compress/evaluate.js43
2 files changed, 78 insertions, 10 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 1b5d1a3a..08abde9b 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -858,7 +858,7 @@ merge(Compressor.prototype, {
d.assignments++;
var fixed = d.fixed;
if (!fixed) return;
- exp.fixed = d.fixed = function() {
+ d.fixed = function() {
var value = fixed instanceof AST_Node ? fixed : fixed();
return value && make_node(AST_Binary, node, {
operator: node.operator.slice(0, -1),
@@ -871,6 +871,13 @@ merge(Compressor.prototype, {
})
});
};
+ exp.fixed = node instanceof AST_UnaryPrefix ? d.fixed : function() {
+ var value = fixed instanceof AST_Node ? fixed : fixed();
+ return value && make_node(AST_UnaryPrefix, node, {
+ operator: "+",
+ expression: value
+ });
+ };
if (!safe) return;
d.references.push(exp);
mark(tw, d, true);
@@ -3233,6 +3240,12 @@ merge(Compressor.prototype, {
&& unaryPrefix[this.operator];
}
});
+ function modified(sym) {
+ if (!(sym instanceof AST_SymbolRef)) return;
+ sym.definition().references.forEach(function(node) {
+ delete node._eval;
+ });
+ }
def(AST_Statement, function() {
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
});
@@ -3246,6 +3259,7 @@ merge(Compressor.prototype, {
if (this.operator != "=") return this;
var node = this.right;
var value = node._eval(compressor, ignore_side_effects, cached, depth);
+ modified(this.left);
return value === node ? this : value;
});
def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
@@ -3317,7 +3331,7 @@ merge(Compressor.prototype, {
if (!non_converting_unary[op]) depth++;
var v = e._eval(compressor, ignore_side_effects, cached, depth);
if (v === e) {
- if (ignore_side_effects && op == "void") return void 0;
+ if (ignore_side_effects && op == "void") return;
return this;
}
switch (op) {
@@ -3327,7 +3341,7 @@ merge(Compressor.prototype, {
// so cannot evaluate reliably
if (v instanceof RegExp) return this;
return typeof v;
- case "void": return void v;
+ case "void": return;
case "~": return ~v;
case "-": return -v;
case "+": return +v;
@@ -3335,11 +3349,22 @@ merge(Compressor.prototype, {
case "--":
if (!(e instanceof AST_SymbolRef)) return this;
var refs = e.definition().references;
- if (refs[refs.length - 1] !== e) return this;
- return HOP(e, "_eval") ? +(op[0] + 1) + +v : v;
+ if (!ignore_side_effects && refs[refs.length - 1] !== e) return this;
+ if (HOP(e, "_eval")) v = +(op[0] + 1) + +v;
+ modified(e);
+ return v;
}
return this;
});
+ def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) {
+ var e = this.expression;
+ if (!(e instanceof AST_SymbolRef)) return this;
+ var refs = e.definition().references;
+ if (!ignore_side_effects && refs[refs.length - 1] !== e) return this;
+ var v = e._eval(compressor, ignore_side_effects, cached, depth + 1);
+ modified(e);
+ return v === e ? this : +v;
+ });
var non_converting_binary = makePredicate("&& || === !==");
def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
if (!non_converting_binary[this.operator]) depth++;
@@ -3514,14 +3539,14 @@ merge(Compressor.prototype, {
}
if (node instanceof AST_Scope && node !== fn) return true;
}));
- if (!found) return void 0;
+ if (!found) return;
}
return this;
}
var args = eval_args(this.args);
- if (!args) return this;
- if (!stat.value) return undefined;
- if (!all(fn.argnames, function(sym, i) {
+ if (!args && !ignore_side_effects) return this;
+ if (!stat.value) return;
+ if (args && !all(fn.argnames, function(sym, i) {
var value = args[i];
var def = sym.definition();
if (def.orig[def.orig.length - 1] !== sym) return false;
@@ -3532,7 +3557,7 @@ merge(Compressor.prototype, {
cached.push(node);
});
return true;
- })) return this;
+ }) && !ignore_side_effects) return this;
fn.evaluating = true;
var val = stat.value._eval(compressor, ignore_side_effects, cached, depth);
delete fn.evaluating;
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index a46d38aa..a4bee8cc 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -2326,3 +2326,46 @@ void_returns_recursive: {
"undefined",
]
}
+
+issue_3878_1: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var b = function(a) {
+ return (a = 0) == (a && this > (a += 0));
+ }();
+ console.log(b ? "PASS" : "FAIL");
+ }
+ expect: {
+ var b = function(a) {
+ return (a = 0) == (a && this > (a += 0));
+ }();
+ console.log(b ? "PASS" : "FAIL");
+ }
+ expect_stdout: "PASS"
+}
+
+issue_3878_2: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ }
+ input: {
+ var a = "foo";
+ a++ + a;
+ a && a;
+ console.log(a);
+ }
+ expect: {
+ var a = "foo";
+ a++ + a;
+ a;
+ console.log(a);
+ }
+ expect_stdout: "NaN"
+}