Compare commits

...

29 Commits

Author SHA1 Message Date
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
Alex Lam S.L
2fd927a7cc v3.1.6 2017-10-29 12:38:10 +08:00
Alex Lam S.L
8428326ea1 enhance properties (#2412)
- trim array items only if `side_effects`
- extend to non-identifier properties
2017-10-29 04:11:26 +08:00
Alex Lam S.L
31f8209193 remove dead code (#2405) 2017-10-27 14:28:09 +08:00
Alex Lam S.L
9b0f86f5a1 fix reduce_vars on AST_Array.length (#2404) 2017-10-27 02:33:37 +08:00
Alex Lam S.L
ee082ace1b compress self comparisons (#2398) 2017-10-26 01:16:12 +08:00
kzc
ae67a49850 document compress option hoist_props (#2399) 2017-10-25 14:03:43 +08:00
Alex Lam S.L
4178289c38 implement hoist_props (#2396)
fixes #2377
2017-10-25 03:38:11 +08:00
Alex Lam S.L
74ae16f9f8 fix unsafe reduce_vars on arrays & objects (#2397) 2017-10-24 22:10:36 +08:00
Tom MacWright
1968203d83 docs: Fix spelling and style (#2395) 2017-10-24 04:59:12 +08:00
Alex Lam S.L
86ea38a259 enhance unsafe evaluate of arrays & objects (#2394) 2017-10-24 02:58:30 +08:00
Alex Lam S.L
8a713e449f deduplicate declarations regardless of toplevel (#2393) 2017-10-23 01:00:50 +08:00
Alex Lam S.L
24aa07855b safer properties transform (#2391)
`{ a: x, b: y }.a` => `[ x, y ][0]`
- `x` cannot be function containing `this`

`[ x, y, z ][1]` => `(x, z, y)`
- only if `z` is side-effect-free
2017-10-22 20:10:13 +08:00
Alex Lam S.L
5fd723f143 fix unsafe expansion of object literals (#2390) 2017-10-22 15:00:36 +08:00
Alex Lam S.L
516eaef50c fix unsafe evaluation of AST_Sub (#2389) 2017-10-22 13:14:15 +08:00
Alex Lam S.L
4ae1fb3ed8 fix unsafe evaluation of objects (#2388) 2017-10-22 04:19:40 +08:00
Alex Lam S.L
011123223b fix unsafe escape analysis in reduce_vars (#2387) 2017-10-22 03:23:31 +08:00
16 changed files with 2387 additions and 385 deletions

View File

@@ -221,7 +221,7 @@ to prevent the `require`, `exports` and `$` names from being changed.
is a separate step, different from variable name mangling. Pass is a separate step, different from variable name mangling. Pass
`--mangle-props` to enable it. It will mangle all properties in the `--mangle-props` to enable it. It will mangle all properties in the
input code with the exception of built in DOM properties and properties input code with the exception of built in DOM properties and properties
in core javascript classes. For example: in core JavaScript classes. For example:
```javascript ```javascript
// example.js // example.js
@@ -236,7 +236,7 @@ x.bar_ = 2;
x["baz_"] = 3; x["baz_"] = 3;
console.log(x.calc()); console.log(x.calc());
``` ```
Mangle all properties (except for javascript `builtins`): Mangle all properties (except for JavaScript `builtins`):
```bash ```bash
$ uglifyjs example.js -c -m --mangle-props $ uglifyjs example.js -c -m --mangle-props
``` ```
@@ -632,6 +632,12 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `hoist_funs` (default: `true`) -- hoist function declarations - `hoist_funs` (default: `true`) -- hoist function declarations
- `hoist_props` (default: `false`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher,
and the `compress` option `toplevel` enabled.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false` - `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general) by default because it seems to increase the size of the output in general)
@@ -1060,7 +1066,7 @@ in total it's a bit more than just using UglifyJS's own parser.
### Uglify Fast Minify Mode ### Uglify Fast Minify Mode
It's not well known, but whitespace removal and symbol mangling accounts It's not well known, but whitespace removal and symbol mangling accounts
for 95% of the size reduction in minified code for most javascript - not for 95% of the size reduction in minified code for most JavaScript - not
elaborate code transforms. One can simply disable `compress` to speed up elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to comparable minify speeds and gzip sizes to

View File

@@ -60,6 +60,7 @@ function Compressor(options, false_by_default) {
expression : false, expression : false,
global_defs : {}, global_defs : {},
hoist_funs : !false_by_default, hoist_funs : !false_by_default,
hoist_props : false,
hoist_vars : false, hoist_vars : false,
ie8 : false, ie8 : false,
if_return : !false_by_default, if_return : !false_by_default,
@@ -149,7 +150,9 @@ merge(Compressor.prototype, {
} }
var passes = +this.options.passes || 1; var passes = +this.options.passes || 1;
var last_count = 1 / 0; var last_count = 1 / 0;
var mangle = { ie8: this.option("ie8") };
for (var pass = 0; pass < passes; pass++) { for (var pass = 0; pass < passes; pass++) {
node.figure_out_scope(mangle);
if (pass > 0 || this.option("reduce_vars")) if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this); node.reset_opt_flags(this);
node = node.transform(this); node = node.transform(this);
@@ -190,6 +193,7 @@ merge(Compressor.prototype, {
if (node._squeezed) return node; if (node._squeezed) return node;
var was_scope = false; var was_scope = false;
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
node = node.hoist_properties(this);
node = node.hoist_declarations(this); node = node.hoist_declarations(this);
was_scope = true; was_scope = true;
} }
@@ -283,7 +287,7 @@ merge(Compressor.prototype, {
self.transform(tt); 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 reduce_vars = compressor.option("reduce_vars");
var unused = compressor.option("unused"); var unused = compressor.option("unused");
// Stack of look-up tables to keep track of whether a `SymbolDef` has been // Stack of look-up tables to keep track of whether a `SymbolDef` has been
@@ -311,29 +315,29 @@ merge(Compressor.prototype, {
d.references.push(node); d.references.push(node);
if (d.fixed === undefined || !safe_to_read(d) || d.single_use == "m") { if (d.fixed === undefined || !safe_to_read(d) || d.single_use == "m") {
d.fixed = false; d.fixed = false;
} else { } else if (d.fixed) {
var value = node.fixed_value(); var value = node.fixed_value();
if (unused) { if (value && ref_once(d)) {
d.single_use = value if (value instanceof AST_Lambda) {
&& d.references.length == 1 d.single_use = d.scope === node.scope
&& loop_ids[d.id] === in_loop && !(d.orig[0] instanceof AST_SymbolFunarg)
&& d.scope === node.scope || value.is_constant_expression(node.scope);
&& value.is_constant_expression(); } else {
d.single_use = d.scope === node.scope
&& loop_ids[d.id] === in_loop
&& value.is_constant_expression();
}
} else {
d.single_use = false;
} }
if (is_modified(node, 0, is_immutable(value))) { if (is_modified(node, value, 0, is_immutable(value))) {
if (d.single_use) { if (d.single_use) {
d.single_use = "m"; d.single_use = "m";
} else { } else {
d.fixed = false; d.fixed = false;
} }
} else { } else {
var parent = tw.parent(); mark_escaped(d, node, value, 0);
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;
}
} }
} }
} }
@@ -381,6 +385,10 @@ merge(Compressor.prototype, {
} else { } else {
d.fixed = node; d.fixed = node;
mark(d, true); mark(d, true);
if (ref_once(d)) {
d.single_use = d.scope === d.references[0].scope
|| node.is_constant_expression(d.references[0].scope);
}
} }
var save_ids = safe_ids; var save_ids = safe_ids;
safe_ids = Object.create(null); safe_ids = Object.create(null);
@@ -531,6 +539,7 @@ merge(Compressor.prototype, {
} }
return true; return true;
} }
return def.fixed instanceof AST_Defun;
} }
function safe_to_assign(def, value) { function safe_to_assign(def, value) {
@@ -553,8 +562,9 @@ merge(Compressor.prototype, {
} }
function reset_def(def) { function reset_def(def) {
def.direct_access = false;
def.escaped = false; def.escaped = false;
if (def.scope.uses_eval) { if (def.scope.uses_eval || def.scope.uses_with) {
def.fixed = false; def.fixed = false;
} else if (!compressor.exposed(def)) { } else if (!compressor.exposed(def)) {
def.fixed = undefined; def.fixed = undefined;
@@ -566,22 +576,72 @@ merge(Compressor.prototype, {
def.single_use = undefined; def.single_use = undefined;
} }
function is_immutable(value) { function ref_once(def) {
return value && (value.is_constant() || value instanceof AST_Lambda); return unused && !def.scope.uses_eval && !def.scope.uses_with && def.references.length == 1;
} }
function is_modified(node, level, immutable) { function is_immutable(value) {
if (!value) return false;
return value.is_constant()
|| value instanceof AST_Lambda
|| value instanceof AST_This;
}
function read_property(obj, key) {
if (key instanceof AST_Constant) key = key.getValue();
if (key instanceof AST_Node) return null;
var value;
if (obj instanceof AST_Array) {
var elements = obj.elements;
if (key == "length") return make_node_from_constant(elements.length, obj);
if (typeof key == "number" && key in elements) value = elements[key];
} else if (obj instanceof AST_Object) {
var props = obj.properties;
for (var i = props.length; --i >= 0;) {
var prop = props[i];
if (!(prop instanceof AST_ObjectKeyVal)) return;
if (!value && props[i].key === key) value = props[i].value;
}
}
return value instanceof AST_SymbolRef && value.fixed_value() || value;
}
function is_modified(node, value, level, immutable) {
var parent = tw.parent(level); var parent = tw.parent(level);
if (is_lhs(node, parent) if (is_lhs(node, parent)
|| !immutable && parent instanceof AST_Call && parent.expression === node) { || !immutable
&& parent instanceof AST_Call
&& parent.expression === node
&& (!(value instanceof AST_Function) || value.contains_this(parent))) {
return true; return true;
} else if (parent instanceof AST_Array || parent instanceof AST_Object) {
return is_modified(parent, parent, level + 1);
} else if (parent instanceof AST_PropAccess && parent.expression === node) { } else if (parent instanceof AST_PropAccess && parent.expression === node) {
return !immutable && is_modified(parent, level + 1); return !immutable && is_modified(parent, read_property(value, parent.property), level + 1);
} }
} }
function mark_escaped(d, node, value, level) {
var parent = tw.parent(level);
if (value instanceof AST_Constant || 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) {
mark_escaped(d, parent, parent, level + 1);
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
value = read_property(value, parent.property);
mark_escaped(d, parent, value, level + 1);
if (value) return;
}
if (level == 0) d.direct_access = true;
}
}); });
AST_SymbolRef.DEFMETHOD("fixed_value", function() { AST_Symbol.DEFMETHOD("fixed_value", function() {
var fixed = this.definition().fixed; var fixed = this.definition().fixed;
if (!fixed || fixed instanceof AST_Node) return fixed; if (!fixed || fixed instanceof AST_Node) return fixed;
return fixed(); return fixed();
@@ -732,6 +792,14 @@ merge(Compressor.prototype, {
|| compressor.option("unsafe") && global_names(this.name); || 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) { function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10; var CHANGED, max_iter = 10;
do { do {
@@ -765,6 +833,7 @@ merge(Compressor.prototype, {
function collapse(statements, compressor) { function collapse(statements, compressor) {
var scope = compressor.find_parent(AST_Scope); var scope = compressor.find_parent(AST_Scope);
if (scope.uses_eval || scope.uses_with) return statements; if (scope.uses_eval || scope.uses_with) return statements;
var args;
var candidates = []; var candidates = [];
var stat_index = statements.length; var stat_index = statements.length;
while (--stat_index >= 0) { while (--stat_index >= 0) {
@@ -785,7 +854,7 @@ merge(Compressor.prototype, {
var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1; var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1;
var side_effects = value_has_side_effects(candidate); var side_effects = value_has_side_effects(candidate);
var hit = candidate.name instanceof AST_SymbolFunarg; 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) { var tt = new TreeTransformer(function(node, descend) {
if (abort) return node; if (abort) return node;
// Skip nodes before `candidate` as quickly as possible // Skip nodes before `candidate` as quickly as possible
@@ -810,7 +879,8 @@ merge(Compressor.prototype, {
return node; return node;
} }
// Replace variable with assignment when found // Replace variable with assignment when found
if (!(node instanceof AST_SymbolDeclaration) if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& !is_lhs(node, parent) && !is_lhs(node, parent)
&& lhs.equivalent_to(node)) { && lhs.equivalent_to(node)) {
CHANGED = replaced = abort = true; CHANGED = replaced = abort = true;
@@ -861,6 +931,12 @@ merge(Compressor.prototype, {
// Skip (non-executed) functions and (leading) default case in switch statements // Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node; 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++) { for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(tt); statements[i].transform(tt);
} }
@@ -878,12 +954,18 @@ merge(Compressor.prototype, {
&& iife.expression === fn) { && iife.expression === fn) {
var fn_strict = compressor.has_directive("use strict"); var fn_strict = compressor.has_directive("use strict");
if (fn_strict && fn.body.indexOf(fn_strict) < 0) fn_strict = false; 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); 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 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; if (sym.name in names) continue;
names[sym.name] = true; names[sym.name] = true;
var arg = iife.args[i];
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor); if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else { else {
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
@@ -930,7 +1012,8 @@ merge(Compressor.prototype, {
function get_lhs(expr) { function get_lhs(expr) {
if (expr instanceof AST_VarDef) { if (expr instanceof AST_VarDef) {
var def = expr.name.definition(); 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)) { || def.references.length == 1 && !compressor.exposed(def)) {
return make_node(AST_SymbolRef, expr.name, expr.name); return make_node(AST_SymbolRef, expr.name, expr.name);
} }
@@ -939,6 +1022,10 @@ merge(Compressor.prototype, {
} }
} }
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) { function get_lvalues(expr) {
var lvalues = Object.create(null); var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues; if (expr instanceof AST_Unary) return lvalues;
@@ -949,7 +1036,7 @@ merge(Compressor.prototype, {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent()); 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; return lvalues;
} }
@@ -973,7 +1060,9 @@ merge(Compressor.prototype, {
if (node === expr) { if (node === expr) {
found = true; found = true;
if (node instanceof AST_VarDef) { 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; return in_list ? MAP.skip : null;
} }
@@ -982,16 +1071,13 @@ merge(Compressor.prototype, {
case 0: return null; case 0: return null;
case 1: return node.expressions[0]; case 1: return node.expressions[0];
} }
if (node instanceof AST_Definitions && node.definitions.length == 0 if (node instanceof AST_SimpleStatement && !node.body) return null;
|| node instanceof AST_SimpleStatement && !node.body) {
return null;
}
})); }));
} }
function value_has_side_effects(expr) { function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false; 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) { function references_in_scope(def) {
@@ -1607,35 +1693,6 @@ merge(Compressor.prototype, {
&& unaryPrefix(this.operator); && unaryPrefix(this.operator);
} }
}); });
// Obtain the constant value of an expression already known to be constant.
// Result only valid iff this.is_constant() is true.
AST_Node.DEFMETHOD("constant_value", function(compressor){
// Accomodate when option evaluate=false.
if (this instanceof AST_Constant && !(this instanceof AST_RegExp)) {
return this.value;
}
// Accomodate the common constant expressions !0 and -1 when option evaluate=false.
if (this instanceof AST_UnaryPrefix
&& this.expression instanceof AST_Constant) switch (this.operator) {
case "!":
return !this.expression.value;
case "~":
return ~this.expression.value;
case "-":
return -this.expression.value;
case "+":
return +this.expression.value;
default:
throw new Error(string_template("Cannot evaluate unary expression {value}", {
value: this.print_to_string()
}));
}
var result = this.evaluate(compressor);
if (result !== this) {
return result;
}
throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
});
def(AST_Statement, function(){ def(AST_Statement, function(){
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start)); throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
}); });
@@ -1654,6 +1711,7 @@ merge(Compressor.prototype, {
var elements = []; var elements = [];
for (var i = 0, len = this.elements.length; i < len; i++) { for (var i = 0, len = this.elements.length; i < len; i++) {
var element = this.elements[i]; var element = this.elements[i];
if (element instanceof AST_Function) continue;
var value = ev(element, compressor); var value = ev(element, compressor);
if (element === value) return this; if (element === value) return this;
elements.push(value); elements.push(value);
@@ -1677,6 +1735,7 @@ merge(Compressor.prototype, {
if (typeof Object.prototype[key] === 'function') { if (typeof Object.prototype[key] === 'function') {
return this; return this;
} }
if (prop.value instanceof AST_Function) continue;
val[key] = ev(prop.value, compressor); val[key] = ev(prop.value, compressor);
if (val[key] === prop.value) return this; if (val[key] === prop.value) return this;
} }
@@ -2135,18 +2194,22 @@ merge(Compressor.prototype, {
} }
def(AST_Node, return_false); def(AST_Node, return_false);
def(AST_Constant, return_true); def(AST_Constant, return_true);
def(AST_Function, function(){ def(AST_Lambda, function(scope){
var self = this; var self = this;
var result = true; var result = true;
self.walk(new TreeWalker(function(node) { self.walk(new TreeWalker(function(node) {
if (!result) return true; if (!result) return true;
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var def = node.definition(); var def = node.definition();
if (self.enclosed.indexOf(def) >= 0 if (member(def, self.enclosed)
&& self.variables.get(def.name) !== def) { && !self.variables.has(def.name)) {
if (scope) {
var scope_def = scope.find_variable(node);
if (def.undeclared ? !scope_def : scope_def === def) return true;
}
result = false; result = false;
return true;
} }
return true;
} }
})); }));
return result; return result;
@@ -2234,7 +2297,6 @@ merge(Compressor.prototype, {
if (self.uses_eval || self.uses_with) return; if (self.uses_eval || self.uses_with) return;
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs; var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars; var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
if (!drop_funcs && !drop_vars) return;
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node) { var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node) {
if (node instanceof AST_Assign && (node.write_only || node.operator == "=")) { if (node instanceof AST_Assign && (node.write_only || node.operator == "=")) {
return node.left; return node.left;
@@ -2257,58 +2319,63 @@ merge(Compressor.prototype, {
// this scope (not in nested scopes). // this scope (not in nested scopes).
var scope = this; var scope = this;
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node !== self) { if (node === self) return;
if (node instanceof AST_Defun) { if (node instanceof AST_Defun) {
if (!drop_funcs && scope === self) { if (!drop_funcs && scope === self) {
var node_def = node.name.definition(); 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)) { if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true; in_use_ids[node_def.id] = true;
in_use.push(node_def); in_use.push(node_def);
} }
} }
initializations.add(node.name.name, node); if (def.value) {
return true; // don't go in nested scopes initializations.add(def.name.name, def.value);
} if (def.value.has_side_effects(compressor)) {
if (node instanceof AST_Definitions && scope === self) { def.value.walk(tw);
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);
}
}
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; });
} return true;
if (node instanceof AST_Scope) { }
var save_scope = scope; var sym;
scope = node; if (scope === self
descend(); && (sym = assign_as_unused(node)) instanceof AST_SymbolRef
scope = save_scope; && self.variables.get(sym.name) === sym.definition()) {
return true; 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); self.walk(tw);
@@ -2363,13 +2430,16 @@ merge(Compressor.prototype, {
} }
} }
if (drop_funcs && node instanceof AST_Defun && node !== self) { 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)); 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 make_node(AST_EmptyStatement, node);
} }
return node; return node;
} }
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) { var parent = tt.parent();
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
// place uninitialized names at the start // place uninitialized names at the start
var body = [], head = [], tail = []; var body = [], head = [], tail = [];
// for unused names whose initialization has // for unused names whose initialization has
@@ -2379,13 +2449,13 @@ merge(Compressor.prototype, {
node.definitions.forEach(function(def) { node.definitions.forEach(function(def) {
if (def.value) def.value = def.value.transform(tt); if (def.value) def.value = def.value.transform(tt);
var sym = def.name.definition(); var sym = def.name.definition();
if (sym.id in in_use_ids) { if (!drop_vars || sym.id in in_use_ids) {
if (def.name instanceof AST_SymbolVar) { if (def.name instanceof AST_SymbolVar) {
var var_defs = var_defs_by_id.get(sym.id); var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && !def.value) { if (var_defs.length > 1 && !def.value) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name)); compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
remove(var_defs, def); remove(var_defs, def);
remove(sym.orig, def.name); drop_decl(sym);
return; return;
} }
} }
@@ -2418,7 +2488,7 @@ merge(Compressor.prototype, {
} else { } else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name)); 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) { if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
@@ -2427,7 +2497,7 @@ merge(Compressor.prototype, {
var def = tail.pop(); var def = tail.pop();
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name)); compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
remove(var_defs, def); remove(var_defs, def);
remove(def.name.definition().orig, def.name); drop_decl(def.name.definition());
side_effects.unshift(make_node(AST_Assign, def, { side_effects.unshift(make_node(AST_Assign, def, {
operator: "=", operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name), left: make_node(AST_SymbolRef, def.name, def.name),
@@ -2445,23 +2515,22 @@ merge(Compressor.prototype, {
})); }));
} }
switch (body.length) { switch (body.length) {
case 0: case 0:
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
case 1: case 1:
return body[0]; return body[0];
default: default:
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
body: body body: body
}); });
} }
} }
if (drop_vars) { if (drop_vars) {
var def = assign_as_unused(node); var sym = assign_as_unused(node);
if (def instanceof AST_SymbolRef if (sym instanceof AST_SymbolRef
&& !((def = def.definition()).id in in_use_ids) && !(sym.definition().id in in_use_ids)) {
&& self.variables.get(def.name) === def) {
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
return maintain_this_binding(tt.parent(), node, node.right.transform(tt)); return maintain_this_binding(parent, node, node.right.transform(tt));
} }
return make_node(AST_Number, node, { return make_node(AST_Number, node, {
value: 0 value: 0
@@ -2645,6 +2714,71 @@ merge(Compressor.prototype, {
return self; return self;
}); });
AST_Scope.DEFMETHOD("hoist_properties", function(compressor){
var self = this;
if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return self;
var defs_by_id = Object.create(null);
var var_names = Object.create(null);
self.enclosed.forEach(function(def) {
var_names[def.name] = true;
});
self.variables.each(function(def, name) {
var_names[name] = true;
});
return self.transform(new TreeTransformer(function(node) {
if (node instanceof AST_VarDef) {
var sym = node.name, def, value;
if (sym.scope === self
&& !(def = sym.definition()).escaped
&& !def.single_use
&& !def.direct_access
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
var defs = new Dictionary();
var assignments = [];
value.properties.forEach(function(prop) {
assignments.push(make_node(AST_VarDef, node, {
name: make_sym(prop.key),
value: prop.value
}));
});
defs_by_id[def.id] = defs;
return MAP.splice(assignments);
}
}
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var key = node.property;
if (key instanceof AST_Node) key = key.getValue();
var def = defs.get(key);
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function make_sym(key) {
var prefix = sym.name + "_" + key.toString().replace(/[^a-z_$]+/ig, "_");
var name = prefix;
for (var i = 0; var_names[name]; i++) name = prefix + "$" + i;
var new_var = make_node(sym.CTOR, sym, {
name: name,
scope: self
});
var def = self.def_variable(new_var);
defs.set(key, def);
self.enclosed.push(def);
var_names[name] = true;
return new_var;
}
}));
});
// drop_side_effect_free() // drop_side_effect_free()
// remove side-effect-free parts which only affects return value // remove side-effect-free parts which only affects return value
(function(def){ (function(def){
@@ -3164,7 +3298,7 @@ merge(Compressor.prototype, {
})); }));
if (reduce_vars) name.definition().fixed = false; if (reduce_vars) name.definition().fixed = false;
} }
remove(def.name.definition().orig, def.name); drop_decl(def.name.definition());
return a; return a;
}, []); }, []);
if (assignments.length == 0) return null; if (assignments.length == 0) return null;
@@ -3406,6 +3540,7 @@ merge(Compressor.prototype, {
&& !exp.uses_arguments && !exp.uses_arguments
&& !exp.uses_eval && !exp.uses_eval
&& exp.body.length == 1 && exp.body.length == 1
&& !exp.contains_this()
&& all(exp.argnames, function(arg) { && all(exp.argnames, function(arg) {
return arg.__unused; return arg.__unused;
}) })
@@ -3419,23 +3554,6 @@ merge(Compressor.prototype, {
expression: stat.body 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) { if (value) {
var args = self.args.concat(value); var args = self.args.concat(value);
return make_sequence(self, args).optimize(compressor); return make_sequence(self, args).optimize(compressor);
@@ -3719,6 +3837,11 @@ merge(Compressor.prototype, {
}); });
var commutativeOperators = makePredicate("== === != !== * & | ^"); var commutativeOperators = makePredicate("== === != !== * & | ^");
function is_object(node) {
return node instanceof AST_Array
|| node instanceof AST_Lambda
|| node instanceof AST_Object;
}
OPT(AST_Binary, function(self, compressor){ OPT(AST_Binary, function(self, compressor){
function reversible() { function reversible() {
@@ -3754,7 +3877,8 @@ merge(Compressor.prototype, {
case "!==": case "!==":
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) || if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
(self.left.is_number(compressor) && self.right.is_number(compressor)) || (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
(self.left.is_boolean() && self.right.is_boolean())) { (self.left.is_boolean() && self.right.is_boolean()) ||
self.left.equivalent_to(self.right)) {
self.operator = self.operator.substr(0, 2); self.operator = self.operator.substr(0, 2);
} }
// XXX: intentionally falling down to the next case // XXX: intentionally falling down to the next case
@@ -3774,6 +3898,13 @@ merge(Compressor.prototype, {
if (self.operator.length == 2) self.operator += "="; if (self.operator.length == 2) self.operator += "=";
} }
} }
// obj !== obj => false
else if (self.left instanceof AST_SymbolRef
&& self.right instanceof AST_SymbolRef
&& self.left.definition() === self.right.definition()
&& is_object(self.left.fixed_value())) {
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
}
break; break;
} }
if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) { if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
@@ -4110,49 +4241,64 @@ merge(Compressor.prototype, {
if (fixed instanceof AST_Defun) { if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed); d.fixed = fixed = make_node(AST_Function, fixed, fixed);
} }
if (compressor.option("unused") if (fixed && d.single_use) {
&& fixed var recurse;
&& d.references.length == 1 if (fixed instanceof AST_Function) {
&& (d.single_use || fixed instanceof AST_Function for (var i = 0; recurse = compressor.parent(i); i++) {
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) if (recurse instanceof AST_Lambda) {
&& !d.scope.uses_eval var name = recurse.name;
&& compressor.find_parent(AST_Scope) === fixed.parent_scope)) { if (name && name.definition() === d) break;
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 (d.should_replace) { if (!recurse) {
return d.should_replace(); var value = fixed.optimize(compressor);
return value === fixed ? fixed.clone(true) : value;
} }
} }
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 (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; return self;
@@ -4446,19 +4592,69 @@ merge(Compressor.prototype, {
}); });
OPT(AST_Sub, function(self, compressor){ OPT(AST_Sub, function(self, compressor){
var expr = self.expression;
var prop = self.property; var prop = self.property;
if (prop instanceof AST_String && compressor.option("properties")) { if (compressor.option("properties")) {
prop = prop.getValue(); var key = prop.evaluate(compressor);
if (is_identifier_string(prop)) { if (key !== prop) {
return make_node(AST_Dot, self, { var property = "" + key;
expression : self.expression, if (is_identifier_string(property)
property : prop && property.length <= prop.print_to_string().length + 1) {
}).optimize(compressor); return make_node(AST_Dot, self, {
expression: expr,
property: property
}).optimize(compressor);
}
if (!(prop instanceof AST_Number)) {
var value = parseFloat(property);
if (value.toString() == property) {
prop = self.property = make_node(AST_Number, prop, {
value: value
});
}
}
} }
var v = parseFloat(prop); }
if (!isNaN(v) && v.toString() == prop) { if (is_lhs(self, compressor.parent())) return self;
self.property = make_node(AST_Number, self.property, { if (key !== prop) {
value: v 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")
&& prop instanceof AST_Number && expr instanceof AST_Array) {
var index = prop.getValue();
var elements = expr.elements;
if (index in elements) {
var flatten = true;
var values = [];
for (var i = elements.length; --i > index;) {
var value = elements[i].drop_side_effect_free(compressor);
if (value) {
values.unshift(value);
if (flatten && value.has_side_effects(compressor)) flatten = false;
}
}
var retValue = elements[index];
retValue = retValue instanceof AST_Hole ? make_node(AST_Undefined, retValue) : retValue;
if (!flatten) values.unshift(retValue);
while (--i >= 0) {
var value = elements[i].drop_side_effect_free(compressor);
if (value) values.unshift(value);
else index--;
}
if (flatten) {
values.push(retValue);
return make_sequence(self, values).optimize(compressor);
} else return make_node(AST_Sub, self, {
expression: make_node(AST_Array, expr, {
elements: values
}),
property: make_node(AST_Number, prop, {
value: index
})
}); });
} }
} }
@@ -4470,7 +4666,8 @@ merge(Compressor.prototype, {
return self; 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 result;
var self = this; var self = this;
self.walk(new TreeWalker(function(node) { self.walk(new TreeWalker(function(node) {
@@ -4481,25 +4678,40 @@ merge(Compressor.prototype, {
return result; return result;
}); });
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;
for (var i = props.length; --i >= 0;) {
var prop = props[i];
if ("" + prop.key == key) {
if (!all(props, function(prop) {
return prop instanceof AST_ObjectKeyVal;
})) break;
var value = prop.value;
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) {
return prop.value;
})
}),
property: make_node(AST_Number, this, {
value: i
})
});
}
}
}
});
OPT(AST_Dot, function(self, compressor){ OPT(AST_Dot, function(self, compressor){
var def = self.resolve_defines(compressor); var def = self.resolve_defines(compressor);
if (def) { if (def) {
return def.optimize(compressor); return def.optimize(compressor);
} }
if (compressor.option("unsafe") && self.expression instanceof AST_Object) {
var values = self.expression.properties;
for (var i = values.length; --i >= 0;) {
if (values[i].key === self.property) {
var value = values[i].value;
if (value instanceof AST_Function ? !value.contains_this() : !value.has_side_effects(compressor)) {
var obj = self.expression.clone();
obj.properties = obj.properties.slice();
obj.properties.splice(i, 1);
return make_sequence(self, [ obj, value ]).optimize(compressor);
}
}
}
}
if (compressor.option("unsafe_proto") if (compressor.option("unsafe_proto")
&& self.expression instanceof AST_Dot && self.expression instanceof AST_Dot
&& self.expression.property == "prototype") { && self.expression.property == "prototype") {
@@ -4522,6 +4734,9 @@ merge(Compressor.prototype, {
break; break;
} }
} }
if (is_lhs(self, compressor.parent())) return self;
var sub = self.flatten_object(self.property, compressor);
if (sub) return sub.optimize(compressor);
var ev = self.evaluate(compressor); var ev = self.evaluate(compressor);
if (ev !== self) { if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor); ev = make_node_from_constant(ev, self).optimize(compressor);

View File

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

View File

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

View File

@@ -128,50 +128,110 @@ constant_join_3: {
for_loop: { for_loop: {
options = { options = {
unsafe : true, evaluate: true,
unused : true, reduce_vars: true,
evaluate : true, unsafe: true,
reduce_vars : true unused: true,
}; };
input: { input: {
function f0() { function f0() {
var a = [1, 2, 3]; var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) { var b = 0;
console.log(a[i]); for (var i = 0; i < a.length; i++)
} b += a[i];
return b;
} }
function f1() { function f1() {
var a = [1, 2, 3]; var a = [1, 2, 3];
for (var i = 0, len = a.length; i < len; i++) { var b = 0;
console.log(a[i]); for (var i = 0, len = a.length; i < len; i++)
} b += a[i];
return b;
} }
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
a[i]++;
}
}
}
expect: {
function f0() {
var a = [1, 2, 3];
for (var i = 0; i < 3; i++)
console.log(a[i]);
}
function f1() {
var a = [1, 2, 3];
for (var i = 0; i < 3; i++)
console.log(a[i]);
}
function f2() { function f2() {
var a = [1, 2, 3]; var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) for (var i = 0; i < a.length; i++)
a[i]++; a[i]++;
return a[2];
} }
console.log(f0(), f1(), f2());
} }
expect: {
function f0() {
var a = [1, 2, 3];
var b = 0;
for (var i = 0; i < 3; i++)
b += a[i];
return b;
}
function f1() {
var a = [1, 2, 3];
var b = 0;
for (var i = 0; i < 3; i++)
b += a[i];
return b;
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++)
a[i]++;
return a[2];
}
console.log(f0(), f1(), f2());
}
expect_stdout: "6 6 4"
}
index: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a[0], a[1]);
}
expect: {
console.log(1, 2);
}
expect_stdout: "1 2"
}
length: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a.length);
}
expect: {
console.log(2);
}
expect_stdout: "2"
}
index_length: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a[0], a.length);
}
expect: {
console.log(1, 2);
}
expect_stdout: "1 2"
} }

View File

@@ -1388,6 +1388,7 @@ issue_1605_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: false, toplevel: false,
unused: true,
} }
input: { input: {
function foo(x) { function foo(x) {
@@ -1410,6 +1411,7 @@ issue_1605_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: "vars", toplevel: "vars",
unused: true,
} }
input: { input: {
function foo(x) { function foo(x) {
@@ -1537,6 +1539,7 @@ issue_1631_3: {
var_side_effects_1: { var_side_effects_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var print = console.log.bind(console); var print = console.log.bind(console);
@@ -1559,6 +1562,7 @@ var_side_effects_1: {
var_side_effects_2: { var_side_effects_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var print = console.log.bind(console); var print = console.log.bind(console);
@@ -1584,6 +1588,7 @@ var_side_effects_3: {
collapse_vars: true, collapse_vars: true,
pure_getters: true, pure_getters: true,
unsafe: true, unsafe: true,
unused: true,
} }
input: { input: {
var print = console.log.bind(console); var print = console.log.bind(console);
@@ -1659,6 +1664,7 @@ iife_2: {
}(foo); }(foo);
} }
expect: { expect: {
var foo;
!function(x) { !function(x) {
console.log(x); console.log(x);
}(bar()); }(bar());
@@ -1945,6 +1951,7 @@ ref_scope: {
chained_1: { chained_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var a = 2; var a = 2;
@@ -1961,6 +1968,7 @@ chained_1: {
chained_2: { chained_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var a; var a;
@@ -2051,7 +2059,7 @@ inner_lvalues: {
console.log(null, a, b); console.log(null, a, b);
} }
expect: { expect: {
var a, b = 10; var b = 10;
var a = (--b || a || 3).toString(), c = --b + -a; var a = (--b || a || 3).toString(), c = --b + -a;
console.log(null, a, b); console.log(null, a, b);
} }
@@ -2061,6 +2069,7 @@ inner_lvalues: {
double_def: { double_def: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var a = x, a = a && y; var a = x, a = a && y;
@@ -2075,6 +2084,7 @@ double_def: {
toplevel_single_reference: { toplevel_single_reference: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var a; var a;
@@ -2084,9 +2094,10 @@ toplevel_single_reference: {
} }
} }
expect: { expect: {
var a; for (var b in x) {
for (var b in x) var a;
b(a = b); b(a = b);
}
} }
} }
@@ -2534,73 +2545,6 @@ issue_2319_3: {
expect_stdout: "true" expect_stdout: "true"
} }
prop_side_effects_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log({
bar: function() {
return 2;
}
}.bar());
}
expect_stdout: [
"1",
"2",
]
}
prop_side_effects_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log(2);
}
expect_stdout: [
"1",
"2",
]
}
issue_2365: { issue_2365: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -2956,6 +2900,7 @@ pure_getters_chain: {
options = { options = {
collapse_vars: true, collapse_vars: true,
pure_getters: true, pure_getters: true,
unused: true,
} }
input: { input: {
function o(t, r) { function o(t, r) {
@@ -2976,6 +2921,7 @@ pure_getters_chain: {
conditional_1: { conditional_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
function f(a, b) { function f(a, b) {
@@ -3000,6 +2946,7 @@ conditional_1: {
conditional_2: { conditional_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
function f(a, b) { function f(a, b) {
@@ -3016,3 +2963,117 @@ conditional_2: {
} }
expect_stdout: "5 5" 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,
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

@@ -73,4 +73,41 @@ dont_change_in_or_instanceof_expressions: {
1 instanceof 1; 1 instanceof 1;
null instanceof null; null instanceof null;
} }
} }
self_comparison_1: {
options = {
comparisons: true,
}
input: {
a === a;
a !== b;
b.c === a.c;
b.c !== b.c;
}
expect: {
a == a;
a !== b;
b.c === a.c;
b.c != b.c;
}
}
self_comparison_2: {
options = {
comparisons: true,
reduce_vars: true,
toplevel: true,
}
input: {
function f() {}
var o = {};
console.log(f != f, o === o);
}
expect: {
function f() {}
var o = {};
console.log(false, true);
}
expect_stdout: "false true"
}

View File

@@ -1109,11 +1109,11 @@ var_catch_toplevel: {
} }
} }
issue_2105: { issue_2105_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
inline: true, inline: true,
passes: 3, passes: 2,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
unused: true, unused: true,
@@ -1139,17 +1139,50 @@ issue_2105: {
}); });
} }
expect: { expect: {
(function() { ({
var quux = function() { prop: function() {
console.log;
console.log("PASS"); console.log("PASS");
}; }
return { }).prop();
prop: function() { }
console.log; expect_stdout: "PASS"
quux(); }
issue_2105_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
properties: true,
pure_getters: "strict",
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 };
} }
}; return bar;
})().prop(); } );
});
}
expect: {
console.log("PASS");
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }

View File

@@ -386,10 +386,11 @@ unsafe_object_accessor: {
} }
} }
unsafe_function: { prop_function: {
options = { options = {
evaluate : true, evaluate: true,
unsafe : true properties: true,
side_effects: true,
} }
input: { input: {
console.log( console.log(
@@ -402,9 +403,9 @@ unsafe_function: {
expect: { expect: {
console.log( console.log(
({a:{b:1},b:function(){}}) + 1, ({a:{b:1},b:function(){}}) + 1,
({b:function(){}}, {b:1}) + 1, ({b:1}) + 1,
({a:{b:1}}, function(){}) + 1, function(){} + 1,
({b:function(){}}, {b:1}).b + 1 2
); );
} }
expect_stdout: true expect_stdout: true
@@ -630,10 +631,11 @@ unsafe_string_bad_index: {
expect_stdout: true expect_stdout: true
} }
unsafe_prototype_function: { prototype_function: {
options = { options = {
evaluate : true, evaluate: true,
unsafe : true properties: true,
side_effects: true,
} }
input: { input: {
var a = ({valueOf: 0}) < 1; var a = ({valueOf: 0}) < 1;
@@ -652,8 +654,8 @@ unsafe_prototype_function: {
var d = ({toString: 0}) + ""; var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2]; var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2]; var f = (({toString: 0}) + "")[2];
var g = ({}, 0)(); var g = 0();
var h = ({}, 0)(); var h = 0();
} }
} }
@@ -1195,3 +1197,40 @@ issue_2231_2: {
} }
expect_stdout: true expect_stdout: true
} }
self_comparison_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var o = { n: NaN };
console.log(o.n == o.n, o.n === o.n, o.n != o.n, o.n !== o.n, typeof o.n);
}
expect: {
console.log(false, false, true, true, "number");
}
expect_stdout: "false false true true 'number'"
}
self_comparison_2: {
options = {
evaluate: true,
hoist_props: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { n: NaN };
console.log(o.n == o.n, o.n === o.n, o.n != o.n, o.n !== o.n, typeof o.n);
}
expect: {
console.log(false, false, true, true, "number");
}
expect_stdout: "false false true true 'number'"
}

View File

@@ -151,13 +151,13 @@ issue_1841_2: {
function_returning_constant_literal: { function_returning_constant_literal: {
options = { options = {
reduce_vars: true,
unsafe: true,
toplevel: true,
evaluate: true,
cascade: true,
unused: true,
inline: true, inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
} }
input: { input: {
function greeter() { function greeter() {
@@ -508,3 +508,41 @@ issue_2114_2: {
} }
expect_stdout: "2" expect_stdout: "2"
} }
issue_2428: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
pure_getters: "strict",
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

@@ -0,0 +1,399 @@
issue_2377_1: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
var obj_foo = 1, obj_cube = function(x) {
return x * x * x;
};
console.log(obj_foo, obj_cube(3));
}
expect_stdout: "1 27"
}
issue_2377_2: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
console.log(1, function(x) {
return x * x * x;
}(3));
}
expect_stdout: "1 27"
}
issue_2377_3: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
passes: 3,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
console.log(1, 27);
}
expect_stdout: "1 27"
}
direct_access_1: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
var obj = {
a: 1,
b: 2,
};
for (var k in obj) a++;
console.log(a, obj.a);
}
expect: {
var a = 0;
var obj = {
a: 1,
b: 2,
};
for (var k in obj) a++;
console.log(a, obj.a);
}
expect_stdout: "2 1"
}
direct_access_2: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { a: 1 };
var f = function(k) {
if (o[k]) return "PASS";
};
console.log(f("a"));
}
expect: {
var o = { a: 1 };
console.log(function(k) {
if (o[k]) return "PASS";
}("a"));
}
expect_stdout: "PASS"
}
direct_access_3: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { a: 1 };
o.b;
console.log(o.a);
}
expect: {
var o = { a: 1 };
o.b;
console.log(o.a);
}
expect_stdout: "1"
}
single_use: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
bar: function() {
return 42;
},
};
console.log(obj.bar());
}
expect: {
console.log({
bar: function() {
return 42;
},
}.bar());
}
}
name_collision_1: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var obj_foo = 1;
var obj_bar = 2;
function f() {
var obj = {
foo: 3,
bar: 4,
"b-r": 5,
"b+r": 6,
"b!r": 7,
};
console.log(obj_foo, obj.foo, obj.bar, obj["b-r"], obj["b+r"], obj["b!r"]);
}
f();
}
expect: {
var obj_foo = 1;
var obj_bar = 2;
function f() {
var obj_foo$0 = 3,
obj_bar = 4,
obj_b_r = 5,
obj_b_r$0 = 6,
obj_b_r$1 = 7;
console.log(obj_foo, obj_foo$0, obj_bar, obj_b_r, obj_b_r$0, obj_b_r$1);
}
f();
}
expect_stdout: "1 3 4 5 6 7"
}
name_collision_2: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
p: 1,
0: function(x) {
return x;
},
1: function(x) {
return x + 1;
}
}, o__$0 = 2, o__$1 = 3;
console.log(o.p === o.p, o[0](4), o[1](5), o__$0, o__$1);
}
expect: {
var o_p = 1,
o__ = function(x) {
return x;
},
o__$2 = function(x) {
return x + 1;
},
o__$0 = 2,
o__$1 = 3;
console.log(o_p === o_p, o__(4), o__$2(5), o__$0, o__$1);
}
expect_stdout: "true 4 6 2 3"
}
name_collision_3: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
p: 1,
0: function(x) {
return x;
},
1: function(x) {
return x + 1;
}
}, o__$0 = 2, o__$1 = 3;
console.log(o.p === o.p, o[0](4), o[1](5));
}
expect: {
var o_p = 1,
o__ = function(x) {
return x;
},
o__$2 = function(x) {
return x + 1;
},
o__$0 = 2,
o__$1 = 3;
console.log(o_p === o_p, o__(4), o__$2(5));
}
expect_stdout: "true 4 6"
}
contains_this_1: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p);
}
expect: {
console.log(1, 1);
}
expect_stdout: "1 1"
}
contains_this_2: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u);
}
expect: {
console.log(1, 1, function() {
return this === this;
});
}
expect_stdout: true
}
contains_this_3: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u());
}
expect: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u());
}
expect_stdout: "1 1 true"
}
new_this: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
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

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

View File

@@ -1,7 +1,8 @@
keep_properties: { keep_properties: {
options = { options = {
properties: false evaluate: true,
}; properties: false,
}
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
} }
@@ -12,6 +13,7 @@ keep_properties: {
dot_properties: { dot_properties: {
options = { options = {
evaluate: true,
properties: true, properties: true,
} }
beautify = { beautify = {
@@ -37,6 +39,7 @@ dot_properties: {
dot_properties_es5: { dot_properties_es5: {
options = { options = {
evaluate: true,
properties: true, properties: true,
} }
beautify = { beautify = {
@@ -61,8 +64,8 @@ dot_properties_es5: {
sub_properties: { sub_properties: {
options = { options = {
evaluate: true, evaluate: true,
properties: true properties: true,
}; }
input: { input: {
a[0] = 0; a[0] = 0;
a["0"] = 1; a["0"] = 1;
@@ -81,18 +84,18 @@ sub_properties: {
a[3.14] = 3; a[3.14] = 3;
a.if = 4; a.if = 4;
a["foo bar"] = 5; a["foo bar"] = 5;
a[NaN] = 6; a.NaN = 6;
a[null] = 7; a.null = 7;
a[void 0] = 8; a[void 0] = 8;
} }
} }
evaluate_array_length: { evaluate_array_length: {
options = { options = {
evaluate: true,
properties: true, properties: true,
unsafe: true, unsafe: true,
evaluate: true }
};
input: { input: {
a = [1, 2, 3].length; a = [1, 2, 3].length;
a = [1, 2, 3].join()["len" + "gth"]; a = [1, 2, 3].join()["len" + "gth"];
@@ -109,10 +112,10 @@ evaluate_array_length: {
evaluate_string_length: { evaluate_string_length: {
options = { options = {
evaluate: true,
properties: true, properties: true,
unsafe: true, unsafe: true,
evaluate: true }
};
input: { input: {
a = "foo".length; a = "foo".length;
a = ("foo" + "bar")["len" + "gth"]; a = ("foo" + "bar")["len" + "gth"];
@@ -151,7 +154,8 @@ mangle_properties: {
mangle_unquoted_properties: { mangle_unquoted_properties: {
options = { options = {
properties: false evaluate: true,
properties: false,
} }
mangle = { mangle = {
properties: { properties: {
@@ -249,7 +253,8 @@ mangle_debug_suffix: {
mangle_debug_suffix_keep_quoted: { mangle_debug_suffix_keep_quoted: {
options = { options = {
properties: false evaluate: true,
properties: false,
} }
mangle = { mangle = {
properties: { properties: {
@@ -677,8 +682,8 @@ accessor_this: {
issue_2208_1: { issue_2208_1: {
options = { options = {
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true,
} }
input: { input: {
console.log({ console.log({
@@ -696,8 +701,8 @@ issue_2208_1: {
issue_2208_2: { issue_2208_2: {
options = { options = {
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true,
} }
input: { input: {
console.log({ console.log({
@@ -721,8 +726,8 @@ issue_2208_2: {
issue_2208_3: { issue_2208_3: {
options = { options = {
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true,
} }
input: { input: {
a = 42; a = 42;
@@ -746,8 +751,8 @@ issue_2208_3: {
issue_2208_4: { issue_2208_4: {
options = { options = {
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true,
} }
input: { input: {
function foo() {} function foo() {}
@@ -770,8 +775,8 @@ issue_2208_4: {
issue_2208_5: { issue_2208_5: {
options = { options = {
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true,
} }
input: { input: {
console.log({ console.log({
@@ -804,3 +809,219 @@ issue_2256: {
g.keep = g.g; g.keep = g.g;
} }
} }
lhs_prop_1: {
options = {
evaluate: true,
properties: true,
}
input: {
console.log(++{
a: 1
}.a);
}
expect: {
console.log(++{
a: 1
}.a);
}
expect_stdout: "2"
}
lhs_prop_2: {
options = {
evaluate: true,
inline: true,
properties: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
[1][0] = 42;
(function(a) {
a.b = "g";
})("abc");
(function(a) {
a[2] = "g";
})("def");
(function(a) {
a[""] = "g";
})("ghi");
}
expect: {
[1][0] = 42;
"abc".b = "g";
"def"[2] = "g";
"ghi"[""] = "g";
}
}
literal_duplicate_key_side_effects: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log({
a: "FAIL",
a: console.log ? "PASS" : "FAIL"
}.a);
}
expect: {
console.log(console.log ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
prop_side_effects_1: {
options = {
evaluate: true,
inline: true,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
var obj = {
bar: function() {
return 2;
}
};
console.log(obj.bar());
}
expect_stdout: [
"1",
"2",
]
}
prop_side_effects_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
"": function() {
return C + C;
}
};
console.log(obj[""]());
}
expect: {
console.log(1);
console.log(2);
}
expect_stdout: [
"1",
"2",
]
}
accessor_1: {
options = {
properties: true,
}
input: {
console.log({
a: "FAIL",
get a() {
return "PASS";
}
}.a);
}
expect: {
console.log({
a: "FAIL",
get a() {
return "PASS";
}
}.a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
accessor_2: {
options = {
properties: true,
}
input: {
console.log({
get a() {
return "PASS";
},
set a(v) {},
a: "FAIL"
}.a);
}
expect: {
console.log({
get a() {
return "PASS";
},
set a(v) {},
a: "FAIL"
}.a);
}
expect_stdout: true
}
array_hole: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log(
[ 1, 2, , 3][1],
[ 1, 2, , 3][2],
[ 1, 2, , 3][3]
);
}
expect: {
console.log(2, void 0, 3);
}
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

@@ -211,7 +211,116 @@ unsafe_evaluate: {
} }
} }
unsafe_evaluate_object: { unsafe_evaluate_side_effect_free_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.p); return o; }());
console.log(function(){ var o={p:3}; console.log([o][0].p); return o.p; }());
}
expect: {
console.log(function(){ console.log(1); return 1; }());
console.log(function(){ var o={p:2}; console.log(2); return o; }());
console.log(function(){ console.log(3); return 3; }());
}
expect_stdout: true
}
unsafe_evaluate_side_effect_free_2: {
options = {
collapse_vars: true,
evaluate: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1},a=[o]; console.log(a[0].p); return o.p; }());
}
expect: {
console.log(function(){ console.log(1); return 1; }());
}
expect_stdout: true
}
unsafe_evaluate_escaped: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1}; console.log(o, o.p); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.p, o); return o.p; }());
console.log(function(){ var o={p:3},a=[o]; console.log(a[0].p++); return o.p; }());
}
expect: {
console.log(function(){ var o={p:1}; console.log(o, o.p); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.p, o); return o.p; }());
console.log(function(){ var o={p:3},a=[o]; console.log(a[0].p++); return o.p; }());
}
expect_stdout: true
}
unsafe_evaluate_modified: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:4}; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }());
function inc() { this.p++; }
console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }());
console.log(function(){ var o={p:7}; console.log([o][0].p++); return o.p; }());
}
expect: {
console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:4}; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }());
function inc() { this.p++; }
console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }());
console.log(function(){ var o={p:7}; console.log([o][0].p++); return o.p; }());
}
expect_stdout: true
}
unsafe_evaluate_unknown: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1}; console.log(o.not_present); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.prototype); return o.p; }());
console.log(function(){ var o={p:3}; console.log(o.hasOwnProperty); return o.p; }());
}
expect: {
console.log(function(){ var o={p:1}; console.log(o.not_present); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.prototype); return o.p; }());
console.log(function(){ var o={p:3}; console.log(o.hasOwnProperty); return o.p; }());
}
expect_stdout: true
}
unsafe_evaluate_object_1: {
options = { options = {
evaluate : true, evaluate : true,
reduce_vars : true, reduce_vars : true,
@@ -251,7 +360,83 @@ unsafe_evaluate_object: {
} }
} }
unsafe_evaluate_array: { unsafe_evaluate_object_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.bar, obj.square(2), obj.cube);
}
expect: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(1, 2, obj.square(2), obj.cube);
}
expect_stdout: true
}
unsafe_evaluate_object_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var obj = {
get foo() {
return 1;
},
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.bar, obj.square(2), obj.cube);
}
expect: {
var obj = {
get foo() {
return 1;
},
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.bar, obj.square(2), obj.cube);
}
expect_stdout: true
}
unsafe_evaluate_array_1: {
options = { options = {
evaluate : true, evaluate : true,
reduce_vars : true, reduce_vars : true,
@@ -299,6 +484,132 @@ unsafe_evaluate_array: {
} }
} }
unsafe_evaluate_array_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var arr = [
1,
2,
function(x) {
return x * x;
},
function(x) {
return x * x * x;
},
];
console.log(arr[0], arr[1], arr[2](2), arr[3]);
}
expect: {
var arr = [
1,
2,
function(x) {
return x * x;
},
function(x) {
return x * x * x;
},
];
console.log(1, 2, arr[2](2), arr[3]);
}
expect_stdout: true
}
unsafe_evaluate_array_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var arr = [
1,
2,
function() {
return ++arr[0];
},
];
console.log(arr[0], arr[1], arr[2](), arr[0]);
}
expect: {
var arr = [
1,
2,
function() {
return ++arr[0];
},
];
console.log(arr[0], arr[1], arr[2](), arr[0]);
}
expect_stdout: "1 2 2 2"
}
unsafe_evaluate_array_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var arr = [
1,
2,
function() {
return ++this[0];
},
];
console.log(arr[0], arr[1], arr[2], arr[0]);
}
expect: {
var arr = [
1,
2,
function() {
return ++this[0];
},
];
console.log(1, 2, arr[2], 1);
}
expect_stdout: true
}
unsafe_evaluate_array_5: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var arr = [
1,
2,
function() {
return ++this[0];
},
];
console.log(arr[0], arr[1], arr[2](), arr[0]);
}
expect: {
var arr = [
1,
2,
function() {
return ++this[0];
},
];
console.log(arr[0], arr[1], arr[2](), arr[0]);
}
expect_stdout: "1 2 2 2"
}
unsafe_evaluate_equality_1: { unsafe_evaluate_equality_1: {
options = { options = {
evaluate : true, evaluate : true,
@@ -1820,14 +2131,13 @@ redefine_farg_1: {
} }
expect: { expect: {
function f(a) { function f(a) {
var a;
return typeof a; return typeof a;
} }
function g() { function g() {
return"number"; return "number";
} }
function h(a, b) { function h(a, b) {
var a = b; a = b;
return typeof a; return typeof a;
} }
console.log(f([]), g([]), h([])); console.log(f([]), g([]), h([]));
@@ -1862,10 +2172,9 @@ redefine_farg_2: {
} }
expect: { expect: {
console.log(function(a) { console.log(function(a) {
var a;
return typeof a; return typeof a;
}([]), "number",function(a, b) { }([]), "number",function(a, b) {
var a = b; a = b;
return typeof a; return typeof a;
}([])); }([]));
} }
@@ -1874,11 +2183,13 @@ redefine_farg_2: {
redefine_farg_3: { redefine_farg_3: {
options = { options = {
cascade: true,
evaluate: true, evaluate: true,
inline: true, inline: true,
keep_fargs: false, keep_fargs: false,
passes: 3, passes: 2,
reduce_vars: true, reduce_vars: true,
sequences: true,
side_effects: true, side_effects: true,
toplevel: true, toplevel: true,
unused: true, unused: true,
@@ -2660,6 +2971,7 @@ obj_var_2: {
evaluate: true, evaluate: true,
inline: true, inline: true,
passes: 2, passes: 2,
properties: true,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
toplevel: true, toplevel: true,
@@ -2716,10 +3028,10 @@ obj_arg_2: {
evaluate: true, evaluate: true,
inline: true, inline: true,
passes: 2, passes: 2,
properties: true,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
toplevel: true, toplevel: true,
unsafe: true,
unused: true, unused: true,
} }
input: { input: {
@@ -2954,3 +3266,582 @@ const_expr_2: {
} }
expect_stdout: "2 2" expect_stdout: "2 2"
} }
escaped_prop: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var obj = { o: { a: 1 } };
(function(o) {
o.a++;
})(obj.o);
(function(o) {
console.log(o.a);
})(obj.o);
}
expect: {
var obj = { o: { a: 1 } };
obj.o.a++;
console.log(obj.o.a);
}
expect_stdout: "2"
}
issue_2420_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function run() {
var self = this;
if (self.count++)
self.foo();
else
self.bar();
}
var o = {
count: 0,
foo: function() { console.log("foo"); },
bar: function() { console.log("bar"); },
};
run.call(o);
run.call(o);
}
expect: {
function run() {
if (this.count++)
this.foo();
else
this.bar();
}
var o = {
count: 0,
foo: function() { console.log("foo"); },
bar: function() { console.log("bar"); },
};
run.call(o);
run.call(o);
}
expect_stdout: [
"bar",
"foo",
]
}
issue_2420_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function f() {
var that = this;
if (that.bar)
that.foo();
else
!function(that, self) {
console.log(this === that, self === this, that === self);
}(that, this);
}
f.call({
bar: 1,
foo: function() { console.log("foo", this.bar); },
});
f.call({});
}
expect: {
function f() {
if (this.bar)
this.foo();
else
!function(that, self) {
console.log(this === that, self === this, that === self);
}(this, this);
}
f.call({
bar: 1,
foo: function() { console.log("foo", this.bar); },
});
f.call({});
}
expect_stdout: [
"foo 1",
"false false true",
]
}
issue_2423_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
p();
}
expect: {
function p() { console.log(function() { return 1; }()); }
p();
p();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_2: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
p();
}
expect: {
function p() { console.log(1); }
p();
p();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
}
expect: {
(function() { console.log(function() { return 1; }()); })();
}
expect_stdout: "1"
}
issue_2423_4: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
}
expect: {
console.log(1);
}
expect_stdout: "1"
}
issue_2423_5: {
options = {
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function x() {
y();
}
function y() {
console.log(1);
}
function z() {
function y() {
console.log(2);
}
x();
}
z();
z();
}
expect: {
function z() {
console.log(1);
}
z();
z();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_6: {
options = {
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function x() {
y();
}
function y() {
console.log(1);
}
function z() {
function y() {
console.log(2);
}
x();
y();
}
z();
z();
}
expect: {
function z(){
console.log(1);
console.log(2);
}
z();
z();
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2440_eval_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
}
expect: {
function foo() {
return bar();
}
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
}
}
issue_2440_eval_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
function foo() {
return bar();
}
}
expect: {
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
function foo() {
return bar();
}
}
}
issue_2440_with_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
baz = {
quux: foo
};
with (o) whatever();
}
expect: {
function foo() {
return bar();
}
baz = {
quux: foo
};
with (o) whatever();
}
}
issue_2440_with_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
baz = {
quux: foo
};
with (o) whatever();
function foo() {
return bar();
}
}
expect: {
baz = {
quux: foo
};
with (o) whatever();
function foo() {
return bar();
}
}
}
issue_2442: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
foo();
}
}
expect: {}
}
recursive_inlining_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo() { bar(); }
function bar() { foo(); }
console.log("PASS");
}();
}
expect: {
!function() {
console.log("PASS");
}();
}
expect_stdout: "PASS"
}
recursive_inlining_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo() { qux(); }
function bar() { foo(); }
function qux() { bar(); }
console.log("PASS");
}();
}
expect: {
!function() {
console.log("PASS");
}();
}
expect_stdout: "PASS"
}
recursive_inlining_3: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
}();
}
expect: {
!function() {
function qux(x) {
console.log("qux", x);
if (x) (function(x) {
console.log("foo", x);
if (x) (function(x) {
console.log("bar", x);
if (x) qux(x - 1);
})(x - 1);
})(x - 1);
}
qux(4);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}
recursive_inlining_4: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
bar(5);
}();
}
expect: {
!function() {
function bar(x) {
console.log("bar", x);
if (x) qux(x - 1);
}
function qux(x) {
console.log("qux", x);
if (x) (function(x) {
console.log("foo", x);
if (x) bar(x - 1);
})(x - 1);
}
qux(4);
bar(5);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"bar 5",
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}
recursive_inlining_5: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
bar(5);
foo(3);
}();
}
expect: {
!function() {
function foo(x) {
console.log("foo", x);
if (x) bar(x - 1);
}
function bar(x) {
console.log("bar", x);
if (x) qux(x - 1);
}
function qux(x) {
console.log("qux", x);
if (x) foo(x - 1);
}
qux(4);
bar(5);
foo(3);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"bar 5",
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}

View File

@@ -162,6 +162,7 @@ var VALUES = [
'"object"', '"object"',
'"number"', '"number"',
'"function"', '"function"',
'this',
]; ];
var BINARY_OPS_NO_COMMA = [ var BINARY_OPS_NO_COMMA = [
@@ -349,10 +350,10 @@ function createParams() {
return params.join(', '); return params.join(', ');
} }
function createArgs() { function createArgs(recurmax, stmtDepth, canThrow) {
var args = []; var args = [];
for (var n = rng(4); --n >= 0;) { 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(', '); return args.join(', ');
} }
@@ -390,9 +391,10 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
VAR_NAMES.length = namesLenBefore; 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) // 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; return s;
} }
@@ -626,6 +628,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
case p++: case p++:
return createValue(); return createValue();
case p++:
case p++:
return getVarName();
case p++: case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: case p++:

View File

@@ -16,11 +16,9 @@
{}, {},
{ {
"compress": { "compress": {
"toplevel": true "hoist_props": true
}, },
"mangle": { "toplevel": true
"toplevel": true
}
}, },
{ {
"compress": { "compress": {