aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2020-09-15 21:43:01 +0100
committerGitHub <noreply@github.com>2020-09-16 04:43:01 +0800
commitad27c1420226032459157444cf7ac17bf95bd5e6 (patch)
treec09914c296bd2cbbc3e0bbe5e2fd663b01e2738f
parenta62b086184e70072929a218d97dd11ce8efdfaff (diff)
downloadtracifyjs-ad27c1420226032459157444cf7ac17bf95bd5e6.tar.gz
tracifyjs-ad27c1420226032459157444cf7ac17bf95bd5e6.zip
fix corner cases in `merge_vars` (#4108)
fixes #4107 fixes #4109 fixes #4110 fixes #4111
-rw-r--r--lib/compress.js131
-rw-r--r--test/compress/merge_vars.js126
2 files changed, 195 insertions, 62 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 51f65268..52ed0e61 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -4309,15 +4309,16 @@ merge(Compressor.prototype, {
AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
if (!compressor.option("merge_vars")) return;
- var self = this, segment;
+ var self = this, segment = null;
var first = [], last = [], index = 0;
+ var declarations = Object.create(null);
var references = Object.create(null);
var prev = Object.create(null);
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Assign) {
- if (node.operator != "=") return;
var sym = node.left;
if (!(sym instanceof AST_SymbolRef)) return;
+ if (node.operator != "=") mark(sym);
node.right.walk(tw);
mark(sym, true);
return true;
@@ -4325,58 +4326,54 @@ merge(Compressor.prototype, {
if (node instanceof AST_Binary) {
if (!lazy_op[node.operator]) return;
node.left.walk(tw);
- var save = segment;
- segment = node;
+ push();
node.right.walk(tw);
- segment = save;
+ pop();
return true;
}
if (node instanceof AST_Conditional) {
node.condition.walk(tw);
- var save = segment;
- segment = node.consequent;
+ push();
node.consequent.walk(tw);
- segment = node.alternative;
+ pop();
+ push();
node.alternative.walk(tw);
- segment = save;
+ pop();
return true;
}
if (node instanceof AST_For) {
if (node.init) node.init.walk(tw);
- var save = segment;
- segment = node;
+ push();
if (node.condition) node.condition.walk(tw);
node.body.walk(tw);
if (node.step) node.step.walk(tw);
- segment = save;
+ pop();
return true;
}
if (node instanceof AST_ForIn) {
node.object.walk(tw);
- var save = segment;
- segment = node;
+ push();
node.init.walk(tw);
node.body.walk(tw);
- segment = save;
+ pop();
return true;
}
if (node instanceof AST_If) {
node.condition.walk(tw);
- var save = segment;
- segment = node;
+ push();
node.body.walk(tw);
+ pop();
if (node.alternative) {
- segment = node.alternative;
+ push();
node.alternative.walk(tw);
+ pop();
}
- segment = save;
return true;
}
if (node instanceof AST_IterationStatement) {
- var save = segment;
- segment = node;
+ push();
descend();
- segment = save;
+ pop();
return true;
}
if (node instanceof AST_Scope) {
@@ -4384,35 +4381,32 @@ merge(Compressor.prototype, {
references[node.variables.get("arguments").id] = false;
if (node.name) references[node.name.definition().id] = false;
}
- var save = segment;
- segment = node;
+ push();
descend();
- segment = save;
+ pop();
return true;
}
if (node instanceof AST_Switch) {
node.expression.walk(tw);
- var save = segment, first = true;
+ var first = true;
node.body.forEach(function(branch) {
if (branch instanceof AST_Default) return;
- if (first) {
- first = false;
- } else {
- segment = branch.expression;
- }
+ if (!first) push();
branch.expression.walk(tw);
+ if (!first) pop();
+ first = false;
});
node.body.forEach(function(branch) {
- segment = branch;
+ push();
branch.body.forEach(function(stat) {
stat.walk(tw);
});
+ pop();
});
- segment = save;
return true;
}
if (node instanceof AST_SymbolFunarg) {
- mark(node, true);
+ if (!node.__unused) mark(node, true);
return true;
}
if (node instanceof AST_SymbolRef) {
@@ -4420,16 +4414,19 @@ merge(Compressor.prototype, {
return true;
}
if (node instanceof AST_Try) {
- var save = segment;
- segment = node;
- node.body.forEach(function(branch) {
- branch.walk(tw);
+ push();
+ node.body.forEach(function(stat) {
+ stat.walk(tw);
});
+ pop();
if (node.bcatch) {
- segment = node.bcatch;
- node.bcatch.walk(tw);
+ references[node.bcatch.argname.definition().id] = false;
+ push();
+ node.bcatch.body.forEach(function(stat) {
+ stat.walk(tw);
+ });
+ pop();
}
- segment = save;
if (node.bfinally) node.bfinally.walk(tw);
return true;
}
@@ -4441,9 +4438,14 @@ merge(Compressor.prototype, {
return true;
}
if (node instanceof AST_VarDef) {
- if (!node.value) return true;
- node.value.walk(tw);
- mark(node.name, true);
+ if (node.value) {
+ node.value.walk(tw);
+ mark(node.name, true);
+ } else {
+ var id = node.name.definition().id;
+ if (!(id in declarations)) declarations[id] = [];
+ declarations[id].push(node.name);
+ }
return true;
}
});
@@ -4460,28 +4462,41 @@ merge(Compressor.prototype, {
var tail = last.pop();
if (!tail) continue;
if (tail.index > head.index) continue;
- if (!references[tail.definition.id]) continue;
- if (references[def.id].segment !== references[tail.definition.id].segment) {
+ var id = tail.definition.id;
+ if (!references[id]) continue;
+ if (references[def.id].segment !== references[id].segment) {
skipped.unshift(tail);
continue;
}
- var orig = [], refs = [];
- references[tail.definition.id].forEach(function(sym) {
- push(sym);
+ if (id in declarations) declarations[id].forEach(function(sym) {
+ sym.thedef = def;
+ sym.name = def.name;
+ def.orig.push(sym);
+ });
+ references[id].forEach(function(sym) {
sym.thedef = def;
sym.name = def.name;
+ if (sym instanceof AST_SymbolRef) {
+ def.references.push(sym);
+ } else {
+ def.orig.push(sym);
+ }
});
- references[def.id].forEach(push);
- def.orig = orig;
- def.refs = refs;
- def.eliminated = def.replaced = 0;
def.fixed = tail.definition.fixed && def.fixed;
- merged[tail.definition.id] = def;
+ merged[id] = def;
break;
} while (last.length);
if (skipped.length) last = last.concat(skipped);
}
+ function push() {
+ segment = Object.create(segment);
+ }
+
+ function pop() {
+ segment = Object.getPrototypeOf(segment);
+ }
+
function read(def) {
prev[def.id] = last.length;
last.push({
@@ -4516,14 +4531,6 @@ merge(Compressor.prototype, {
}
}
}
-
- function push(sym) {
- if (sym instanceof AST_SymbolRef) {
- refs.push(sym);
- } else {
- orig.push(sym);
- }
- }
});
AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
diff --git a/test/compress/merge_vars.js b/test/compress/merge_vars.js
index 4c5f6dac..f83643e3 100644
--- a/test/compress/merge_vars.js
+++ b/test/compress/merge_vars.js
@@ -324,3 +324,129 @@ issue_4103: {
"NaN",
]
}
+
+issue_4107: {
+ options = {
+ keep_fargs: "strict",
+ merge_vars: true,
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ (function() {
+ function f(b, b, c) {
+ var d = 1 && a, a = console || c;
+ console.log(typeof a);
+ }
+ f();
+ })();
+ console.log(typeof a);
+ }
+ expect: {
+ (function() {
+ (function(c) {
+ var a = console || c;
+ console.log(typeof a);
+ })();
+ })();
+ console.log(typeof a);
+ }
+ expect_stdout: [
+ "object",
+ "undefined",
+ ]
+}
+
+issue_4109: {
+ options = {
+ ie8: true,
+ merge_vars: true,
+ toplevel: true,
+ }
+ input: {
+ var a = "foo";
+ try {
+ throw "bar";
+ } catch (e) {
+ console.log(e);
+ } finally {
+ var o = a;
+ for (var k in o);
+ (function() {
+ a++;
+ });
+ }
+ console.log(a);
+ }
+ expect: {
+ var a = "foo";
+ try {
+ throw "bar";
+ } catch (e) {
+ console.log(e);
+ } finally {
+ var o = a;
+ for (var k in o);
+ (function() {
+ a++;
+ });
+ }
+ console.log(a);
+ }
+ expect_stdout: [
+ "bar",
+ "foo",
+ ]
+}
+
+issue_4110: {
+ options = {
+ merge_vars: true,
+ toplevel: true,
+ }
+ input: {
+ while (a)
+ var c;
+ var b, a = c += b = a;
+ console.log(b);
+ }
+ expect: {
+ while (a)
+ var c;
+ var b, a = c += b = a;
+ console.log(b);
+ }
+ expect_stdout: "undefined"
+}
+
+issue_4111: {
+ options = {
+ join_vars: true,
+ loops: true,
+ merge_vars: true,
+ toplevel: true,
+ }
+ input: {
+ var a = 0;
+ if (a)
+ a = 0;
+ else
+ for (var b = 0; --b && ++a < 2;) {
+ var o = console, k;
+ for (k in o);
+ }
+ console.log(a);
+ }
+ expect: {
+ var a = 0;
+ if (a)
+ a = 0;
+ else
+ for (var b = 0; --b && ++a < 2;) {
+ var o = console, k;
+ for (k in o);
+ }
+ console.log(a);
+ }
+ expect_stdout: "2"
+}