Compare commits

...

39 Commits

Author SHA1 Message Date
Alex Lam S.L
d535daa2c7 harmony-v3.1.6 2017-10-29 18:58:58 +08:00
alexlamsl
0a9cdb6c73 handle computed properties correctly 2017-10-29 17:46:25 +08:00
alexlamsl
3ae34177a6 merge #2391 & #2393 2017-10-29 17:14:52 +08:00
alexlamsl
086cb33163 Merge branch 'master' into harmony-v3.1.6 2017-10-29 13:23:39 +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
6371e2ee63 consistently reduce const safe literals (#2411)
fixes #2406
2017-10-28 11:36:44 +08:00
Alex Lam S.L
6ab73c7bd5 fix & improve AST_TemplateString (#2410)
- resolve `semicolons:false` ambiguity with tagged literals
- allow `side_effects` to work on template literals
- traverse `AST_TemplateString` properly

fixes #2401
2017-10-28 11:36:09 +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
Tom MacWright
2848596280 docs: Fix spelling and style (#2395) 2017-10-24 04:53:56 +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
7d9a8596a9 fix dead_code on AST_Destructuring (#2392)
fixes #2383
2017-10-23 00:34:34 +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
44352eb26a harmony-v3.1.5 2017-10-22 01:49:55 +08:00
alexlamsl
9f1c72ae28 update test
Sub-optimal result due to block scope.
2017-10-22 00:38:16 +08:00
alexlamsl
c60fa67827 Merge branch 'master' into harmony-v3.1.5 2017-10-22 00:35:00 +08:00
Alex Lam S.L
96439ca246 v3.1.5 2017-10-22 00:27:26 +08:00
Thomas Sauer
f9c57dfee0 Allow 'yield' as method name (#2382)
fixes #2381
2017-10-21 14:22:39 +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
kzc
6bf5fea008 option formatting in docs (#2374)
- reintroduce compress documentation for keep_fnames and unsafe_methods
- reintroduce keep_fnames in mangle option docs
- order compress & mangle options
2017-10-18 05:23:53 +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
b6a7ca292e deduplicate AST_Super & AST_This logic (#2366) 2017-10-17 04:19:53 +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
23 changed files with 2223 additions and 310 deletions

View File

@@ -102,7 +102,7 @@ a double dash to prevent input files being used as option arguments:
sequences. sequences.
--config-file <file> Read `minify()` options from JSON file. --config-file <file> Read `minify()` options from JSON file.
-d, --define <expr>[=value] Global definitions. -d, --define <expr>[=value] Global definitions.
--ecma <version> Specifiy ECMAScript release: 5, 6, 7 or 8. --ecma <version> Specify ECMAScript release: 5, 6, 7 or 8.
--ie8 Support non-standard Internet Explorer 8. --ie8 Support non-standard Internet Explorer 8.
Equivalent to setting `ie8: true` in `minify()` Equivalent to setting `ie8: true` in `minify()`
for `compress`, `mangle` and `output` options. for `compress`, `mangle` and `output` options.
@@ -219,7 +219,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
@@ -234,7 +234,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
``` ```
@@ -604,7 +604,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Compress options ## Compress options
- `arrows` (default `true`) -- Converts `()=>{return x}` to `()=>x`. Class - `arrows` (default: `true`) -- Converts `()=>{return x}` to `()=>x`. Class
and object literal methods will also be converted to arrow expressions if and object literal methods will also be converted to arrow expressions if
the resultant code is shorter: `m(){return x}` becomes `m:()=>x`. the resultant code is shorter: `m(){return x}` becomes `m:()=>x`.
This transform requires that the `ecma` compress option is set to `6` or greater. This transform requires that the `ecma` compress option is set to `6` or greater.
@@ -622,8 +622,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
`!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc. nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `computed_props` -- default `true`. Transforms constant computed properties - `computed_props` (default: `true`) -- Transforms constant computed properties
into regular ones: `{["computed"]: 1}` is converted into `{computed: 1}`. into regular ones: `{["computed"]: 1}` is converted to `{computed: 1}`.
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional - `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
expressions expressions
@@ -637,7 +637,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `drop_debugger` (default: `true`) -- remove `debugger;` statements - `drop_debugger` (default: `true`) -- remove `debugger;` statements
- `ecma` -- default `5`. Pass `6` or greater to enable `compress` options that - `ecma` (default: `5`) -- Pass `6` or greater to enable `compress` options that
will transform ES5 code into smaller ES6+ equivalent forms. will transform ES5 code into smaller ES6+ equivalent forms.
- `evaluate` (default: `true`) -- attempt to evaluate constant expressions - `evaluate` (default: `true`) -- attempt to evaluate constant expressions
@@ -649,6 +649,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)
@@ -662,6 +668,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.
@@ -726,7 +736,9 @@ 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_arrows` (default `false`) -- Convert ES5 style anonymous function - `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
- `unsafe_arrows` (default: `false`) -- Convert ES5 style anonymous function
expressions to arrow functions if the function body does not reference `this`. expressions to arrow functions if the function body does not reference `this`.
Note: it is not always safe to perform this conversion if code relies on the Note: it is not always safe to perform this conversion if code relies on the
the function having a `prototype`, which arrow functions lack. the function having a `prototype`, which arrow functions lack.
@@ -739,14 +751,19 @@ 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.
- `unsafe_math` (default: `false`) -- optimize numerical expressions like - `unsafe_math` (default: `false`) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results. `2 * x * 3` into `6 * x`, which may give imprecise floating point results.
- `unsafe_methods` (default: false) -- Converts `{ m: function(){} }` to
`{ m(){} }`. `ecma` must be set to `6` or greater to enable this transform.
If `unsafe_methods` is a RegExp then key/value pairs with keys matching the
RegExp will be converted to concise methods.
Note: if enabled there is a risk of getting a "`<method name>` is not a
constructor" TypeError should any code try to `new` the former function.
- `unsafe_proto` (default: `false`) -- optimize expressions like - `unsafe_proto` (default: `false`) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)` `Array.prototype.slice.call(a)` into `[].slice.call(a)`
@@ -761,17 +778,21 @@ 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_classnames` (default `false`). Pass `true` to not mangle class names. - `keep_classnames` (default `false`). Pass `true` to not mangle class names.
- `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.
- `safari10` (default `false`). Pass `true` to work around the Safari 10 loop - `safari10` (default `false`). Pass `true` to work around the Safari 10 loop
iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041) iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041)
"Cannot declare a let variable twice". "Cannot declare a let variable twice".
@@ -1088,7 +1109,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

@@ -44,7 +44,7 @@ program.option("-o, --output <file>", "Output file (default STDOUT).");
program.option("--comments [filter]", "Preserve copyright comments in the output."); program.option("--comments [filter]", "Preserve copyright comments in the output.");
program.option("--config-file <file>", "Read minify() options from JSON file."); program.option("--config-file <file>", "Read minify() options from JSON file.");
program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define")); program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("define"));
program.option("--ecma <version>", "Specifiy ECMAScript release: 5, 6, 7 or 8."); program.option("--ecma <version>", "Specify ECMAScript release: 5, 6, 7 or 8.");
program.option("--ie8", "Support non-standard Internet Explorer 8."); program.option("--ie8", "Support non-standard Internet Explorer 8.");
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name."); program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
program.option("--name-cache <file>", "File to hold mangled name mappings."); program.option("--name-cache <file>", "File to hold mangled name mappings.");

View File

@@ -448,10 +448,8 @@ var AST_TemplateString = DEFNODE("TemplateString", "segments", {
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.segments.forEach(function(seg, i){ this.segments.forEach(function(seg){
if (i % 2 !== 0) { seg._walk(visitor);
seg._walk(visitor);
}
}); });
}); });
} }
@@ -1042,7 +1040,7 @@ var AST_This = DEFNODE("This", null, {
var AST_Super = DEFNODE("Super", null, { var AST_Super = DEFNODE("Super", null, {
$documentation: "The `super` symbol", $documentation: "The `super` symbol",
}, AST_Symbol); }, AST_This);
var AST_Constant = DEFNODE("Constant", null, { var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants", $documentation: "Base class for all constants",

View File

@@ -63,6 +63,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,
@@ -157,7 +158,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;
@@ -196,6 +197,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;
} }
@@ -289,8 +291,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
@@ -315,16 +318,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 { } else if (d.fixed) {
var parent = tw.parent(); var value = node.fixed_value();
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right if (unused && !compressor.exposed(d)) {
|| parent instanceof AST_Call && node !== parent.expression d.single_use = value
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope && d.references.length == 1
|| parent instanceof AST_VarDef && node === parent.value) { && loop_ids[d.id] === in_loop
d.escaped = true; && 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 {
d.fixed = false;
}
} else {
mark_escaped(d, node, value, 0);
} }
} }
} }
@@ -419,8 +431,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);
@@ -554,6 +565,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;
@@ -567,34 +579,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();
@@ -610,7 +653,6 @@ merge(Compressor.prototype, {
} }
function is_lhs_read_only(lhs) { function is_lhs_read_only(lhs) {
if (lhs instanceof AST_Super) return true;
if (lhs instanceof AST_This) return true; if (lhs instanceof AST_This) return true;
if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda; if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda;
if (lhs instanceof AST_PropAccess) { if (lhs instanceof AST_PropAccess) {
@@ -819,6 +861,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;
@@ -880,17 +923,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_Case && (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Conditional || parent instanceof AST_Case
|| parent instanceof AST_For || parent instanceof AST_Conditional
|| 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;
@@ -1003,27 +1047,14 @@ 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; lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
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());
}
} }
}); });
expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw); expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw);
@@ -1522,9 +1553,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();
@@ -1596,6 +1628,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) {
@@ -1707,35 +1740,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));
}); });
@@ -1759,6 +1763,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);
@@ -1783,6 +1788,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;
} }
@@ -2143,7 +2149,6 @@ merge(Compressor.prototype, {
def(AST_EmptyStatement, return_false); def(AST_EmptyStatement, return_false);
def(AST_Constant, return_false); def(AST_Constant, return_false);
def(AST_Super, return_false);
def(AST_This, return_false); def(AST_This, return_false);
def(AST_Call, function(compressor){ def(AST_Call, function(compressor){
@@ -2238,6 +2243,10 @@ merge(Compressor.prototype, {
return expression.has_side_effects(compressor); return expression.has_side_effects(compressor);
}); });
}); });
def(AST_TemplateSegment, return_false);
def(AST_TemplateString, function(compressor){
return any(this.segments, compressor);
});
})(function(node, func){ })(function(node, func){
node.DEFMETHOD("has_side_effects", func); node.DEFMETHOD("has_side_effects", func);
}); });
@@ -2521,9 +2530,8 @@ merge(Compressor.prototype, {
return node; return node;
} }
var parent = tt.parent(); var parent = tt.parent();
if (node instanceof AST_Definitions if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
&& !(parent instanceof AST_ForIn && parent.init === node) var drop_block = !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var);
&& (drop_vars || !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var))) {
// 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
@@ -2534,8 +2542,8 @@ merge(Compressor.prototype, {
if (def.value) def.value = def.value.transform(tt); if (def.value) def.value = def.value.transform(tt);
if (def.name instanceof AST_Destructuring) return tail.push(def); if (def.name instanceof AST_Destructuring) return tail.push(def);
var sym = def.name.definition(); var sym = def.name.definition();
if (!drop_vars && sym.global) return tail.push(def); if (drop_block && sym.global) return tail.push(def);
if (sym.id in in_use_ids) { if (!(drop_vars || drop_block) || 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) {
@@ -2601,11 +2609,11 @@ 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
}); });
@@ -2814,6 +2822,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){
@@ -2838,7 +2911,6 @@ merge(Compressor.prototype, {
def(AST_Node, return_this); def(AST_Node, return_this);
def(AST_Constant, return_null); def(AST_Constant, return_null);
def(AST_Super, return_null);
def(AST_This, return_null); def(AST_This, return_null);
def(AST_Call, function(compressor, first_in_statement){ def(AST_Call, function(compressor, first_in_statement){
if (!this.is_expr_pure(compressor)) { if (!this.is_expr_pure(compressor)) {
@@ -2864,14 +2936,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 ]);
@@ -2955,6 +3025,11 @@ merge(Compressor.prototype, {
def(AST_Expansion, function(compressor, first_in_statement){ def(AST_Expansion, function(compressor, first_in_statement){
return this.expression.drop_side_effect_free(compressor, first_in_statement); return this.expression.drop_side_effect_free(compressor, first_in_statement);
}); });
def(AST_TemplateSegment, return_null);
def(AST_TemplateString, function(compressor){
var values = trim(this.segments, compressor, first_in_statement);
return values && make_sequence(this, values);
});
})(function(node, func){ })(function(node, func){
node.DEFMETHOD("drop_side_effect_free", func); node.DEFMETHOD("drop_side_effect_free", func);
}); });
@@ -3326,7 +3401,21 @@ merge(Compressor.prototype, {
}); });
AST_Definitions.DEFMETHOD("remove_initializers", function(){ AST_Definitions.DEFMETHOD("remove_initializers", function(){
this.definitions.forEach(function(def){ def.value = null }); var decls = [];
this.definitions.forEach(function(def) {
if (def.name instanceof AST_SymbolDeclaration) {
def.value = null;
decls.push(def);
} else def.name.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolDeclaration) {
decls.push(make_node(AST_VarDef, def, {
name: node,
value: null
}));
}
}));
});
this.definitions = decls;
}); });
AST_Definitions.DEFMETHOD("to_assignments", function(compressor){ AST_Definitions.DEFMETHOD("to_assignments", function(compressor){
@@ -3795,7 +3884,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;
} }
@@ -3951,6 +4040,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() {
@@ -3986,7 +4080,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
@@ -4006,6 +4101,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()) {
@@ -4295,8 +4397,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)
@@ -4348,6 +4449,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 || is_func_expr(fixed) && (d.single_use || is_func_expr(fixed)
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
@@ -4701,19 +4803,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 (compressor.option("properties") && key !== prop) {
value: v var node = self.flatten_object(property, compressor);
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
})
}); });
} }
} }
@@ -4736,30 +4888,47 @@ merge(Compressor.prototype, {
return result; return result;
}); });
AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 6;
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 instanceof AST_ConciseMethod ? prop.key.name : prop.key) == key) {
if (!all(props, function(prop) {
return prop instanceof AST_ObjectKeyVal
|| arrows && prop instanceof AST_ConciseMethod && !prop.is_generator;
})) break;
var value = prop.value;
if ((value instanceof AST_Accessor || 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) {
var v = prop.value;
if (v instanceof AST_Accessor) v = make_node(AST_Function, v, v);
var k = prop.key;
if (k instanceof AST_Node && !(k instanceof AST_SymbolMethod)) {
return make_sequence(prop, [ k, v ]);
}
return v;
})
}),
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;) {
var key = values[i].key;
if ((key instanceof AST_SymbolMethod ? key.name : key) === self.property) {
var value = values[i].value;
if (key instanceof AST_SymbolMethod) {
if (values[i].is_generator) break;
value = make_node(AST_Function, value, 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") {
@@ -4782,6 +4951,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, compressor);
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);
@@ -4832,7 +5006,7 @@ merge(Compressor.prototype, {
var has_special_symbol = false; var has_special_symbol = false;
self.walk(new TreeWalker(function(node) { self.walk(new TreeWalker(function(node) {
if (has_special_symbol) return true; if (has_special_symbol) return true;
if (node instanceof AST_Super || node instanceof AST_This) { if (node instanceof AST_This) {
has_special_symbol = true; has_special_symbol = true;
return true; return true;
} }
@@ -4885,7 +5059,7 @@ merge(Compressor.prototype, {
} }
self.segments = segments; self.segments = segments;
return self; return segments.length == 1 ? make_node(AST_String, self, segments[0]) : self;
}); });
OPT(AST_PrefixedTemplateString, function(self, compressor){ OPT(AST_PrefixedTemplateString, function(self, compressor){

View File

@@ -262,7 +262,7 @@ function OutputStream(options) {
} }
} : noop; } : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , ."); var requireSemicolonChars = makePredicate("( [ + * / - , . `");
function print(str) { function print(str) {
str = String(str); str = String(str);
@@ -1685,9 +1685,6 @@ function OutputStream(options) {
DEFPRINT(AST_Symbol, function (self, output) { DEFPRINT(AST_Symbol, function (self, output) {
self._do_print(output); self._do_print(output);
}); });
DEFPRINT(AST_SymbolDeclaration, function(self, output){
self._do_print(output);
});
DEFPRINT(AST_Hole, noop); DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_This, function(self, output){ DEFPRINT(AST_This, function(self, output){
output.print("this"); output.print("this");

View File

@@ -2565,7 +2565,7 @@ function parse($TEXT, options) {
unexpected(tmp); unexpected(tmp);
} }
case "name": case "name":
if (tmp.value == "yield" && !is_token(peek(), "punc", ":") if (tmp.value == "yield" && !is_token(peek(), "punc", ":") && !is_token(peek(), "punc", "(")
&& S.input.has_directive("use strict") && !is_in_generator()) { && S.input.has_directive("use strict") && !is_in_generator()) {
token_error(tmp, "Unexpected yield identifier inside strict mode"); token_error(tmp, "Unexpected yield identifier inside strict mode");
} }

View File

@@ -262,11 +262,7 @@ TreeTransformer.prototype = new TreeWalker;
}); });
_(AST_TemplateString, function(self, tw) { _(AST_TemplateString, function(self, tw) {
for (var i = 0; i < self.segments.length; i++) { self.segments = do_list(self.segments, tw);
if (!(self.segments[i] instanceof AST_TemplateSegment)) {
self.segments[i] = self.segments[i].transform(tw);
}
}
}); });
_(AST_PrefixedTemplateString, function(self, tw) { _(AST_PrefixedTemplateString, function(self, tw) {

View File

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"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

@@ -203,50 +203,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

@@ -631,3 +631,31 @@ issue_2271: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
concise_method_with_super: {
options = {
arrows: true,
}
input: {
var o = {
f: "FAIL",
g() {
return super.f;
}
}
Object.setPrototypeOf(o, { f: "PASS" });
console.log(o.g());
}
expect: {
var o = {
f: "FAIL",
g() {
return super.f;
}
}
Object.setPrototypeOf(o, { f: "PASS" });
console.log(o.g());
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -2150,7 +2150,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);
} }
@@ -2808,69 +2808,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

@@ -560,3 +560,64 @@ global_fns: {
"RangeError", "RangeError",
] ]
} }
issue_2383_1: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
if (0) {
var {x, y} = foo();
}
}
expect: {
var x, y;
}
}
issue_2383_2: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
if (0) {
var {
x = 0,
y: [ w, , { z, p: q = 7 } ] = [ 1, 2, { z: 3 } ]
} = {};
}
console.log(x, q, w, z);
}
expect: {
var x, w, z, q;
console.log(x, q, w, z);
}
expect_stdout: "undefined undefined undefined undefined"
node_version: ">=6"
}
issue_2383_3: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
}
input: {
var b = 7, y = 8;
if (0) {
var a = 1, [ x, y, z ] = [ 2, 3, 4 ], b = 5;
}
console.log(a, x, y, z, b);
}
expect: {
var b = 7, y = 8;
var a, x, y, z, b;
console.log(a, x, y, z, b);
}
expect_stdout: "undefined undefined 8 undefined 7"
node_version: ">=6"
}

View File

@@ -1291,7 +1291,6 @@ issue_2063: {
} }
expect: { expect: {
var a; var a;
var a;
} }
} }

View File

@@ -480,10 +480,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(
@@ -496,9 +497,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
@@ -724,10 +725,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;
@@ -746,8 +748,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();
} }
} }
@@ -1288,3 +1290,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

@@ -902,10 +902,10 @@ issue_2349b: {
evaluate: true, evaluate: true,
inline: true, inline: true,
passes: 3, passes: 3,
properties: true,
reduce_vars: true, reduce_vars: true,
toplevel: true, toplevel: true,
side_effects: true, side_effects: true,
unsafe: true,
unsafe_arrows: true, unsafe_arrows: true,
unused: true, unused: true,
} }

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: {
@@ -250,7 +254,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: {
@@ -679,8 +684,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({
@@ -698,8 +703,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({
@@ -723,8 +728,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;
@@ -748,8 +753,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() {}
@@ -772,8 +777,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({
@@ -792,8 +797,8 @@ issue_2208_5: {
issue_2208_6: { issue_2208_6: {
options = { options = {
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true,
} }
input: { input: {
console.log({ console.log({
@@ -809,9 +814,11 @@ issue_2208_6: {
issue_2208_7: { issue_2208_7: {
options = { options = {
ecma: 6,
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true, unsafe_arrows: true,
} }
input: { input: {
console.log({ console.log({
@@ -829,9 +836,11 @@ issue_2208_7: {
issue_2208_8: { issue_2208_8: {
options = { options = {
ecma: 6,
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true, unsafe_arrows: true,
} }
input: { input: {
console.log({ console.log({
@@ -851,17 +860,17 @@ issue_2208_8: {
return x(); return x();
} }
}.p()); }.p());
console.log(async function() { console.log((async () => {
return await x(); return await x();
}()); })());
} }
} }
issue_2208_9: { issue_2208_9: {
options = { options = {
inline: true, inline: true,
properties: true,
side_effects: true, side_effects: true,
unsafe: true,
} }
input: { input: {
a = 42; a = 42;
@@ -1052,3 +1061,224 @@ unsafe_methods_regex: {
] ]
node_version: ">=6" node_version: ">=6"
} }
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"
}
computed_property: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log({
a: "bar",
[console.log("foo")]: 42,
}.a);
}
expect: {
console.log([
"bar",
console.log("foo")
][0]);
}
expect_stdout: [
"foo",
"bar"
]
node_version: ">=4"
}

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,
@@ -2766,6 +3077,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,
@@ -2822,10 +3134,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: {
@@ -3051,3 +3363,177 @@ array_forof_2: {
expect_stdout: "3" expect_stdout: "3"
node_version: ">=0.12" node_version: ">=0.12"
} }
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"
}
issue_2406_1: {
options = {
reduce_vars: true,
toplevel: false,
unused: true,
}
input: {
const c = {
fn: function() {
return this;
}
};
let l = {
fn: function() {
return this;
}
};
var v = {
fn: function() {
return this;
}
};
console.log(c.fn(), l.fn(), v.fn());
}
expect: {
const c = {
fn: function() {
return this;
}
};
let l = {
fn: function() {
return this;
}
};
var v = {
fn: function() {
return this;
}
};
console.log(c.fn(), l.fn(), v.fn());
}
}
issue_2406_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
const c = {
fn: function() {
return this;
}
};
let l = {
fn: function() {
return this;
}
};
var v = {
fn: function() {
return this;
}
};
console.log(c.fn(), l.fn(), v.fn());
}
expect: {
console.log({
fn: function() {
return this;
}
}.fn(), {
fn: function() {
return this;
}
}.fn(), {
fn: function() {
return this;
}
}.fn());
}
}
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

@@ -59,7 +59,7 @@ template_string_with_constant_expression: {
var foo = `${4 + 4} equals 4 + 4`; var foo = `${4 + 4} equals 4 + 4`;
} }
expect: { expect: {
var foo = `8 equals 4 + 4`; var foo = "8 equals 4 + 4";
} }
} }
@@ -89,21 +89,21 @@ template_string_with_predefined_constants: {
var c = `${4**14}`; // 8 in template vs 9 chars - 268435456 var c = `${4**14}`; // 8 in template vs 9 chars - 268435456
} }
expect: { expect: {
var foo = `This is undefined`; var foo = "This is undefined";
var bar = `This is NaN`; var bar = "This is NaN";
var baz = `This is null`; var baz = "This is null";
var foofoo = `This is ${1/0}`; var foofoo = `This is ${1/0}`;
var foobar = "This is ${1/0}"; var foobar = "This is ${1/0}";
var foobaz = 'This is ${1/0}'; var foobaz = 'This is ${1/0}';
var barfoo = "This is ${NaN}"; var barfoo = "This is ${NaN}";
var bazfoo = "This is ${null}"; var bazfoo = "This is ${null}";
var bazbaz = `This is ${1/0}`; var bazbaz = `This is ${1/0}`;
var barbar = `This is NaN`; var barbar = "This is NaN";
var barbar = "This is ${0/0}"; var barbar = "This is ${0/0}";
var barber = 'This is ${0/0}'; var barber = 'This is ${0/0}';
var a = `4194304`; var a = "4194304";
var b = `16777216`; // Potential for further concatentation var b = "16777216"; // Potential for further concatentation
var c = `${4**14}`; // Not worth converting var c = `${4**14}`; // Not worth converting
} }
} }
@@ -123,7 +123,7 @@ template_string_evaluate_with_many_segments: {
} }
expect: { expect: {
var foo = `Hello ${guest()}, welcome to ${location()}.`; var foo = `Hello ${guest()}, welcome to ${location()}.`;
var bar = `1234567890`; var bar = "1234567890";
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`; var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `1${foobar()}2${foobar()}3${foobar()}`; var buzz = `1${foobar()}2${foobar()}3${foobar()}`;
} }
@@ -159,7 +159,7 @@ template_string_to_normal_string: {
var bar = "Decimals " + `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`; var bar = "Decimals " + `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
} }
expect: { expect: {
var foo = `This is undefined`; var foo = "This is undefined";
var bar = "Decimals 1234567890"; var bar = "Decimals 1234567890";
} }
} }
@@ -192,7 +192,7 @@ evaluate_nested_templates: {
var baz = `${`${`${`foo`}`}`}`; var baz = `${`${`${`foo`}`}`}`;
} }
expect: { expect: {
var baz = `foo`; var baz = "foo";
} }
} }
@@ -241,8 +241,8 @@ enforce_double_quotes_and_evaluate: {
var baz = `Hello ${world()}`; var baz = `Hello ${world()}`;
} }
expect: { expect: {
var foo = `Hello world`; var foo = "Hello world";
var bar = `Hello world`; var bar = "Hello world";
var baz = `Hello ${world()}`; var baz = `Hello ${world()}`;
} }
} }
@@ -260,8 +260,8 @@ enforce_single_quotes_and_evaluate: {
var baz = `Hello ${world()}`; var baz = `Hello ${world()}`;
} }
expect: { expect: {
var foo = `Hello world`; var foo = "Hello world";
var bar = `Hello world`; var bar = "Hello world";
var baz = `Hello ${world()}`; var baz = `Hello ${world()}`;
} }
} }
@@ -339,7 +339,7 @@ escape_dollar_curly: {
console.log(`${1-0}\${2-0}$\{3-0}${4-0}`) console.log(`${1-0}\${2-0}$\{3-0}${4-0}`)
console.log(`$${""}{not an expression}`) console.log(`$${""}{not an expression}`)
} }
expect_exact: "console.log(`\\${ beep }`);console.log(`1\\${2-0}\\${3-0}4`);console.log(`\\${not an expression}`);" expect_exact: 'console.log("${ beep }");console.log("1${2-0}${3-0}4");console.log("${not an expression}");'
} }
template_starting_with_newline: { template_starting_with_newline: {
@@ -400,3 +400,61 @@ issue_1856_ascii_only: {
} }
expect_exact: "console.log(`\\\\n\\\\r\\\\u2028\\\\u2029\\n\\r\\u2028\\u2029`);" expect_exact: "console.log(`\\\\n\\\\r\\\\u2028\\\\u2029\\n\\r\\u2028\\u2029`);"
} }
side_effects: {
options = {
evaluate: true,
side_effects: true,
}
input: {
`t1`;
tag`t2`;
`t${3}`;
tag`t${4}`;
console.log(`
t${5}`);
function f(a) {
`t6${a}`;
a = `t7${a}` & a;
a = `t8${b}` | a;
a = f`t9${a}` ^ a;
}
}
expect: {
tag`t2`;
tag`t${4}`;
console.log("\nt5");
function f(a) {
a &= `t7${a}`;
a = `t8${b}` | a;
a = f`t9${a}` ^ a;
}
}
}
simple_string: {
options = {
computed_props: true,
evaluate: true,
properties: true,
}
input: {
console.log({[`foo`]: 1}[`foo`], `hi` == "hi", `world`);
}
expect: {
console.log([ 1 ][0], true, "world");
}
expect_stdout: "1 true 'world'"
node_version: ">=4"
}
semicolons: {
beautify = {
semicolons: false,
}
input: {
foo;
`bar`;
}
expect_exact: "foo;`bar`\n"
}

View File

@@ -102,4 +102,15 @@ describe("Yield", function() {
assert.throws(test(tests[i]), fail, tests[i]); assert.throws(test(tests[i]), fail, tests[i]);
} }
}); });
it("Should allow yield to be used as class/object property name", function() {
var input = [
'"use strict";',
"({yield:42});",
"({yield(){}});",
"(class{yield(){}});",
"class C{yield(){}}",
].join("");
assert.strictEqual(UglifyJS.minify(input, { compress: false }).code, input);
});
}); });

View File

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