aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2020-08-06 09:39:50 +0100
committerGitHub <noreply@github.com>2020-08-06 09:39:50 +0100
commit9b05494ebcbcc40a6682cf570aa183f045244650 (patch)
tree933a355be5fa4715413c09ce7795e7b1c43b0616
parent30ef20a208adceeb465593d89dd9fead7ab953c2 (diff)
downloadtracifyjs-9b05494ebcbcc40a6682cf570aa183f045244650.tar.gz
tracifyjs-9b05494ebcbcc40a6682cf570aa183f045244650.zip
fix corner cases in aliasing of global variables (#4039)
fixes #4038
-rw-r--r--lib/compress.js48
-rw-r--r--test/compress/collapse_vars.js58
-rw-r--r--test/compress/reduce_vars.js22
-rw-r--r--test/ufuzz/index.js3
4 files changed, 115 insertions, 16 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 650a7634..7131e104 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -167,7 +167,7 @@ merge(Compressor.prototype, {
if (def.global) for (var i = 0; i < def.orig.length; i++)
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
return true;
- return false;
+ return def.undeclared;
},
compress: function(node) {
node = node.resolve_defines(this);
@@ -1386,6 +1386,7 @@ merge(Compressor.prototype, {
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
var scan_rhs = foldable(candidate);
if (!scan_lhs && !scan_rhs) continue;
+ var read_toplevel = false;
var modify_toplevel = false;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
@@ -1548,8 +1549,10 @@ merge(Compressor.prototype, {
return lvalues.has(node.name.name) || side_effects && may_modify(node.name);
}
var sym = is_lhs(node.left, node);
- if (sym && lvalues.has(sym.name)) return true;
- if (sym instanceof AST_PropAccess) return true;
+ if (!sym) return false;
+ return lvalues.has(sym.name)
+ || sym instanceof AST_PropAccess
+ || read_toplevel && compressor.exposed(sym.definition());
}
function extract_args() {
@@ -1936,26 +1939,43 @@ merge(Compressor.prototype, {
};
}
+ function may_be_global(node) {
+ if (node instanceof AST_SymbolRef) {
+ node = node.fixed_value();
+ if (!node) return true;
+ }
+ if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
+ return node instanceof AST_PropAccess || node instanceof AST_This;
+ }
+
function get_lvalues(expr) {
var lvalues = new Dictionary();
- if (candidate instanceof AST_VarDef) lvalues.add(candidate.name.name, lhs);
- var scan_iife = scope instanceof AST_Toplevel;
+ if (expr instanceof AST_VarDef) lvalues.add(expr.name.name, lhs);
+ var scan_toplevel = scope instanceof AST_Toplevel;
var tw = new TreeWalker(function(node) {
- if (scan_iife && node.TYPE == "Call") {
- var exp = node.expression;
- if (exp instanceof AST_PropAccess) return;
- if (exp instanceof AST_Function && !exp.contains_this()) return;
- modify_toplevel = true;
- scan_iife = false;
- return;
- }
var value;
if (node instanceof AST_SymbolRef) {
value = node.fixed_value() || node;
} else if (node instanceof AST_This) {
value = node;
}
- if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
+ if (value) {
+ lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
+ } else if (scan_toplevel) {
+ if (node.TYPE == "Call") {
+ if (modify_toplevel) return;
+ var exp = node.expression;
+ if (exp instanceof AST_PropAccess) return;
+ if (exp instanceof AST_Function && !exp.contains_this()) return;
+ modify_toplevel = true;
+ } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
+ if (node === lhs && !(expr instanceof AST_Unary)) {
+ modify_toplevel = true;
+ } else {
+ read_toplevel = true;
+ }
+ }
+ }
});
expr.walk(tw);
return lvalues;
diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js
index 109bbde0..d68c10e2 100644
--- a/test/compress/collapse_vars.js
+++ b/test/compress/collapse_vars.js
@@ -8317,3 +8317,61 @@ issue_4012: {
}
expect_stdout: "PASS"
}
+
+global_assign: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ this.A = "FAIL";
+ A = "PASS";
+ B = "FAIL";
+ console.log(A);
+ }
+ expect: {
+ this.A = "FAIL";
+ A = "PASS";
+ B = "FAIL";
+ console.log(A);
+ }
+ expect_stdout: "PASS"
+}
+
+global_read: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = 0;
+ a = this.A;
+ A = 1;
+ a ? console.log("FAIL") : console.log("PASS");
+ }
+ expect: {
+ var a = 0;
+ a = this.A;
+ A = 1;
+ a ? console.log("FAIL") : console.log("PASS");
+ }
+ expect_stdout: "PASS"
+}
+
+issue_4038: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = 0;
+ a = this;
+ a = a.A;
+ A = 1;
+ a ? console.log("FAIL") : console.log("PASS");
+ }
+ expect: {
+ var a = 0;
+ a = (a = this).A;
+ A = 1;
+ a ? console.log("FAIL") : console.log("PASS");
+ }
+ expect_stdout: "PASS"
+}
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 85969f90..77782cfd 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -7402,7 +7402,27 @@ issue_4030: {
}
expect: {
A = "PASS";
- console.log("PASS");
+ console.log(A);
+ }
+ expect_stdout: "PASS"
+}
+
+global_assign: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ A = "FAIL";
+ this.A = "PASS";
+ console.log(A);
+ }
+ expect: {
+ A = "FAIL";
+ this.A = "PASS";
+ console.log(A);
}
expect_stdout: "PASS"
}
diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js
index 1123d57e..9e8ca8dc 100644
--- a/test/ufuzz/index.js
+++ b/test/ufuzz/index.js
@@ -1175,7 +1175,8 @@ function log(options) {
}
function sort_globals(code) {
- return "var " + sandbox.run_code("throw Object.keys(this).sort();" + code).join(",") + ";" + code;
+ var globals = sandbox.run_code("throw Object.keys(this).sort();" + code);
+ return globals.length ? "var " + globals.join(",") + ";" + code : code;
}
function fuzzy_match(original, uglified) {