Compare commits

...

25 Commits

Author SHA1 Message Date
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
Alex Lam S.L
96439ca246 v3.1.5 2017-10-22 00:27:26 +08:00
Alex Lam S.L
c927cea632 unsafe fix-ups for #2351 (#2379) 2017-10-21 04:08:26 +08:00
Alex Lam S.L
9f4b98f8e4 backport #2374 (#2376) 2017-10-19 23:02:27 +08:00
Alex Lam S.L
0f2ef3367c enhance collapse_vars around lazy operations (#2369) 2017-10-19 04:52:00 +08:00
Alex Lam S.L
7e5b5cac97 fix AST_PropAccess in collapse_vars (take 3) (#2375)
Suppress scanning beyond assignment to `a.b`
2017-10-18 02:54:51 +08:00
Alex Lam S.L
c1346e06b7 clean up lazy operator detection (#2373) 2017-10-17 23:25:45 +08:00
Alex Lam S.L
0d2fe8e3ef fix AST_PropAccess in collapse_vars (take 2) (#2372)
fixes #2364
2017-10-17 22:59:15 +08:00
Alex Lam S.L
f2b9c11e2a fix AST_PropAccess in collapse_vars (#2370)
fixes #2364
2017-10-17 18:33:03 +08:00
Alex Lam S.L
fe647b083e account for side-effects from AST_This in collapse_vars (#2365) 2017-10-17 01:18:55 +08:00
12 changed files with 1878 additions and 256 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
``` ```
@@ -515,7 +515,6 @@ if (result.error) throw result.error;
```javascript ```javascript
{ {
warnings: false,
parse: { parse: {
// parse options // parse options
}, },
@@ -538,6 +537,7 @@ if (result.error) throw result.error;
nameCache: null, // or specify a name cache object nameCache: null, // or specify a name cache object
toplevel: false, toplevel: false,
ie8: false, ie8: false,
warnings: false,
} }
``` ```
@@ -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)
@@ -645,6 +651,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
compressor from discarding unused function arguments. You need this compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`. for code which relies on `Function.length`.
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `keep_infinity` (default: `false`) -- default `false`. Pass `true` to prevent `Infinity` from - `keep_infinity` (default: `false`) -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome. being compressed into `1/0`, which may cause performance issues on Chrome.
@@ -709,6 +719,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and `foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues. earlier versions due to known issues.
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: `false`) -- Reverse `<` and `<=` to `>` and `>=` to - `unsafe_comps` (default: `false`) -- Reverse `<` and `<=` to `>` and `>=` to
allow improved compression. This might be unsafe when an at least one of two allow improved compression. This might be unsafe when an at least one of two
operands is an object with computed values due the use of methods like `get`, operands is an object with computed values due the use of methods like `get`,
@@ -716,8 +728,6 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
comparison are switching. Compression only works if both `comparisons` and comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true. `unsafe_comps` are both set to true.
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `unsafe_Func` (default: `false`) -- compress and mangle `Function(args, code)` - `unsafe_Func` (default: `false`) -- compress and mangle `Function(args, code)`
when both `args` and `code` are string literals. when both `args` and `code` are string literals.
@@ -738,15 +748,19 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Mangle options ## Mangle options
- `eval` (default `false`). Pass `true` to mangle names visible in scopes
where `eval` or `with` are used.
- `keep_fnames` (default `false`). Pass `true` to not mangle function names.
Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
[compress option](#compress-options).
- `reserved` (default `[]`). Pass an array of identifiers that should be - `reserved` (default `[]`). Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`. excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` (default `false`). Pass `true` to mangle names declared in the - `toplevel` (default `false`). Pass `true` to mangle names declared in the
top level scope. top level scope.
- `eval` (default `false`). Pass `true` to mangle names visible in scopes
where `eval` or `with` are used.
Examples: Examples:
```javascript ```javascript
@@ -1052,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,
@@ -151,7 +152,7 @@ merge(Compressor.prototype, {
var last_count = 1 / 0; var last_count = 1 / 0;
for (var pass = 0; pass < passes; pass++) { for (var pass = 0; pass < passes; pass++) {
if (pass > 0 || this.option("reduce_vars")) if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this, true); node.reset_opt_flags(this);
node = node.transform(this); node = node.transform(this);
if (passes > 1) { if (passes > 1) {
var count = 0; var count = 0;
@@ -190,6 +191,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,8 +285,9 @@ merge(Compressor.prototype, {
self.transform(tt); self.transform(tt);
}); });
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan) { AST_Node.DEFMETHOD("reset_opt_flags", function(compressor) {
var reduce_vars = rescan && compressor.option("reduce_vars"); 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 // Stack of look-up tables to keep track of whether a `SymbolDef` has been
// properly assigned before use: // properly assigned before use:
// - `push()` & `pop()` when visiting conditional branches // - `push()` & `pop()` when visiting conditional branches
@@ -308,16 +311,25 @@ merge(Compressor.prototype, {
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var d = node.definition(); var d = node.definition();
d.references.push(node); d.references.push(node);
if (d.fixed === undefined || !safe_to_read(d) if (d.fixed === undefined || !safe_to_read(d) || d.single_use == "m") {
|| is_modified(node, 0, is_immutable(node))) {
d.fixed = false; 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();
}
if (is_modified(node, value, 0, is_immutable(value))) {
if (d.single_use) {
d.single_use = "m";
} else { } else {
var parent = tw.parent(); d.fixed = false;
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right }
|| parent instanceof AST_Call && node !== parent.expression } else {
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope mark_escaped(d, node, value, 0);
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped = true;
} }
} }
} }
@@ -404,8 +416,7 @@ merge(Compressor.prototype, {
pop(); pop();
return true; return true;
} }
if (node instanceof AST_Binary if (node instanceof AST_Binary && lazy_op(node.operator)) {
&& (node.operator == "&&" || node.operator == "||")) {
node.left.walk(tw); node.left.walk(tw);
push(); push();
node.right.walk(tw); node.right.walk(tw);
@@ -538,6 +549,7 @@ 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.fixed = false; def.fixed = false;
@@ -551,34 +563,65 @@ merge(Compressor.prototype, {
def.single_use = undefined; def.single_use = undefined;
} }
function is_immutable(node) { function is_immutable(value) {
var value = node.fixed_value(); return value && (value.is_constant() || value instanceof AST_Lambda);
if (!value) return false;
if (value.is_constant()) return true;
if (compressor.option("unused")) {
var d = node.definition();
if (d.single_use === undefined) {
d.single_use = loop_ids[d.id] === in_loop
&& d.scope === node.scope
&& value.is_constant_expression();
}
if (d.references.length == 1 && d.single_use) return true;
}
return value instanceof AST_Lambda;
} }
function is_modified(node, level, immutable) { 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())) {
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();
@@ -779,6 +822,7 @@ merge(Compressor.prototype, {
// Locate symbols which may execute code outside of scanning range // Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate); var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false; if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
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;
@@ -838,17 +882,18 @@ merge(Compressor.prototype, {
var sym; var sym;
if (node instanceof AST_Call if (node instanceof AST_Call
|| node instanceof AST_Exit || node instanceof AST_Exit
|| node instanceof AST_PropAccess && node.has_side_effects(compressor) || node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef || node instanceof AST_SymbolRef
&& (lvalues[node.name] && (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition())) || side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node)) && get_symbol(sym).name in lvalues || (sym = lhs_or_def(node))
|| parent instanceof AST_Binary && (sym instanceof AST_PropAccess || sym.name in lvalues)
&& (parent.operator == "&&" || parent.operator == "||") || (side_effects || !one_off)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Case || parent instanceof AST_Case
|| parent instanceof AST_Conditional || parent instanceof AST_Conditional
|| parent instanceof AST_For || parent instanceof AST_If)) {
|| parent instanceof AST_If) {
if (!(node instanceof AST_Scope)) descend(node, tt); if (!(node instanceof AST_Scope)) descend(node, tt);
abort = true; abort = true;
return node; return node;
@@ -934,28 +979,15 @@ merge(Compressor.prototype, {
} }
} }
function get_symbol(node) {
while (node instanceof AST_PropAccess) node = node.expression;
return node;
}
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;
var scope;
var tw = new TreeWalker(function(node, descend) { var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Scope) { var sym = node;
var save_scope = scope; while (sym instanceof AST_PropAccess) sym = sym.expression;
descend(); if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
scope = save_scope;
return true;
}
if (node instanceof AST_SymbolRef || node instanceof AST_PropAccess) {
var sym = get_symbol(node);
if (sym instanceof AST_SymbolRef) {
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); expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw);
return lvalues; return lvalues;
@@ -1431,9 +1463,10 @@ merge(Compressor.prototype, {
return member(this.operator, unary_bool); return member(this.operator, unary_bool);
}); });
def(AST_Binary, function(){ def(AST_Binary, function(){
return member(this.operator, binary_bool) || return member(this.operator, binary_bool)
( (this.operator == "&&" || this.operator == "||") && || lazy_op(this.operator)
this.left.is_boolean() && this.right.is_boolean() ); && this.left.is_boolean()
&& this.right.is_boolean();
}); });
def(AST_Conditional, function(){ def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean(); return this.consequent.is_boolean() && this.alternative.is_boolean();
@@ -1502,6 +1535,7 @@ merge(Compressor.prototype, {
node.DEFMETHOD("is_string", func); node.DEFMETHOD("is_string", func);
}); });
var lazy_op = makePredicate("&& ||");
var unary_side_effects = makePredicate("delete ++ --"); var unary_side_effects = makePredicate("delete ++ --");
function is_lhs(node, parent) { function is_lhs(node, parent) {
@@ -1613,35 +1647,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));
}); });
@@ -1660,6 +1665,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);
@@ -1683,6 +1689,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;
} }
@@ -2240,7 +2247,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;
@@ -2375,7 +2381,8 @@ merge(Compressor.prototype, {
} }
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
@@ -2385,7 +2392,7 @@ 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) {
@@ -2467,7 +2474,7 @@ merge(Compressor.prototype, {
&& !((def = def.definition()).id in in_use_ids) && !((def = def.definition()).id in in_use_ids)
&& self.variables.get(def.name) === def) { && 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
@@ -2651,6 +2658,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){
@@ -2698,14 +2770,12 @@ merge(Compressor.prototype, {
def(AST_Binary, function(compressor, first_in_statement){ def(AST_Binary, function(compressor, first_in_statement){
var right = this.right.drop_side_effect_free(compressor); var right = this.right.drop_side_effect_free(compressor);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement); if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
switch (this.operator) { if (lazy_op(this.operator)) {
case "&&":
case "||":
if (right === this.right) return this; if (right === this.right) return this;
var node = this.clone(); var node = this.clone();
node.right = right; node.right = right;
return node; return node;
default: } else {
var left = this.left.drop_side_effect_free(compressor, first_in_statement); var left = this.left.drop_side_effect_free(compressor, first_in_statement);
if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement); if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement);
return make_sequence(this, [ left, right ]); return make_sequence(this, [ left, right ]);
@@ -3571,7 +3641,7 @@ merge(Compressor.prototype, {
} }
if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) { if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
if (cdr.left.is_constant()) { if (cdr.left.is_constant()) {
if (cdr.operator == "||" || cdr.operator == "&&") { if (lazy_op(cdr.operator)) {
expressions[++i] = expressions[j]; expressions[++i] = expressions[j];
break; break;
} }
@@ -3727,6 +3797,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() {
@@ -3762,7 +3837,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
@@ -3782,6 +3858,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()) {
@@ -4071,8 +4154,7 @@ merge(Compressor.prototype, {
// "x" + (y + "z")==> "x" + y + "z" // "x" + (y + "z")==> "x" + y + "z"
if (self.right instanceof AST_Binary if (self.right instanceof AST_Binary
&& self.right.operator == self.operator && self.right.operator == self.operator
&& (self.operator == "&&" && (lazy_op(self.operator)
|| self.operator == "||"
|| (self.operator == "+" || (self.operator == "+"
&& (self.right.left.is_string(compressor) && (self.right.left.is_string(compressor)
|| (self.left.is_string(compressor) || (self.left.is_string(compressor)
@@ -4120,6 +4202,7 @@ merge(Compressor.prototype, {
d.fixed = fixed = make_node(AST_Function, fixed, fixed); d.fixed = fixed = make_node(AST_Function, fixed, fixed);
} }
if (compressor.option("unused") if (compressor.option("unused")
&& fixed
&& d.references.length == 1 && d.references.length == 1
&& (d.single_use || fixed instanceof AST_Function && (d.single_use || fixed instanceof AST_Function
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
@@ -4454,19 +4537,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) {
var property = "" + key;
if (is_identifier_string(property)
&& property.length <= prop.print_to_string().length + 1) {
return make_node(AST_Dot, self, { return make_node(AST_Dot, self, {
expression : self.expression, expression: expr,
property : prop property: property
}).optimize(compressor); }).optimize(compressor);
} }
var v = parseFloat(prop); if (!(prop instanceof AST_Number)) {
if (!isNaN(v) && v.toString() == prop) { var value = parseFloat(property);
self.property = make_node(AST_Number, self.property, { if (value.toString() == property) {
value: v prop = self.property = make_node(AST_Number, prop, {
value: value
});
}
}
}
}
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 (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
})
}); });
} }
} }
@@ -4489,25 +4622,38 @@ merge(Compressor.prototype, {
return result; return result;
}); });
AST_PropAccess.DEFMETHOD("flatten_object", function(key) {
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()) 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") {
@@ -4530,6 +4676,11 @@ merge(Compressor.prototype, {
break; break;
} }
} }
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 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

@@ -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.4", "version": "3.1.6",
"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 = {
evaluate: true,
reduce_vars: true,
unsafe: true, unsafe: true,
unused: true, unused: true,
evaluate : true,
reduce_vars : 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

@@ -2051,7 +2051,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);
} }
@@ -2534,69 +2534,418 @@ issue_2319_3: {
expect_stdout: "true" expect_stdout: "true"
} }
prop_side_effects_1: { issue_2365: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true, pure_getters: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
} }
input: { input: {
var C = 1; console.log(function(a) {
console.log(C); var b = a.f;
var obj = { a.f++;
bar: function() { return b;
return C + C; }({ f: 1 }));
console.log(function() {
var a = { f: 1 }, b = a.f;
a.f++;
return b;
}());
console.log({
f: 1,
g: function() {
var b = this.f;
this.f++;
return b;
} }
}; }.g());
console.log(obj.bar());
} }
expect: { expect: {
console.log(1); console.log(function(a) {
var b = a.f;
a.f++;
return b;
}({ f: 1 }));
console.log(function() {
var a = { f: 1 }, b = a.f;
a.f++;
return b;
}());
console.log({ console.log({
bar: function() { f: 1,
return 2; g: function() {
var b = this.f;
this.f++;
return b;
} }
}.bar()); }.g());
} }
expect_stdout: [ expect_stdout: [
"1", "1",
"2", "1",
"1",
] ]
} }
prop_side_effects_2: { issue_2364_1: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function inc(obj) {
return obj.count++;
}
function foo() {
var first = arguments[0];
var result = inc(first);
return foo.amount = first.count, result;
}
var data = {
count: 0,
};
var answer = foo(data);
console.log(foo.amount, answer);
}
expect: {
function inc(obj) {
return obj.count++;
}
function foo() {
var first = arguments[0];
var result = inc(first);
return foo.amount = first.count, result;
}
var data = {
count: 0
};
var answer = foo(data);
console.log(foo.amount, answer);
}
expect_stdout: "1 0"
}
issue_2364_2: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function callValidate() {
var validate = compilation.validate;
var result = validate.apply(null, arguments);
return callValidate.errors = validate.errors, result;
}
}
expect: {
function callValidate() {
var validate = compilation.validate;
var result = validate.apply(null, arguments);
return callValidate.errors = validate.errors, result;
}
}
}
issue_2364_3: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function inc(obj) {
return obj.count++;
}
function foo(bar) {
var result = inc(bar);
return foo.amount = bar.count, result;
}
var data = {
count: 0,
};
var answer = foo(data);
console.log(foo.amount, answer);
}
expect: {
function inc(obj) {
return obj.count++;
}
function foo(bar) {
var result = inc(bar);
return foo.amount = bar.count, result;
}
var data = {
count: 0,
};
var answer = foo(data);
console.log(foo.amount, answer);
}
expect_stdout: "1 0"
}
issue_2364_4: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function inc(obj) {
return obj.count++;
}
function foo(bar, baz) {
var result = inc(bar);
return foo.amount = baz.count, result;
}
var data = {
count: 0,
};
var answer = foo(data, data);
console.log(foo.amount, answer);
}
expect: {
function inc(obj) {
return obj.count++;
}
function foo(bar, baz) {
var result = inc(bar);
return foo.amount = baz.count, result;
}
var data = {
count: 0,
};
var answer = foo(data, data);
console.log(foo.amount, answer);
}
expect_stdout: "1 0"
}
issue_2364_5: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true, evaluate: true,
inline: true, pure_getters: true,
passes: 2, properties: true,
pure_getters: "strict",
reduce_vars: true, reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true, unused: true,
} }
input: { input: {
var C = 1; function f0(o, a, h) {
console.log(C); var b = 3 - a;
var obj = { var obj = o;
bar: function() { var seven = 7;
return C + C; var prop = 'run';
var t = obj[prop](b)[seven] = h;
return t;
} }
};
console.log(obj.bar());
} }
expect: { expect: {
console.log(1); function f0(o, a, h) {
console.log(2); return o.run(3 - a)[7] = h;
} }
expect_stdout: [ }
"1", }
"2",
] issue_2364_6: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b) {
var c = a.p;
b.p = "FAIL";
return c;
}
var o = {
p: "PASS"
}
console.log(f(o, o));
}
expect: {
function f(a, b) {
var c = a.p;
b.p = "FAIL";
return c;
}
var o = {
p: "PASS"
}
console.log(f(o, o));
}
expect_stdout: "PASS"
}
issue_2364_7: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b) {
var c = a.p;
b.f();
return c;
}
var o = {
p: "PASS",
f: function() {
this.p = "FAIL";
}
}
console.log(f(o, o));
}
expect: {
function f(a, b) {
var c = a.p;
b.f();
return c;
}
var o = {
p: "PASS",
f: function() {
this.p = "FAIL";
}
}
console.log(f(o, o));
}
expect_stdout: "PASS"
}
issue_2364_8: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b, c) {
var d = a[b.f = function() {
return "PASS";
}];
return c.f(d);
}
var o = {
f: function() {
return "FAIL";
}
};
console.log(f({}, o, o));
}
expect: {
function f(a, b, c) {
var d = a[b.f = function() {
return "PASS";
}];
return c.f(d);
}
var o = {
f: function() {
return "FAIL";
}
};
console.log(f({}, o, o));
}
expect_stdout: "PASS"
}
issue_2364_9: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b) {
var d = a();
return b.f(d);
}
var o = {
f: function() {
return "FAIL";
}
};
console.log(f(function() {
o.f = function() {
return "PASS";
};
}, o));
}
expect: {
function f(a, b) {
var d = a();
return b.f(d);
}
var o = {
f: function() {
return "FAIL";
}
};
console.log(f(function() {
o.f = function() {
return "PASS";
};
}, o));
}
expect_stdout: "PASS"
}
pure_getters_chain: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function o(t, r) {
var a = t[1], s = t[2], o = t[3], i = t[5];
return a <= 23 && s <= 59 && o <= 59 && (!r || i);
}
console.log(o([ , 23, 59, 59, , 42], 1));
}
expect: {
function o(t, r) {
return t[1] <= 23 && t[2] <= 59 && t[3] <= 59 && (!r || t[5]);
}
console.log(o([ , 23, 59, 59, , 42], 1));
}
expect_stdout: "42"
}
conditional_1: {
options = {
collapse_vars: true,
}
input: {
function f(a, b) {
var c = "";
var d = b ? ">" : "<";
if (a) c += "=";
return c += d;
}
console.log(f(0, 0), f(0, 1), f(1, 0), f(1, 1));
}
expect: {
function f(a, b) {
var c = "";
if (a) c += "=";
return c += b ? ">" : "<";
}
console.log(f(0, 0), f(0, 1), f(1, 0), f(1, 1));
}
expect_stdout: "< > =< =>"
}
conditional_2: {
options = {
collapse_vars: true,
}
input: {
function f(a, b) {
var c = a + 1, d = a + 2;
return b ? c : d;
}
console.log(f(3, 0), f(4, 1));
}
expect: {
function f(a, b) {
return b ? a + 1 : a + 2;
}
console.log(f(3, 0), f(4, 1));
}
expect_stdout: "5 5"
} }

View File

@@ -74,3 +74,40 @@ dont_change_in_or_instanceof_expressions: {
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

@@ -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() {

View File

@@ -0,0 +1,371 @@
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"
}

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,200 @@ 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"
}

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,
@@ -1190,10 +1501,10 @@ defun_label: {
!function() { !function() {
console.log(function(a) { console.log(function(a) {
L: { L: {
if (a) break L; if (2) break L;
return 1; return 1;
} }
}(2)); }());
}(); }();
} }
expect_stdout: true expect_stdout: 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: {
@@ -2894,3 +3206,92 @@ array_forin_2: {
} }
expect_stdout: "3" expect_stdout: "3"
} }
const_expr_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var o = {
a: 1,
b: 2
};
o.a++;
console.log(o.a, o.b);
}
expect: {
var o = {
a: 1,
b: 2
};
o.a++;
console.log(o.a, o.b);
}
expect_stdout: "2 2"
}
const_expr_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
Object.prototype.c = function() {
this.a++;
};
var o = {
a: 1,
b: 2
};
o.c();
console.log(o.a, o.b);
}
expect: {
Object.prototype.c = function() {
this.a++;
};
var o = {
a: 1,
b: 2
};
o.c();
console.log(o.a, o.b);
}
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"
}

View File

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