aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2017-07-02 01:05:14 +0800
committerGitHub <noreply@github.com>2017-07-02 01:05:14 +0800
commitd40950b741d67bafc91a102ce2c6e862af46fd11 (patch)
tree491155594137356177a2acedb3fd0287fcf193b3
parent7659ea1d2edd7603bd355eb849bd5dd62c3350c7 (diff)
downloadtracifyjs-d40950b741d67bafc91a102ce2c6e862af46fd11.tar.gz
tracifyjs-d40950b741d67bafc91a102ce2c6e862af46fd11.zip
improve `inline` efficiency (#2188)
... by teaching `collapse_vars` some new tricks. fixes #2187
-rw-r--r--lib/compress.js114
-rw-r--r--test/compress/collapse_vars.js35
-rw-r--r--test/compress/drop-unused.js5
-rw-r--r--test/compress/functions.js6
-rw-r--r--test/compress/issue-281.js24
-rw-r--r--test/mocha/glob.js4
6 files changed, 119 insertions, 69 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 352be282..dffdd6ed 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -714,15 +714,23 @@ merge(Compressor.prototype, {
var candidates = [];
var stat_index = statements.length;
while (--stat_index >= 0) {
+ // Treat parameters as collapsible in IIFE, i.e.
+ // function(a, b){ ... }(x());
+ // would be translated into equivalent assignments:
+ // var a = x(), b = undefined;
+ if (stat_index == 0 && compressor.option("unused")) extract_args();
+ // Find collapsible assignments
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
var candidate = candidates.pop();
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs)) continue;
+ // Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var side_effects = value_has_side_effects(candidate);
- var hit = false, abort = false, replaced = false;
+ var hit = candidate.name instanceof AST_SymbolFunarg;
+ var abort = false, replaced = false;
var tt = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
@@ -803,6 +811,35 @@ merge(Compressor.prototype, {
}
}
+ function extract_args() {
+ var iife, fn = compressor.self();
+ if (fn instanceof AST_Function
+ && !fn.name
+ && !fn.uses_arguments
+ && !fn.uses_eval
+ && (iife = compressor.parent()) instanceof AST_Call
+ && iife.expression === fn) {
+ fn.argnames.forEach(function(sym, i) {
+ var arg = iife.args[i];
+ if (!arg) arg = make_node(AST_Undefined, sym);
+ else arg.walk(new TreeWalker(function(node) {
+ if (!arg) return true;
+ if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
+ var s = node.definition().scope;
+ if (s !== scope) while (s = s.parent_scope) {
+ if (s === scope) return true;
+ }
+ arg = null;
+ }
+ }));
+ if (arg) candidates.push(make_node(AST_VarDef, sym, {
+ name: sym,
+ value: arg
+ }));
+ });
+ }
+ }
+
function extract_candidates(expr) {
if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor)
|| expr instanceof AST_Unary && (expr.operator == "++" || expr.operator == "--")) {
@@ -823,7 +860,7 @@ merge(Compressor.prototype, {
function get_lhs(expr) {
if (expr instanceof AST_VarDef) {
var def = expr.name.definition();
- if (def.orig.length > 1
+ if (def.orig.length > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| def.references.length == 1 && !compressor.exposed(def)) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
@@ -3174,69 +3211,42 @@ merge(Compressor.prototype, {
if (exp instanceof AST_Function) {
if (compressor.option("inline")
&& !exp.name
- && exp.body.length == 1
&& !exp.uses_arguments
&& !exp.uses_eval
+ && exp.body.length == 1
+ && all(exp.argnames, function(arg) {
+ return arg.__unused;
+ })
&& !self.has_pure_annotation(compressor)) {
var value;
if (stat instanceof AST_Return) {
- value = stat.value.clone(true);
+ value = stat.value;
} else if (stat instanceof AST_SimpleStatement) {
value = make_node(AST_UnaryPrefix, stat, {
operator: "void",
- expression: stat.body.clone(true)
+ expression: stat.body
});
}
if (value) {
- var fn = exp.clone();
- fn.argnames = [];
- fn.body = [];
- if (exp.argnames.length > 0) {
- fn.body.push(make_node(AST_Var, self, {
- definitions: exp.argnames.map(function(sym, i) {
- var arg = self.args[i];
- return make_node(AST_VarDef, sym, {
- name: sym,
- value: arg ? arg.clone(true) : make_node(AST_Undefined, self)
- });
- })
- }));
- }
- if (self.args.length > exp.argnames.length) {
- fn.body.push(make_node(AST_SimpleStatement, self, {
- body: make_sequence(self, self.args.slice(exp.argnames.length).map(function(node) {
- return node.clone(true);
- }))
- }));
- }
- fn.body.push(make_node(AST_Return, self, {
- value: value
- }));
- var body = fn.transform(compressor).body;
- if (body.length == 0) return make_node(AST_Undefined, self);
- if (body.length == 1 && body[0] instanceof AST_Return) {
- value = body[0].value;
- if (!value) return make_node(AST_Undefined, self);
- var tw = new TreeWalker(function(node) {
- if (value === self) return true;
- if (node instanceof AST_SymbolRef) {
- var ref = node.scope.find_variable(node);
- if (ref && ref.scope.parent_scope === fn.parent_scope) {
- value = self;
- return true;
- }
- }
- if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
- value = self;
+ var tw = new TreeWalker(function(node) {
+ if (!value) return true;
+ if (node instanceof AST_SymbolRef) {
+ var ref = node.scope.find_variable(node);
+ if (ref && ref.scope.parent_scope === fn.parent_scope) {
+ value = null;
return true;
}
- });
- value.walk(tw);
- if (value !== self) value = best_of(compressor, value, self);
- } else {
- value = self;
- }
- if (value !== self) return value;
+ }
+ if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
+ value = null;
+ return true;
+ }
+ });
+ value.walk(tw);
+ }
+ if (value) {
+ var args = self.args.concat(value);
+ return make_sequence(self, args).transform(compressor);
}
}
if (compressor.option("side_effects") && all(exp.body, is_empty)) {
diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js
index 158d1b4a..f3eb7816 100644
--- a/test/compress/collapse_vars.js
+++ b/test/compress/collapse_vars.js
@@ -1978,10 +1978,10 @@ chained_3: {
}
expect: {
console.log(function(a, b) {
- var c = a, c = b;
+ var c = 1, c = b;
b++;
return c;
- }(1, 2));
+ }(0, 2));
}
expect_stdout: "2"
}
@@ -2186,3 +2186,34 @@ compound_assignment: {
}
expect_stdout: "4"
}
+
+issue_2187: {
+ options = {
+ collapse_vars: true,
+ unused: true,
+ }
+ input: {
+ var a = 1;
+ !function(foo) {
+ foo();
+ var a = 2;
+ console.log(a);
+ }(function() {
+ console.log(a);
+ });
+ }
+ expect: {
+ var a = 1;
+ !function(foo) {
+ foo();
+ var a = 2;
+ console.log(a);
+ }(function() {
+ console.log(a);
+ });
+ }
+ expect_stdout: [
+ "1",
+ "2",
+ ]
+}
diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js
index 08fc7b18..e1acdc10 100644
--- a/test/compress/drop-unused.js
+++ b/test/compress/drop-unused.js
@@ -1113,6 +1113,7 @@ issue_2105: {
options = {
collapse_vars: true,
inline: true,
+ passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -1138,7 +1139,7 @@ issue_2105: {
});
}
expect: {
- !void function() {
+ (function() {
var quux = function() {
console.log("PASS");
};
@@ -1148,7 +1149,7 @@ issue_2105: {
quux();
}
};
- }().prop();
+ })().prop();
}
expect_stdout: "PASS"
}
diff --git a/test/compress/functions.js b/test/compress/functions.js
index c8efc12c..f411afa2 100644
--- a/test/compress/functions.js
+++ b/test/compress/functions.js
@@ -468,11 +468,9 @@ issue_2114_1: {
}
expect: {
var c = 0;
- !function() {
- 0;
- }((c += 1, c = 1 + c, function() {
+ c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
- }()));
+ }();
console.log(c);
}
expect_stdout: "2"
diff --git a/test/compress/issue-281.js b/test/compress/issue-281.js
index 9b8c8bfd..65871a84 100644
--- a/test/compress/issue-281.js
+++ b/test/compress/issue-281.js
@@ -419,7 +419,7 @@ wrap_iife_in_return_call: {
expect_exact: '(void console.log("test"))();'
}
-pure_annotation: {
+pure_annotation_1: {
options = {
inline: true,
side_effects: true,
@@ -432,6 +432,20 @@ pure_annotation: {
expect_exact: ""
}
+pure_annotation_2: {
+ options = {
+ collapse_vars: true,
+ inline: true,
+ side_effects: true,
+ }
+ input: {
+ /*@__PURE__*/(function(n) {
+ console.log("hello", n);
+ }(42));
+ }
+ expect_exact: ""
+}
+
drop_fargs: {
options = {
cascade: true,
@@ -449,9 +463,7 @@ drop_fargs: {
}
expect: {
var a = 1;
- !function() {
- a++;
- }(++a && a.var);
+ ++a && a.var, a++;
console.log(a);
}
expect_stdout: "3"
@@ -474,9 +486,7 @@ keep_fargs: {
}
expect: {
var a = 1;
- !function(a_1) {
- a++;
- }(++a && a.var);
+ ++a && a.var, a++;
console.log(a);
}
expect_stdout: "3"
diff --git a/test/mocha/glob.js b/test/mocha/glob.js
index 8567ecb3..eb5d477d 100644
--- a/test/mocha/glob.js
+++ b/test/mocha/glob.js
@@ -26,12 +26,12 @@ describe("bin/uglifyjs with input file globs", function() {
});
});
it("bin/uglifyjs with multiple input file globs.", function(done) {
- var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel';
+ var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel,passes=2';
exec(command, function(err, stdout) {
if (err) throw err;
- assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
+ assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",22);\n');
done();
});
});