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)
}
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) {
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];
}
}
expect: {
var c = "FAIL";
- !function(NaN) {
- (true << NaN) - 0/0 || (c = "PASS");
- }([]);
+ (true << []) - NaN || (c = "PASS");
console.log(c);
}
expect_stdout: "PASS"
}
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);
}
}
expect: {
console.log(1);
- a = 2, console.log(a);
- var a;
+ console.log(2);
(function(b) {
var c = b;
console.log(c);
}
expect: {
console.log(1);
- a = 2, console.log(a);
- var a;
+ console.log(2);
b = 3, c = b, console.log(c);
var b, c;
}
}
expect: {
console.log(1);
- a = 2, console.log(a);
- var a;
+ console.log(2);
b = 3, c = b, console.log(c);
var b, c;
}
}("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"
expect: {
var b = 1;
do {
- a = 3,
- a[b];
+ 3[b];
} while(0);
- var a;
console.log(b);
}
expect_stdout: "1"
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,
var g = [ "PASS" ];
console.log(function(problem) {
return g[problem];
- }(function(arg) {
- return g.indexOf(arg);
- }("PASS")));
+ }(g.indexOf("PASS")));
}
expect_stdout: "PASS"
}
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();
});
});
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();
});
});
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();
});
});
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();
});
});