enhance inline (#3767)
This commit is contained in:
118
lib/compress.js
118
lib/compress.js
@@ -5819,38 +5819,45 @@ merge(Compressor.prototype, {
|
||||
var args = self.args.concat(value || make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).optimize(compressor);
|
||||
}
|
||||
var funarg, pos;
|
||||
if (value instanceof AST_SymbolRef
|
||||
&& (funarg = resolve_funarg(value.definition().orig))
|
||||
&& (pos = fn.argnames.indexOf(funarg)) >= 0
|
||||
&& (pos >= self.args.length - 1 || all(self.args.slice(pos), function(funarg) {
|
||||
return !funarg.has_side_effects(compressor);
|
||||
}))) {
|
||||
var args = self.args.slice();
|
||||
args.push(args.splice(pos, 1)[0] || make_node(AST_Undefined, self));
|
||||
var node = make_sequence(self, args).optimize(compressor);
|
||||
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
|
||||
}
|
||||
}
|
||||
if (is_func) {
|
||||
var def, value, scope, in_loop, level = -1;
|
||||
var def, value;
|
||||
if (can_inline
|
||||
&& !fn.uses_arguments
|
||||
&& !fn.pinned()
|
||||
&& !(fn.name && fn instanceof AST_Function)
|
||||
&& (value = can_flatten_body(stat))
|
||||
&& (exp === fn
|
||||
|| compressor.option("unused")
|
||||
&& (def = exp.definition()).references.length == 1
|
||||
&& !recursive_ref(compressor, def)
|
||||
&& fn.is_constant_expression(exp.scope))
|
||||
&& !self.pure
|
||||
&& !fn.contains_this()
|
||||
&& can_inject_symbols()) {
|
||||
fn._squeezed = true;
|
||||
if (exp !== fn) fn.parent_scope = exp.scope;
|
||||
var node = make_sequence(self, flatten_fn()).optimize(compressor);
|
||||
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
|
||||
|| !recursive_ref(compressor, def = exp.definition) && fn.is_constant_expression(exp.scope))
|
||||
&& !fn.contains_this()) {
|
||||
if (can_substitute_directly()) {
|
||||
var args = self.args.slice();
|
||||
args.push(value.clone(true).transform(new TreeTransformer(function(node) {
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
var def = node.definition();
|
||||
if (fn.variables.get(node.name) !== def) return node;
|
||||
var index = resolve_index(def);
|
||||
var arg = args[index];
|
||||
if (!arg) return make_node(AST_Undefined, self);
|
||||
args[index] = null;
|
||||
var parent = this.parent();
|
||||
return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
|
||||
}
|
||||
})));
|
||||
var node = make_sequence(self, args.filter(function(arg) {
|
||||
return arg;
|
||||
})).optimize(compressor);
|
||||
node = maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
|
||||
if (best_of(compressor, self, node) === node) return node;
|
||||
}
|
||||
var scope, in_loop, level = -1;
|
||||
if ((exp === fn || compressor.option("unused") && exp.definition().references.length == 1)
|
||||
&& can_inject_symbols()) {
|
||||
fn._squeezed = true;
|
||||
if (exp !== fn) fn.parent_scope = exp.scope;
|
||||
var node = make_sequence(self, flatten_fn()).optimize(compressor);
|
||||
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
|
||||
}
|
||||
}
|
||||
if (compressor.option("side_effects")
|
||||
&& all(fn.body, is_empty)
|
||||
@@ -5877,14 +5884,6 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return try_evaluate(compressor, self);
|
||||
|
||||
function resolve_funarg(orig) {
|
||||
var funarg;
|
||||
for (var i = 0; orig[i] instanceof AST_SymbolFunarg; i++) {
|
||||
funarg = orig[i];
|
||||
}
|
||||
return funarg;
|
||||
}
|
||||
|
||||
function return_value(stat) {
|
||||
if (!stat) return make_node(AST_Undefined, self);
|
||||
if (stat instanceof AST_Return) {
|
||||
@@ -5924,6 +5923,61 @@ merge(Compressor.prototype, {
|
||||
return return_value(stat);
|
||||
}
|
||||
|
||||
function resolve_index(def) {
|
||||
for (var i = fn.argnames.length; --i >= 0;) {
|
||||
if (fn.argnames[i].definition() === def) return i;
|
||||
}
|
||||
}
|
||||
|
||||
function can_substitute_directly() {
|
||||
if (compressor.option("inline") <= 1 && fn.argnames.length) return;
|
||||
if (fn.variables.size() > fn.argnames.length + 1) return;
|
||||
if (!all(fn.argnames, function(argname) {
|
||||
return argname.definition().references.length < 2;
|
||||
})) return;
|
||||
var abort = false;
|
||||
var begin;
|
||||
var in_order = [];
|
||||
var side_effects = false;
|
||||
value.walk(new TreeWalker(function(node) {
|
||||
if (abort) return true;
|
||||
if (node instanceof AST_Binary && lazy_op[node.operator]
|
||||
|| node instanceof AST_Conditional) {
|
||||
in_order = null;
|
||||
return;
|
||||
}
|
||||
if (node instanceof AST_Scope) return abort = true;
|
||||
var def;
|
||||
if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === (def = node.definition())) {
|
||||
if (def.init instanceof AST_Defun) return abort = true;
|
||||
if (is_lhs(node, this.parent())) return abort = true;
|
||||
var index = resolve_index(def);
|
||||
if (!(begin < index)) begin = index;
|
||||
if (!in_order) return;
|
||||
if (side_effects) {
|
||||
in_order = null;
|
||||
} else {
|
||||
in_order.push(fn.argnames[index]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (node.has_side_effects(compressor)) side_effects = true;
|
||||
}));
|
||||
if (abort) return;
|
||||
var end = self.args.length;
|
||||
if (in_order && fn.argnames.length >= end) {
|
||||
end = fn.argnames.length;
|
||||
while (end-- > begin && fn.argnames[end] === in_order.pop());
|
||||
end++;
|
||||
}
|
||||
var scope = side_effects && compressor.find_parent(AST_Scope);
|
||||
return end <= begin || all(self.args.slice(begin, end), side_effects ? function(funarg) {
|
||||
return funarg.is_constant_expression(scope);
|
||||
} : function(funarg) {
|
||||
return !funarg.has_side_effects(compressor);
|
||||
});
|
||||
}
|
||||
|
||||
function var_exists(defined, name) {
|
||||
return defined[name] || identifier_atom[name] || scope.var_names()[name];
|
||||
}
|
||||
|
||||
@@ -1020,9 +1020,7 @@ issue_2616: {
|
||||
}
|
||||
expect: {
|
||||
var c = "FAIL";
|
||||
!function(NaN) {
|
||||
(true << NaN) - 0/0 || (c = "PASS");
|
||||
}([]);
|
||||
(true << []) - NaN || (c = "PASS");
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
@@ -1607,7 +1605,31 @@ duplicate_argnames_2: {
|
||||
}
|
||||
expect: {
|
||||
var a = "PASS";
|
||||
console, b && (a = "FAIL");
|
||||
console, void 0 && (a = "FAIL");
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
duplicate_argnames_3: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a = "FAIL";
|
||||
function f(b, b, b) {
|
||||
b && (a = "PASS");
|
||||
}
|
||||
f(null, 0, console, "42".toString());
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL";
|
||||
b = console, "42".toString(), b && (a = "PASS");
|
||||
var b;
|
||||
console.log(a);
|
||||
}
|
||||
@@ -1766,8 +1788,7 @@ inline_2: {
|
||||
}
|
||||
expect: {
|
||||
console.log(1);
|
||||
a = 2, console.log(a);
|
||||
var a;
|
||||
console.log(2);
|
||||
(function(b) {
|
||||
var c = b;
|
||||
console.log(c);
|
||||
@@ -1800,8 +1821,7 @@ inline_3: {
|
||||
}
|
||||
expect: {
|
||||
console.log(1);
|
||||
a = 2, console.log(a);
|
||||
var a;
|
||||
console.log(2);
|
||||
b = 3, c = b, console.log(c);
|
||||
var b, c;
|
||||
}
|
||||
@@ -1832,8 +1852,7 @@ inline_true: {
|
||||
}
|
||||
expect: {
|
||||
console.log(1);
|
||||
a = 2, console.log(a);
|
||||
var a;
|
||||
console.log(2);
|
||||
b = 3, c = b, console.log(c);
|
||||
var b, c;
|
||||
}
|
||||
@@ -1897,7 +1916,24 @@ duplicate_arg_var_2: {
|
||||
}("PA"));
|
||||
}
|
||||
expect: {
|
||||
console.log((b = "PA", b + "SS"));
|
||||
console.log("PA" + "SS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
duplicate_arg_var_3: {
|
||||
options = {
|
||||
inline: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(b) {
|
||||
return b + "SS";
|
||||
var b;
|
||||
}("PA", "42".toString()));
|
||||
}
|
||||
expect: {
|
||||
console.log((b = "PA", "42".toString(), b + "SS"));
|
||||
var b;
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
@@ -2045,10 +2081,8 @@ issue_3016_1: {
|
||||
expect: {
|
||||
var b = 1;
|
||||
do {
|
||||
a = 3,
|
||||
a[b];
|
||||
3[b];
|
||||
} while(0);
|
||||
var a;
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
@@ -2556,10 +2590,9 @@ cross_references_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
hoist_props: true,
|
||||
inline: true,
|
||||
passes: 4,
|
||||
pure_getters: true,
|
||||
passes: 6,
|
||||
properties: true,
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
@@ -3685,9 +3718,7 @@ pr_3595_3: {
|
||||
var g = [ "PASS" ];
|
||||
console.log(function(problem) {
|
||||
return g[problem];
|
||||
}(function(arg) {
|
||||
return g.indexOf(arg);
|
||||
}("PASS")));
|
||||
}(g.indexOf("PASS")));
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ warn: {
|
||||
}().length);
|
||||
}
|
||||
expect_warnings: [
|
||||
"WARN: Function.prototype.caller not supported [test/compress/issue-2719.js:5,19]",
|
||||
"WARN: Function.prototype.arguments not supported [test/compress/issue-2719.js:5,19]",
|
||||
"WARN: Function.prototype.caller not supported [test/compress/issue-2719.js:5,19]",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2289,11 +2289,10 @@ redefine_farg_2: {
|
||||
console.log(f([]), g([]), h([]));
|
||||
}
|
||||
expect: {
|
||||
console.log((a = [], typeof a), "number",function(a, b) {
|
||||
console.log(typeof [], "number",function(a, b) {
|
||||
a = b;
|
||||
return typeof a;
|
||||
}([]));
|
||||
var a;
|
||||
}
|
||||
expect_stdout: "object number undefined"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
function f(x) {
|
||||
return g(x);
|
||||
function g(x) {
|
||||
return x + 1;
|
||||
return x + x;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,7 +674,7 @@ describe("bin/uglifyjs", function() {
|
||||
var command = uglifyjscmd + " test/input/rename/input.js --rename";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c+1}}\n");
|
||||
assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c+c}}\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -682,7 +682,7 @@ describe("bin/uglifyjs", function() {
|
||||
var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2 --no-rename";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, "function f(n){return function(n){return n+1}(n)}\n");
|
||||
assert.strictEqual(stdout, "function f(n){return function(n){return n+n}(n)}\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -690,7 +690,7 @@ describe("bin/uglifyjs", function() {
|
||||
var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, "function f(n){return n+1}\n");
|
||||
assert.strictEqual(stdout, "function f(n){return n+n}\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -698,7 +698,7 @@ describe("bin/uglifyjs", function() {
|
||||
var command = uglifyjscmd + " test/input/rename/input.js -c passes=2";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, "function f(x){return function(x){return x+1}(x)}\n");
|
||||
assert.strictEqual(stdout, "function f(x){return function(x){return x+x}(x)}\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user