aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2017-03-02 00:20:53 +0800
committerGitHub <noreply@github.com>2017-03-02 00:20:53 +0800
commit7aa69117e1a38e4aeead13100ac952ca99dbb07f (patch)
tree6b957de603843f936905ff76caceb22a2e625a4c
parentbff7ad67bbab6ce0792292fba66d3a6cf8d1836f (diff)
downloadtracifyjs-7aa69117e1a38e4aeead13100ac952ca99dbb07f.tar.gz
tracifyjs-7aa69117e1a38e4aeead13100ac952ca99dbb07f.zip
fix corner cases in `reduce_vars` (#1524)
Avoid variable substitution in the following cases: - use of variable before declaration - declaration within conditional code blocks - declaration within loop body fixes #1518 fixes #1525
-rw-r--r--lib/compress.js107
-rw-r--r--test/compress/reduce_vars.js119
2 files changed, 206 insertions, 20 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 32e7a649..24550973 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -179,38 +179,105 @@ merge(Compressor.prototype, {
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
var reduce_vars = rescan && compressor.option("reduce_vars");
- var unsafe = compressor.option("unsafe");
+ var safe_ids = [];
+ push();
var tw = new TreeWalker(function(node){
+ if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
+ node._squeezed = false;
+ node._optimized = false;
+ }
if (reduce_vars) {
if (node instanceof AST_Toplevel) node.globals.each(reset_def);
if (node instanceof AST_Scope) node.variables.each(reset_def);
if (node instanceof AST_SymbolRef) {
var d = node.definition();
d.references.push(node);
- if (d.fixed && (d.orig.length > 1 || isModified(node, 0))) {
+ if (!d.fixed || isModified(node, 0) || !is_safe(d)) {
+ d.fixed = false;
+ }
+ }
+ if (node instanceof AST_VarDef) {
+ var d = node.name.definition();
+ if (d.fixed === undefined) {
+ d.fixed = node.value || make_node(AST_Undefined, node);
+ mark_as_safe(d);
+ } else {
d.fixed = false;
}
}
- if (node instanceof AST_Call && node.expression instanceof AST_Function) {
- node.expression.argnames.forEach(function(arg, i) {
- arg.definition().init = node.args[i] || make_node(AST_Undefined, node);
+ var iife;
+ if (node instanceof AST_Function
+ && (iife = tw.parent()) instanceof AST_Call
+ && iife.expression === node) {
+ node.argnames.forEach(function(arg, i) {
+ var d = arg.definition();
+ d.fixed = iife.args[i] || make_node(AST_Undefined, iife);
+ mark_as_safe(d);
});
}
- }
- if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
- node._squeezed = false;
- node._optimized = false;
+ if (node instanceof AST_If || node instanceof AST_DWLoop) {
+ node.condition.walk(tw);
+ push();
+ node.body.walk(tw);
+ pop();
+ if (node.alternative) {
+ push();
+ node.alternative.walk(tw);
+ pop();
+ }
+ return true;
+ }
+ if (node instanceof AST_LabeledStatement) {
+ push();
+ node.body.walk(tw);
+ pop();
+ return true;
+ }
+ if (node instanceof AST_For) {
+ if (node.init) node.init.walk(tw);
+ push();
+ if (node.condition) node.condition.walk(tw);
+ node.body.walk(tw);
+ if (node.step) node.step.walk(tw);
+ pop();
+ return true;
+ }
+ if (node instanceof AST_ForIn) {
+ if (node.init instanceof AST_SymbolRef) {
+ node.init.definition().fixed = false;
+ }
+ node.object.walk(tw);
+ push();
+ node.body.walk(tw);
+ pop();
+ return true;
+ }
}
});
this.walk(tw);
+ function mark_as_safe(def) {
+ safe_ids[safe_ids.length - 1][def.id] = true;
+ }
+
+ function is_safe(def) {
+ for (var i = safe_ids.length, id = def.id; --i >= 0;) {
+ if (safe_ids[i][id]) return true;
+ }
+ }
+
+ function push() {
+ safe_ids.push(Object.create(null));
+ }
+
+ function pop() {
+ safe_ids.pop();
+ }
+
function reset_def(def) {
- def.fixed = true;
+ def.fixed = undefined;
def.references = [];
def.should_replace = undefined;
- if (unsafe && def.init) {
- def.init._evaluated = undefined;
- }
}
function isModified(node, level) {
@@ -1263,14 +1330,14 @@ merge(Compressor.prototype, {
this._evaluating = true;
try {
var d = this.definition();
- if (compressor.option("reduce_vars") && d.fixed && d.init) {
+ if (compressor.option("reduce_vars") && d.fixed) {
if (compressor.option("unsafe")) {
- if (d.init._evaluated === undefined) {
- d.init._evaluated = ev(d.init, compressor);
+ if (!HOP(d.fixed, "_evaluated")) {
+ d.fixed._evaluated = ev(d.fixed, compressor);
}
- return d.init._evaluated;
+ return d.fixed._evaluated;
}
- return ev(d.init, compressor);
+ return ev(d.fixed, compressor);
}
} finally {
this._evaluating = false;
@@ -3046,9 +3113,9 @@ merge(Compressor.prototype, {
}
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
var d = self.definition();
- if (d.fixed && d.init) {
+ if (d.fixed) {
if (d.should_replace === undefined) {
- var init = d.init.evaluate(compressor);
+ var init = d.fixed.evaluate(compressor);
if (init.length > 1) {
var value = init[0].print_to_string().length;
var name = d.name.length;
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 0ee201c0..e38c317b 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -470,3 +470,122 @@ multi_def_2: {
var repeatLength = this.getBits(bitsLength) + bitsOffset;
}
}
+
+use_before_var: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ console.log(t);
+ var t = 1;
+ }
+ expect: {
+ console.log(t);
+ var t = 1;
+ }
+}
+
+inner_var_if: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ function f(){
+ return 0;
+ }
+ if (f())
+ var t = 1;
+ if (!t)
+ console.log(t);
+ }
+ expect: {
+ function f(){
+ return 0;
+ }
+ if (f())
+ var t = 1;
+ if (!t)
+ console.log(t);
+ }
+}
+
+inner_var_label: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ function f(){
+ return 1;
+ }
+ l: {
+ if (f()) break l;
+ var t = 1;
+ }
+ console.log(t);
+ }
+ expect: {
+ function f(){
+ return 1;
+ }
+ l: {
+ if (f()) break l;
+ var t = 1;
+ }
+ console.log(t);
+ }
+}
+
+inner_var_for: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ var a = 1;
+ x(a, b, d);
+ for (var b = 2, c = 3; x(a, b, c, d); x(a, b, c, d)) {
+ var d = 4, e = 5;
+ x(a, b, c, d, e);
+ }
+ x(a, b, c, d, e)
+ }
+ expect: {
+ var a = 1;
+ x(1, b, d);
+ for (var b = 2, c = 3; x(1, b, 3, d); x(1, b, 3, d)) {
+ var d = 4, e = 5;
+ x(1, b, 3, d, e);
+ }
+ x(1, b, 3, d, e);
+ }
+}
+
+inner_var_for_in: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ var a = 1, b = 2;
+ for (b in (function() {
+ return x(a, b, c);
+ })()) {
+ var c = 3, d = 4;
+ x(a, b, c, d);
+ }
+ x(a, b, c, d);
+ }
+ expect: {
+ var a = 1, b = 2;
+ for (b in (function() {
+ return x(1, b, c);
+ })()) {
+ var c = 3, d = 4;
+ x(1, b, c, d);
+ }
+ x(1, b, c, d);
+ }
+}