Compare commits

...

20 Commits

Author SHA1 Message Date
Alex Lam S.L
346fa12e0e v3.1.9 2017-11-11 15:31:13 +08:00
Alex Lam S.L
cda27b0970 extend reduce_funcs to cover cross-scope substitutions (#2469)
fixes #2468
2017-11-11 15:30:17 +08:00
Alex Lam S.L
3c74047368 implement compress option reduce_funcs (#2466)
- inline single-use function declarations as expressions when permissible
- depend on `reduce_vars`
- enabled by default
- disable for speed critical code

fixes #2464
2017-11-11 05:59:35 +08:00
Alex Lam S.L
94525d859f fix object literal tracing in reduce_vars (#2461) 2017-11-10 05:47:10 +08:00
Alex Lam S.L
1127a2caf3 fix multiple nested function substitutions (#2458)
fixes #2449
2017-11-09 23:30:00 +08:00
Alex Lam S.L
246d9d4e83 remove hack in collapse_vars (#2457)
fixes #2456
2017-11-09 20:00:58 +08:00
Alex Lam S.L
4c0b0177b6 preserve function identity in reduce_vars (#2451)
fixes #2450
2017-11-08 03:28:46 +08:00
Alex Lam S.L
38bfb73f06 v3.1.8 2017-11-07 03:55:16 +08:00
Alex Lam S.L
bbedbf4ea0 handle circular function reference gracefully (#2446)
fixes #2442
2017-11-07 02:37:23 +08:00
Alex Lam S.L
2cfb5aa7da account for eval & with in reduce_vars (#2441)
fixes #2440
2017-11-06 16:10:57 +08:00
Alex Lam S.L
6c45101870 consolidate & enhance unused (#2439)
- defer declaration removal in `collapse_vars`
- account for `AST_SymbolFunarg` in deduplication
- private accounting for `collapse_vars`
- avoid issues with identity reference due to deep cloning

fixes #2437
2017-11-06 14:25:10 +08:00
Alex Lam S.L
2c2fd89e34 inline single-use functions that are not constant expressions (#2434)
fixes #2428
2017-11-05 22:14:11 +08:00
Alex Lam S.L
f46281e2b7 v3.1.7 2017-11-05 15:03:19 +08:00
Alex Lam S.L
25a18883f5 tweak #2424 (#2432) 2017-11-05 12:49:14 +08:00
Alex Lam S.L
5b4b07e9a7 extend function inlining safety checks (#2430) 2017-11-05 06:18:45 +08:00
Alex Lam S.L
a8aa28a7a6 consolidate single-use function reduction (#2427)
fixes #2423
2017-11-05 04:27:01 +08:00
Alex Lam S.L
fe5a68f9d5 maintain call argument order in collapse_vars (#2426)
fixes #2425
2017-11-05 00:00:18 +08:00
Alex Lam S.L
71e61153b1 improve variations on call arguments for ufuzz (#2424) 2017-11-04 16:29:42 +08:00
Alex Lam S.L
c8b6f4733d reduce this within functions (#2421)
- only replace same-scope usages
- augment `test/ufuzz.js` to test for `this`


fixes #2420
2017-11-04 00:31:37 +08:00
Alex Lam S.L
a48f87abf2 compress new function containing this (#2417) 2017-10-30 23:19:27 +08:00
27 changed files with 1845 additions and 197 deletions

View File

@@ -689,6 +689,11 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `reduce_funcs` (default: `true`) -- Allows single-use functions
to be inlined as function expressions when permissible.
Enabled by default. Option depends on `reduce_vars` being enabled.
For speed critical code this option should be disabled.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
used as constant values.

View File

@@ -75,6 +75,7 @@ function Compressor(options, false_by_default) {
properties : !false_by_default,
pure_getters : !false_by_default && "strict",
pure_funcs : null,
reduce_funcs : !false_by_default,
reduce_vars : !false_by_default,
sequences : !false_by_default,
side_effects : !false_by_default,
@@ -150,7 +151,9 @@ merge(Compressor.prototype, {
}
var passes = +this.options.passes || 1;
var last_count = 1 / 0;
var mangle = { ie8: this.option("ie8") };
for (var pass = 0; pass < passes; pass++) {
node.figure_out_scope(mangle);
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this);
node = node.transform(this);
@@ -285,7 +288,7 @@ merge(Compressor.prototype, {
self.transform(tt);
});
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor) {
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
var reduce_vars = compressor.option("reduce_vars");
var unused = compressor.option("unused");
// Stack of look-up tables to keep track of whether a `SymbolDef` has been
@@ -311,16 +314,16 @@ merge(Compressor.prototype, {
if (node instanceof AST_SymbolRef) {
var d = node.definition();
d.references.push(node);
var value;
if (d.fixed === undefined || !safe_to_read(d) || d.single_use == "m") {
d.fixed = false;
} else if (d.fixed) {
var value = node.fixed_value();
if (unused) {
d.single_use = value
&& d.references.length == 1
&& loop_ids[d.id] === in_loop
&& d.scope === node.scope
&& value.is_constant_expression();
value = node.fixed_value();
if (value && ref_once(d)) {
d.single_use = value instanceof AST_Lambda
|| d.scope === node.scope && value.is_constant_expression();
} else {
d.single_use = false;
}
if (is_modified(node, value, 0, is_immutable(value))) {
if (d.single_use) {
@@ -328,10 +331,9 @@ merge(Compressor.prototype, {
} else {
d.fixed = false;
}
} else {
mark_escaped(d, node, value, 0);
}
}
mark_escaped(d, node, value, 0);
}
if (node instanceof AST_SymbolCatch) {
node.definition().fixed = false;
@@ -376,7 +378,9 @@ merge(Compressor.prototype, {
d.fixed = false;
} else {
d.fixed = node;
loop_ids[d.id] = in_loop;
mark(d, true);
d.single_use = ref_once(d);
}
var save_ids = safe_ids;
safe_ids = Object.create(null);
@@ -527,6 +531,7 @@ merge(Compressor.prototype, {
}
return true;
}
return def.fixed instanceof AST_Defun;
}
function safe_to_assign(def, value) {
@@ -551,7 +556,7 @@ merge(Compressor.prototype, {
function reset_def(def) {
def.direct_access = false;
def.escaped = false;
if (def.scope.uses_eval) {
if (def.scope.uses_eval || def.scope.uses_with) {
def.fixed = false;
} else if (!compressor.exposed(def)) {
def.fixed = undefined;
@@ -563,8 +568,19 @@ merge(Compressor.prototype, {
def.single_use = undefined;
}
function ref_once(def) {
return unused
&& !def.scope.uses_eval
&& !def.scope.uses_with
&& def.references.length == 1
&& loop_ids[def.id] === in_loop;
}
function is_immutable(value) {
return value && (value.is_constant() || value instanceof AST_Lambda);
if (!value) return false;
return value.is_constant()
|| value instanceof AST_Lambda
|| value instanceof AST_This;
}
function read_property(obj, key) {
@@ -592,10 +608,13 @@ merge(Compressor.prototype, {
|| !immutable
&& parent instanceof AST_Call
&& parent.expression === node
&& (!(value instanceof AST_Function) || value.contains_this())) {
&& (!(value instanceof AST_Function) || value.contains_this(parent))) {
return true;
} else if (parent instanceof AST_Array || parent instanceof AST_Object) {
} else if (parent instanceof AST_Array) {
return is_modified(parent, parent, level + 1);
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1);
return is_modified(obj, obj, level + 2);
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
return !immutable && is_modified(parent, read_property(value, parent.property), level + 1);
}
@@ -603,15 +622,19 @@ merge(Compressor.prototype, {
function mark_escaped(d, node, value, level) {
var parent = tw.parent(level);
if (value instanceof AST_Constant || value instanceof AST_Function) return;
if (value instanceof AST_Constant) return;
if (level > 0 && value instanceof AST_Function) return;
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped = true;
return;
} else if (parent instanceof AST_Array || parent instanceof AST_Object) {
} else if (parent instanceof AST_Array) {
mark_escaped(d, parent, parent, level + 1);
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1);
mark_escaped(d, obj, obj, level + 2);
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
value = read_property(value, parent.property);
mark_escaped(d, parent, value, level + 1);
@@ -772,6 +795,14 @@ merge(Compressor.prototype, {
|| compressor.option("unsafe") && global_names(this.name);
});
function drop_decl(def) {
def._eliminiated = (def._eliminiated || 0) + 1;
if (def.orig.length == def._eliminiated) {
def.scope.functions.del(def.name);
def.scope.variables.del(def.name);
}
}
function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10;
do {
@@ -805,6 +836,7 @@ merge(Compressor.prototype, {
function collapse(statements, compressor) {
var scope = compressor.find_parent(AST_Scope);
if (scope.uses_eval || scope.uses_with) return statements;
var args;
var candidates = [];
var stat_index = statements.length;
while (--stat_index >= 0) {
@@ -825,7 +857,7 @@ merge(Compressor.prototype, {
var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1;
var side_effects = value_has_side_effects(candidate);
var hit = candidate.name instanceof AST_SymbolFunarg;
var abort = false, replaced = false;
var abort = false, replaced = false, can_replace = !args || !hit;
var tt = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
@@ -850,7 +882,8 @@ merge(Compressor.prototype, {
return node;
}
// Replace variable with assignment when found
if (!(node instanceof AST_SymbolDeclaration)
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& !is_lhs(node, parent)
&& lhs.equivalent_to(node)) {
CHANGED = replaced = abort = true;
@@ -901,6 +934,12 @@ merge(Compressor.prototype, {
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; j < args.length; j++) {
args[j].transform(tt);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(tt);
}
@@ -918,12 +957,18 @@ merge(Compressor.prototype, {
&& iife.expression === fn) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && fn.body.indexOf(fn_strict) < 0) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = fn.argnames.length; --i >= 0;) {
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
var arg = iife.args[i];
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else {
var tw = new TreeWalker(function(node) {
@@ -970,7 +1015,8 @@ merge(Compressor.prototype, {
function get_lhs(expr) {
if (expr instanceof AST_VarDef) {
var def = expr.name.definition();
if (def.orig.length > 1 && !(expr.name instanceof AST_SymbolFunarg)
if (def.orig.length - (def._eliminiated || 0) > 1
&& !(expr.name instanceof AST_SymbolFunarg)
|| def.references.length == 1 && !compressor.exposed(def)) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
@@ -979,6 +1025,10 @@ merge(Compressor.prototype, {
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
@@ -989,7 +1039,7 @@ merge(Compressor.prototype, {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw);
get_rvalue(expr).walk(tw);
return lvalues;
}
@@ -1010,10 +1060,12 @@ merge(Compressor.prototype, {
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node === expr) {
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
remove(node.name.definition().orig, node.name);
drop_decl(node.name.definition());
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
@@ -1022,16 +1074,12 @@ merge(Compressor.prototype, {
case 0: return null;
case 1: return node.expressions[0];
}
if (node instanceof AST_Definitions && node.definitions.length == 0
|| node instanceof AST_SimpleStatement && !node.body) {
return null;
}
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return expr[expr instanceof AST_Assign ? "right" : "value"].has_side_effects(compressor);
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
@@ -2148,18 +2196,25 @@ merge(Compressor.prototype, {
}
def(AST_Node, return_false);
def(AST_Constant, return_true);
def(AST_Function, function(){
def(AST_Lambda, function(scope){
var self = this;
var result = true;
self.walk(new TreeWalker(function(node) {
if (!result) return true;
if (node instanceof AST_SymbolRef) {
var def = node.definition();
if (self.enclosed.indexOf(def) >= 0
&& self.variables.get(def.name) !== def) {
if (member(def, self.enclosed)
&& !self.variables.has(def.name)) {
if (scope) {
var scope_def = scope.find_variable(node);
if (def.undeclared ? !scope_def : scope_def === def) {
result = "f";
return true;
}
}
result = false;
return true;
}
return true;
}
}));
return result;
@@ -2269,58 +2324,63 @@ merge(Compressor.prototype, {
// this scope (not in nested scopes).
var scope = this;
var tw = new TreeWalker(function(node, descend){
if (node !== self) {
if (node instanceof AST_Defun) {
if (!drop_funcs && scope === self) {
var node_def = node.name.definition();
if (node === self) return;
if (node instanceof AST_Defun) {
if (!drop_funcs && scope === self) {
var node_def = node.name.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
initializations.add(node.name.name, node);
return true; // don't go in nested scopes
}
if (node instanceof AST_SymbolFunarg && scope === self) {
var_defs_by_id.add(node.definition().id, node);
}
if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def){
var node_def = def.name.definition();
if (def.name instanceof AST_SymbolVar) {
var_defs_by_id.add(node_def.id, def);
}
if (!drop_vars) {
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
initializations.add(node.name.name, node);
return true; // don't go in nested scopes
}
if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def){
var node_def = def.name.definition();
if (def.name instanceof AST_SymbolVar) {
var_defs_by_id.add(node_def.id, def);
if (def.value) {
initializations.add(def.name.name, def.value);
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
if (!drop_vars) {
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
if (def.value) {
initializations.add(def.name.name, def.value);
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
}
});
return true;
}
if (assign_as_unused(node) instanceof AST_SymbolRef && scope === self) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
});
return true;
}
var sym;
if (scope === self
&& (sym = assign_as_unused(node)) instanceof AST_SymbolRef
&& self.variables.get(sym.name) === sym.definition()) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
});
self.walk(tw);
@@ -2375,8 +2435,10 @@ merge(Compressor.prototype, {
}
}
if (drop_funcs && node instanceof AST_Defun && node !== self) {
if (!(node.name.definition().id in in_use_ids)) {
var def = node.name.definition();
if (!(def.id in in_use_ids)) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
drop_decl(def);
return make_node(AST_EmptyStatement, node);
}
return node;
@@ -2398,7 +2460,7 @@ merge(Compressor.prototype, {
if (var_defs.length > 1 && !def.value) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
remove(var_defs, def);
remove(sym.orig, def.name);
drop_decl(sym);
return;
}
}
@@ -2431,7 +2493,7 @@ merge(Compressor.prototype, {
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
remove(sym.orig, def.name);
drop_decl(sym);
}
});
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
@@ -2440,7 +2502,7 @@ merge(Compressor.prototype, {
var def = tail.pop();
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
remove(var_defs, def);
remove(def.name.definition().orig, def.name);
drop_decl(def.name.definition());
side_effects.unshift(make_node(AST_Assign, def, {
operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name),
@@ -2469,10 +2531,9 @@ merge(Compressor.prototype, {
}
}
if (drop_vars) {
var def = assign_as_unused(node);
if (def instanceof AST_SymbolRef
&& !((def = def.definition()).id in in_use_ids)
&& self.variables.get(def.name) === def) {
var sym = assign_as_unused(node);
if (sym instanceof AST_SymbolRef
&& !(sym.definition().id in in_use_ids)) {
if (node instanceof AST_Assign) {
return maintain_this_binding(parent, node, node.right.transform(tt));
}
@@ -3242,7 +3303,7 @@ merge(Compressor.prototype, {
}));
if (reduce_vars) name.definition().fixed = false;
}
remove(def.name.definition().orig, def.name);
drop_decl(def.name.definition());
return a;
}, []);
if (assignments.length == 0) return null;
@@ -3484,6 +3545,7 @@ merge(Compressor.prototype, {
&& !exp.uses_arguments
&& !exp.uses_eval
&& exp.body.length == 1
&& !exp.contains_this()
&& all(exp.argnames, function(arg) {
return arg.__unused;
})
@@ -3497,23 +3559,6 @@ merge(Compressor.prototype, {
expression: stat.body
});
}
if (value) {
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;
}
}
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).optimize(compressor);
@@ -4176,6 +4221,17 @@ merge(Compressor.prototype, {
return self;
});
function recursive_ref(compressor, def) {
var node;
for (var i = 0; node = compressor.parent(i); i++) {
if (node instanceof AST_Lambda) {
var name = node.name;
if (name && name.definition() === def) break;
}
}
return node;
}
OPT(AST_SymbolRef, function(self, compressor){
var def = self.resolve_defines(compressor);
if (def) {
@@ -4201,49 +4257,68 @@ merge(Compressor.prototype, {
if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed);
}
if (compressor.option("unused")
&& fixed
&& d.references.length == 1
&& (d.single_use || fixed instanceof AST_Function
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
&& !d.scope.uses_eval
&& compressor.find_parent(AST_Scope) === fixed.parent_scope)) {
if (d.single_use && fixed instanceof AST_Function) {
if (!compressor.option("reduce_funcs") && d.scope !== self.scope) {
d.single_use = false;
} else if (d.escaped && d.scope !== self.scope || recursive_ref(compressor, d)) {
d.single_use = false;
} else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) {
d.single_use = fixed.is_constant_expression(self.scope);
if (d.single_use == "f") {
var scope = self.scope;
do {
if (scope.name) scope.name.definition().single_use = false;
} while (scope = scope.parent_scope);
}
}
}
if (d.single_use && fixed) {
var value = fixed.optimize(compressor);
return value === fixed ? fixed.clone(true) : value;
}
if (compressor.option("evaluate") && fixed) {
if (d.should_replace === undefined) {
var init = fixed.evaluate(compressor);
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
init = make_node_from_constant(init, fixed);
var value_length = init.optimize(compressor).print_to_string().length;
var fn;
if (has_symbol_ref(fixed)) {
fn = function() {
var result = init.optimize(compressor);
return result === init ? result.clone(true) : result;
};
} else {
value_length = Math.min(value_length, fixed.print_to_string().length);
fn = function() {
var result = best_of_expression(init.optimize(compressor), fixed);
return result === init || result === fixed ? result.clone(true) : result;
};
}
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
d.should_replace = false;
if (fixed && d.should_replace === undefined) {
var init;
if (fixed instanceof AST_This) {
if (!(d.orig[0] instanceof AST_SymbolFunarg)
&& all(d.references, function(ref) {
return d.scope === ref.scope;
})) {
init = fixed;
}
} else {
var ev = fixed.evaluate(compressor);
if (ev !== fixed && (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))) {
init = make_node_from_constant(ev, fixed);
}
}
if (d.should_replace) {
return d.should_replace();
if (init) {
var value_length = init.optimize(compressor).print_to_string().length;
var fn;
if (has_symbol_ref(fixed)) {
fn = function() {
var result = init.optimize(compressor);
return result === init ? result.clone(true) : result;
};
} else {
value_length = Math.min(value_length, fixed.print_to_string().length);
fn = function() {
var result = best_of_expression(init.optimize(compressor), fixed);
return result === init || result === fixed ? result.clone(true) : result;
};
}
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
d.should_replace = false;
}
}
if (d.should_replace) {
return d.should_replace();
}
}
return self;
@@ -4561,11 +4636,11 @@ merge(Compressor.prototype, {
}
}
if (is_lhs(self, compressor.parent())) return self;
if (compressor.option("properties") && key !== prop) {
var node = self.flatten_object(property);
if (node) {
expr = self.expression = node.expression;
prop = self.property = node.property;
if (key !== prop) {
var sub = self.flatten_object(property, compressor);
if (sub) {
expr = self.expression = sub.expression;
prop = self.property = sub.property;
}
}
if (compressor.option("properties") && compressor.option("side_effects")
@@ -4611,7 +4686,8 @@ merge(Compressor.prototype, {
return self;
});
AST_Lambda.DEFMETHOD("contains_this", function() {
AST_Lambda.DEFMETHOD("contains_this", function(grandparent) {
if (grandparent instanceof AST_New) return false;
var result;
var self = this;
self.walk(new TreeWalker(function(node) {
@@ -4622,7 +4698,8 @@ merge(Compressor.prototype, {
return result;
});
AST_PropAccess.DEFMETHOD("flatten_object", function(key) {
AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
if (!compressor.option("properties")) return;
var expr = this.expression;
if (expr instanceof AST_Object) {
var props = expr.properties;
@@ -4633,7 +4710,8 @@ merge(Compressor.prototype, {
return prop instanceof AST_ObjectKeyVal;
})) break;
var value = prop.value;
if (value instanceof AST_Function && value.contains_this()) break;
if (value instanceof AST_Function
&& value.contains_this(compressor.parent())) break;
return make_node(AST_Sub, this, {
expression: make_node(AST_Array, expr, {
elements: props.map(function(prop) {
@@ -4677,10 +4755,8 @@ merge(Compressor.prototype, {
}
}
if (is_lhs(self, compressor.parent())) return self;
if (compressor.option("properties")) {
var node = self.flatten_object(self.property);
if (node) return node.optimize(compressor);
}
var sub = self.flatten_object(self.property, compressor);
if (sub) return sub.optimize(compressor);
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);

View File

@@ -137,11 +137,9 @@ function minify(files, options) {
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (timings) timings.scope1 = Date.now();
if (options.compress) toplevel.figure_out_scope(options.mangle);
if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now();
if (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) {
@@ -199,9 +197,9 @@ function minify(files, options) {
if (timings) {
timings.end = Date.now();
result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2),
compress: 1e-3 * (timings.scope2 - timings.compress),
parse: 1e-3 * (timings.compress - timings.parse),
compress: 1e-3 * (timings.scope - timings.compress),
scope: 1e-3 * (timings.mangle - timings.scope),
mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.1.6",
"version": "3.1.9",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -129,6 +129,7 @@ constant_join_3: {
for_loop: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe: true,
unused: true,
@@ -185,6 +186,7 @@ for_loop: {
index: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
@@ -203,6 +205,7 @@ index: {
length: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
@@ -221,6 +224,7 @@ length: {
index_length: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,

View File

@@ -2,7 +2,8 @@ collapse_vars_side_effects_1: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function f1() {
@@ -150,7 +151,8 @@ collapse_vars_issue_721: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
define(["require", "exports", 'handlebars'], function (require, exports, hb) {
@@ -216,7 +218,8 @@ collapse_vars_properties: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function f1(obj) {
@@ -243,7 +246,8 @@ collapse_vars_if: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function f1() {
@@ -293,7 +297,8 @@ collapse_vars_while: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:false, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function f1(y) {
@@ -712,7 +717,8 @@ collapse_vars_misc1: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function f0(o, a, h) {
@@ -789,7 +795,8 @@ collapse_vars_repeated: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function f1() {
@@ -831,7 +838,8 @@ collapse_vars_closures: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function constant_vars_can_be_replaced_in_any_scope() {
@@ -921,7 +929,8 @@ collapse_vars_try: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function f1() {
@@ -1118,7 +1127,8 @@ collapse_vars_constants: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true, reduce_vars:true
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
reduce_funcs: true, reduce_vars:true
}
input: {
function f1(x) {
@@ -1156,7 +1166,7 @@ collapse_vars_arguments: {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true,
toplevel:true, reduce_vars:true
toplevel:true, reduce_funcs: true, reduce_vars:true
}
input: {
var outer = function() {
@@ -1280,6 +1290,7 @@ collapse_vars_regexp: {
hoist_funs: true,
keep_fargs: true,
loops: false,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -1355,6 +1366,7 @@ issue_1562: {
options = {
collapse_vars: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -1388,6 +1400,7 @@ issue_1605_1: {
options = {
collapse_vars: true,
toplevel: false,
unused: true,
}
input: {
function foo(x) {
@@ -1410,6 +1423,7 @@ issue_1605_2: {
options = {
collapse_vars: true,
toplevel: "vars",
unused: true,
}
input: {
function foo(x) {
@@ -1537,6 +1551,7 @@ issue_1631_3: {
var_side_effects_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var print = console.log.bind(console);
@@ -1559,6 +1574,7 @@ var_side_effects_1: {
var_side_effects_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var print = console.log.bind(console);
@@ -1584,6 +1600,7 @@ var_side_effects_3: {
collapse_vars: true,
pure_getters: true,
unsafe: true,
unused: true,
}
input: {
var print = console.log.bind(console);
@@ -1606,6 +1623,7 @@ var_side_effects_3: {
reduce_vars_assign: {
options = {
collapse_vars: true,
reduce_funcs: true,
reduce_vars: true,
}
input: {
@@ -1628,6 +1646,7 @@ reduce_vars_assign: {
iife_1: {
options = {
collapse_vars: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -1648,6 +1667,7 @@ iife_1: {
iife_2: {
options = {
collapse_vars: true,
reduce_funcs: false,
reduce_vars: false,
toplevel: true,
unused: false,
@@ -1659,6 +1679,7 @@ iife_2: {
}(foo);
}
expect: {
var foo;
!function(x) {
console.log(x);
}(bar());
@@ -1945,6 +1966,7 @@ ref_scope: {
chained_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 2;
@@ -1961,6 +1983,7 @@ chained_1: {
chained_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a;
@@ -2061,6 +2084,7 @@ inner_lvalues: {
double_def: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = x, a = a && y;
@@ -2075,6 +2099,7 @@ double_def: {
toplevel_single_reference: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a;
@@ -2084,9 +2109,10 @@ toplevel_single_reference: {
}
}
expect: {
var a;
for (var b in x)
for (var b in x) {
var a;
b(a = b);
}
}
}
@@ -2094,6 +2120,7 @@ unused_orig: {
options = {
collapse_vars: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2132,6 +2159,7 @@ issue_315: {
collapse_vars: true,
evaluate: true,
keep_fargs: false,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
unused: true,
@@ -2358,6 +2386,7 @@ duplicate_argname: {
issue_2298: {
options = {
collapse_vars: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2723,6 +2752,7 @@ issue_2364_5: {
evaluate: true,
pure_getters: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2889,6 +2919,7 @@ pure_getters_chain: {
options = {
collapse_vars: true,
pure_getters: true,
unused: true,
}
input: {
function o(t, r) {
@@ -2909,6 +2940,7 @@ pure_getters_chain: {
conditional_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(a, b) {
@@ -2933,6 +2965,7 @@ conditional_1: {
conditional_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(a, b) {
@@ -2949,3 +2982,119 @@ conditional_2: {
}
expect_stdout: "5 5"
}
issue_2425_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect_stdout: "15"
}
issue_2425_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b, c) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b, c) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect_stdout: "15"
}
issue_2425_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b, b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b, b) {
(a |= 10).toString();
})(--a);
console.log(a);
}
expect_stdout: "15"
}
issue_2437: {
options = {
collapse_vars: true,
conditionals: true,
inline: true,
join_vars: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
bar();
}
function bar() {
if (xhrDesc) {
var req = new XMLHttpRequest();
var result = !!req.onreadystatechange;
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {});
return result;
}
else {
var req = new XMLHttpRequest();
var detectFunc = function () { };
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
return result;
}
}
foo();
}
expect: {
!function() {
if (xhrDesc)
return result = !!(req = new XMLHttpRequest()).onreadystatechange,
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result;
var req = new XMLHttpRequest(), detectFunc = function() {};
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
}();
}
}

View File

@@ -96,6 +96,7 @@ self_comparison_1: {
self_comparison_2: {
options = {
comparisons: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}

View File

@@ -292,6 +292,7 @@ global_timeout_and_interval_symbols: {
issue_2233_2: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unsafe: true,
@@ -323,6 +324,7 @@ issue_2233_2: {
issue_2233_3: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,

View File

@@ -673,6 +673,7 @@ issue_1539: {
vardef_value: {
options = {
keep_fnames: false,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -734,6 +735,7 @@ assign_chain: {
issue_1583: {
options = {
keep_fargs: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -1080,6 +1082,7 @@ var_catch_toplevel: {
options = {
conditionals: true,
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -1109,11 +1112,12 @@ var_catch_toplevel: {
}
}
issue_2105: {
issue_2105_1: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -1139,17 +1143,51 @@ issue_2105: {
});
}
expect: {
(function() {
var quux = function() {
({
prop: function() {
console.log;
console.log("PASS");
};
return {
prop: function() {
console.log;
quux();
}
}).prop();
}
expect_stdout: "PASS"
}
issue_2105_2: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
properties: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
};
})().prop();
return bar;
} );
});
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -251,6 +251,7 @@ unsafe_constant: {
unsafe_object: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
@@ -279,6 +280,7 @@ unsafe_object: {
unsafe_object_nested: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
@@ -307,6 +309,7 @@ unsafe_object_nested: {
unsafe_object_complex: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
@@ -335,6 +338,7 @@ unsafe_object_complex: {
unsafe_object_repeated: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
@@ -363,6 +367,7 @@ unsafe_object_repeated: {
unsafe_object_accessor: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe: true,
}
@@ -663,6 +668,7 @@ call_args: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
@@ -686,6 +692,7 @@ call_args_drop_param: {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -1016,6 +1023,7 @@ Infinity_NaN_undefined_LHS: {
issue_1964_1: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe_regexp: false,
unused: true,
@@ -1045,6 +1053,7 @@ issue_1964_1: {
issue_1964_2: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
@@ -1201,6 +1210,7 @@ issue_2231_2: {
self_comparison_1: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
@@ -1221,6 +1231,7 @@ self_comparison_2: {
evaluate: true,
hoist_props: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,

View File

@@ -19,6 +19,7 @@ iifes_returning_constants_keep_fargs_true: {
booleans : true,
if_return : true,
join_vars : true,
reduce_funcs : true,
reduce_vars : true,
cascade : true,
inline : true,
@@ -55,6 +56,7 @@ iifes_returning_constants_keep_fargs_false: {
booleans : true,
if_return : true,
join_vars : true,
reduce_funcs : true,
reduce_vars : true,
cascade : true,
inline : true,
@@ -101,6 +103,7 @@ issue_1841_1: {
options = {
keep_fargs: false,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -127,6 +130,7 @@ issue_1841_2: {
options = {
keep_fargs: false,
pure_getters: false,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -154,6 +158,7 @@ function_returning_constant_literal: {
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -305,6 +310,7 @@ issue_2084: {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
@@ -340,6 +346,7 @@ issue_2084: {
issue_2097: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -508,3 +515,42 @@ issue_2114_2: {
}
expect_stdout: "2"
}
issue_2428: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function bar(k) {
console.log(k);
}
function foo(x) {
return bar(x);
}
function baz(a) {
foo(a);
}
baz(42);
baz("PASS");
}
expect: {
function baz(a) {
console.log(a);
}
baz(42);
baz("PASS");
}
expect_stdout: [
"42",
"PASS",
]
}

View File

@@ -3,6 +3,7 @@ issue_2377_1: {
evaluate: true,
inline: true,
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -35,6 +36,7 @@ issue_2377_2: {
inline: true,
hoist_props: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -66,6 +68,7 @@ issue_2377_3: {
inline: true,
hoist_props: true,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -92,6 +95,7 @@ issue_2377_3: {
direct_access_1: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -120,6 +124,7 @@ direct_access_1: {
direct_access_2: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -143,6 +148,7 @@ direct_access_2: {
direct_access_3: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -163,6 +169,7 @@ direct_access_3: {
single_use: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -187,6 +194,7 @@ single_use: {
name_collision_1: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
@@ -224,6 +232,7 @@ name_collision_1: {
name_collision_2: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
@@ -257,6 +266,7 @@ name_collision_2: {
name_collision_3: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
@@ -293,6 +303,7 @@ contains_this_1: {
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -318,6 +329,7 @@ contains_this_2: {
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -345,6 +357,7 @@ contains_this_3: {
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -369,3 +382,32 @@ contains_this_3: {
}
expect_stdout: "1 1 true"
}
new_this: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: 1,
b: 2,
f: function(a) {
this.b = a;
}
};
console.log(new o.f(o.a).b, o.b);
}
expect: {
console.log(new function(a) {
this.b = a;
}(1).b, 2);
}
expect_stdout: "1 2"
}

View File

@@ -1,6 +1,7 @@
const_pragma: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
};
@@ -16,6 +17,7 @@ const_pragma: {
not_const: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
};

View File

@@ -2,6 +2,7 @@ chained_evaluation_1: {
options = {
collapse_vars: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -28,6 +29,7 @@ chained_evaluation_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}

View File

@@ -15,6 +15,7 @@ f7: {
negate_iife: true,
passes: 3,
properties: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,

View File

@@ -1,5 +1,6 @@
side_effects_catch: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -34,6 +35,7 @@ side_effects_catch: {
side_effects_else: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -62,6 +64,7 @@ side_effects_else: {
side_effects_finally: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -98,6 +101,7 @@ side_effects_finally: {
side_effects_label: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -130,6 +134,7 @@ side_effects_label: {
side_effects_switch: {
options = {
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,

View File

@@ -2,6 +2,7 @@ unary_prefix: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}

View File

@@ -1,6 +1,7 @@
iife_for: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -26,6 +27,7 @@ iife_for: {
iife_for_in: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -51,6 +53,7 @@ iife_for_in: {
iife_do: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -80,6 +83,7 @@ iife_do: {
iife_while: {
options = {
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,

View File

@@ -3,6 +3,7 @@ collapse_vars_constants: {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -240,6 +241,7 @@ negate_iife_issue_1073: {
evaluate: true,
inline: true,
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
unused: true,
@@ -267,6 +269,7 @@ issue_1288_side_effects: {
evaluate: true,
inline: true,
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -299,6 +302,7 @@ inner_var_for_in_1: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
}
input: {
@@ -330,6 +334,7 @@ issue_1595_3: {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}

View File

@@ -51,6 +51,7 @@ this_binding_collapse_vars: {
options = {
collapse_vars: true,
toplevel: true,
unused: true,
};
input: {
var c = a; c();

View File

@@ -833,6 +833,7 @@ lhs_prop_2: {
evaluate: true,
inline: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -879,6 +880,7 @@ prop_side_effects_1: {
evaluate: true,
inline: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -915,6 +917,7 @@ prop_side_effects_2: {
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -1006,3 +1009,22 @@ array_hole: {
}
expect_stdout: "2 undefined 3"
}
new_this: {
options = {
properties: true,
side_effects: true,
}
input: {
new {
f: function(a) {
this.a = a;
}
}.f(42);
}
expect: {
new function(a) {
this.a = a;
}(42);
}
}

View File

@@ -1,6 +1,7 @@
strict: {
options = {
pure_getters: "strict",
reduce_funcs: false,
reduce_vars: false,
side_effects: true,
toplevel: true,
@@ -30,6 +31,7 @@ strict: {
strict_reduce_vars: {
options = {
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -58,6 +60,7 @@ strict_reduce_vars: {
unsafe: {
options = {
pure_getters: true,
reduce_funcs: false,
reduce_vars: false,
side_effects: true,
toplevel: true,
@@ -84,6 +87,7 @@ unsafe: {
unsafe_reduce_vars: {
options = {
pure_getters: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -185,6 +189,7 @@ issue_2110_1: {
pure_getters: "strict",
sequences: true,
side_effects: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -215,6 +220,7 @@ issue_2110_2: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -247,6 +253,7 @@ set_immutable_1: {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -270,6 +277,7 @@ set_immutable_2: {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
@@ -293,6 +301,7 @@ set_immutable_3: {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -318,6 +327,7 @@ set_immutable_4: {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
@@ -343,6 +353,7 @@ set_mutable_1: {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -367,6 +378,7 @@ set_mutable_2: {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,

File diff suppressed because it is too large Load Diff

View File

@@ -187,6 +187,7 @@ dont_screw_try_catch_undefined: {
reduce_vars: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
ie8: true,
unused: true,

View File

@@ -714,6 +714,7 @@ issue_1705_2: {
options = {
dead_code: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,

View File

@@ -297,4 +297,36 @@ describe("minify", function() {
assert.strictEqual(result.code, "alert({bar:42});");
});
});
describe("collapse_vars", function() {
it("Should not produce invalid AST", function() {
var code = [
"function f(a) {",
" a = x();",
" return a;",
"}",
"f();",
].join("\n");
var ast = Uglify.minify(code, {
compress: false,
mangle: false,
output: {
ast: true
},
}).ast;
assert.strictEqual(ast.TYPE, "Toplevel");
assert.strictEqual(ast.body.length, 2);
assert.strictEqual(ast.body[0].TYPE, "Defun");
assert.strictEqual(ast.body[0].body.length, 2);
assert.strictEqual(ast.body[0].body[0].TYPE, "SimpleStatement");
var stat = ast.body[0].body[0];
Uglify.minify(ast, {
compress: {
sequences: false
}
});
assert.ok(stat.body);
assert.strictEqual(stat.print_to_string(), "a=x()");
});
});
});

View File

@@ -162,6 +162,7 @@ var VALUES = [
'"object"',
'"number"',
'"function"',
'this',
];
var BINARY_OPS_NO_COMMA = [
@@ -349,10 +350,10 @@ function createParams() {
return params.join(', ');
}
function createArgs() {
function createArgs(recurmax, stmtDepth, canThrow) {
var args = [];
for (var n = rng(4); --n >= 0;) {
args.push(createValue());
args.push(rng(2) ? createValue() : createExpression(recurmax - 1, COMMA_OK, stmtDepth, canThrow));
}
return args.join(', ');
}
@@ -390,9 +391,10 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
VAR_NAMES.length = namesLenBefore;
if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s + '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ');';
if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s;
// avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name;
s += '(' + createArgs(recurmax, stmtDepth, canThrow) + ');';
return s;
}
@@ -626,6 +628,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
case p++:
return createValue();
case p++:
case p++:
return getVarName();
case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: