Compare commits

..

16 Commits

Author SHA1 Message Date
Alex Lam S.L
b84c99ef5c harmony-v3.2.0 2017-11-26 06:02:49 +08:00
alexlamsl
4f08c2f504 Merge branch 'master' into harmony-v3.2.0 2017-11-26 04:23:57 +08:00
Alex Lam S.L
b37a68c84f v3.2.0 2017-11-26 04:08:35 +08:00
Alex Lam S.L
c141ae6f8d fix argument/atom collision by properties (#2514)
fixes #2513
2017-11-25 22:52:46 +08:00
Alex Lam S.L
97c464dbf5 fix wording and formatting (#2512) 2017-11-25 19:07:46 +08:00
kzc
ba4894af18 document top level minify option keep_classnames (#2511) 2017-11-25 16:33:03 +08:00
Alex Lam S.L
f1e3ef5262 separate keep_classnames & keep_fnames (#2510)
fixes #2418
2017-11-25 16:31:52 +08:00
Alex Lam S.L
3b28b915eb extend escape analysis on constant expression properties (#2509)
fixes #2508
2017-11-24 14:07:39 +08:00
Alex Lam S.L
eb001dc1d9 fix argument/atom collision by collapse_vars (#2507)
fixes #2506
2017-11-24 07:26:22 +08:00
Alex Lam S.L
aa9bdf416e make AST_Lambda.contains_this() less magical (#2505) 2017-11-24 07:03:37 +08:00
Alex Lam S.L
bbf38dc9c0 fix reduce_vars on arrow functions with this (#2504)
fixes #2496
2017-11-24 06:21:49 +08:00
Alex Lam S.L
8987780db6 eliminate invalid state caching in collapse_vars (#2502)
fixes #2497
2017-11-24 04:12:37 +08:00
Alex Lam S.L
30cfea2e7a fix rename (#2501)
- suppress spurious `rename` from `commander`
- handle `AST_SymbolCatch` correctly
2017-11-24 03:05:43 +08:00
kzc
3d8341a7ab fix properties for array literal with spread (#2499)
fixes #2498
2017-11-24 02:04:26 +08:00
Alex Lam S.L
f4e2fb9864 expand symbol space to improve compression (#2460)
- give globally distinct names to distinct variables
- improve ability to compress cross-scoped
- introduce `options.rename` to `minify()`
- default `true` if both `compress` & `mangle`
2017-11-19 19:29:51 +08:00
Alex Lam S.L
b80062c490 enable hoist_props by default (#2492) 2017-11-19 14:56:23 +08:00
15 changed files with 1333 additions and 110 deletions

View File

@@ -107,6 +107,7 @@ a double dash to prevent input files being used as option arguments:
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.
By default UglifyJS will not try to be IE-proof. By default UglifyJS will not try to be IE-proof.
--keep-classnames Do not mangle/drop class names.
--keep-fnames Do not mangle/drop function names. Useful for --keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name. code relying on Function.prototype.name.
--name-cache <file> File to hold mangled name mappings. --name-cache <file> File to hold mangled name mappings.
@@ -509,8 +510,13 @@ if (result.error) throw result.error;
- `ie8` (default `false`) - set to `true` to support IE8. - `ie8` (default `false`) - set to `true` to support IE8.
- `keep_classnames` (default: `undefined`) - pass `true` to prevent discarding or mangling
of class names.
- `keep_fnames` (default: `false`) - pass `true` to prevent discarding or mangling - `keep_fnames` (default: `false`) - pass `true` to prevent discarding or mangling
of function names. Useful for code relying on `Function.prototype.name`. of function names. Useful for code relying on `Function.prototype.name`. If the
top level minify option `keep_classnames` is `undefined` it will be overriden with
the value of the top level minify option `keep_fnames`.
## Minify options structure ## Minify options structure
@@ -538,6 +544,8 @@ if (result.error) throw result.error;
ecma: 5, // specify one of: 5, 6, 7 or 8 ecma: 5, // specify one of: 5, 6, 7 or 8
nameCache: null, // or specify a name cache object nameCache: null, // or specify a name cache object
toplevel: false, toplevel: false,
keep_classnames: false,
keep_fnames: false,
ie8: false, ie8: false,
warnings: false, warnings: false,
} }
@@ -609,17 +617,17 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
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.
- `booleans` (default: `true`) -- various optimizations for boolean context, for example `!!a - `booleans` (default: `true`) -- various optimizations for boolean context,
? b : c → a ? b : c` for example `!!a ? b : c → a ? b : c`
- `cascade` (default: `true`) -- small optimization for sequences, transform `x, x` into `x` - `cascade` (default: `true`) -- small optimization for sequences, transform
and `x = something(), x` into `x = something()` `x, x` into `x` and `x = something(), x` into `x = something()`
- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables - side - `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables,
effects permitting. side effects permitting.
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes, for example: - `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
`!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary e.g. `!(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
@@ -630,7 +638,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `dead_code` (default: `true`) -- remove unreachable code - `dead_code` (default: `true`) -- remove unreachable code
- `drop_console` (default: `false`) -- default `false`. Pass `true` to discard calls to - `drop_console` (default: `false`) -- Pass `true` to discard calls to
`console.*` functions. If you wish to drop a specific function call `console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead. after dropping the function call then use `pure_funcs` instead.
@@ -642,14 +650,14 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `evaluate` (default: `true`) -- attempt to evaluate constant expressions - `evaluate` (default: `true`) -- attempt to evaluate constant expressions
- `expression` (default: `false`) -- default `false`. Pass `true` to preserve completion values - `expression` (default: `false`) -- Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets. from terminal statements without `return`, e.g. in bookmarklets.
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation) - `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `hoist_funs` (default: `true`) -- hoist function declarations - `hoist_funs` (default: `true`) -- hoist function declarations
- `hoist_props` (default: `false`) -- hoist properties from constant object and - `hoist_props` (default: `true`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example: 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` `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, works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher,
@@ -664,19 +672,22 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `join_vars` (default: `true`) -- join consecutive `var` statements - `join_vars` (default: `true`) -- join consecutive `var` statements
- `keep_fargs` (default: `true`) -- default `true`. Prevents the - `keep_classnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding unused function arguments. You need this compressor from discarding class names. See also: the `keep_classnames`
for code which relies on `Function.length`. [mangle option](#mangle).
- `keep_fargs` (default: `true`) -- Prevents the compressor from discarding unused
function arguments. You need this for code which relies on `Function.length`.
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the - `keep_fnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle). `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`) -- 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.
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops when we can - `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
statically determine the condition when we can statically determine the condition.
- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions" - `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the where the return value is discarded, to avoid the parens that the
@@ -707,11 +718,11 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
`foo` is certain to not throw, i.e. not `null` or `undefined`. `foo` is certain to not throw, i.e. not `null` or `undefined`.
- `reduce_funcs` (default: `true`) -- Allows single-use functions to be - `reduce_funcs` (default: `true`) -- Allows single-use functions to be
inlined as function expressions when permissible allowing further inlined as function expressions when permissible allowing further
optimization. Enabled by default. Option depends on `reduce_vars` optimization. Enabled by default. Option depends on `reduce_vars`
being enabled. Some code runs faster in the Chrome V8 engine if this being enabled. Some code runs faster in the Chrome V8 engine if this
option is disabled. Does not negatively impact other major browsers. option is disabled. Does not negatively impact other major browsers.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and - `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
used as constant values. used as constant values.
@@ -724,21 +735,22 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
occasions the default sequences limit leads to very slow compress times in which occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended. case a value of `20` or less is recommended.
- `side_effects` (default: `true`) -- default `true`. Pass `false` to disable potentially dropping - `side_effects` (default: `true`) -- Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();` example: `/*@__PURE__*/foo();`
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches - `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`) - `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
in the top level scope (`false` by default, `true` to drop both unreferenced variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
functions and variables) both unreferenced functions and variables)
- `top_retain` (default: `null`) -- prevent specific toplevel functions and variables from `unused` - `top_retain` (default: `null`) -- prevent specific toplevel functions and
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`) variables from `unused` removal (can be array, comma-separated, RegExp or
function. Implies `toplevel`)
- `typeofs` (default: `true`) -- default `true`. Transforms `typeof foo == "undefined"` into - `typeofs` (default: `true`) -- Transforms `typeof foo == "undefined"` into
`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.
@@ -776,30 +788,31 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with - `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants. `RegExp` values the same way as if they are constants.
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple direct variable - `unused` (default: `true`) -- drop unreferenced functions and variables (simple
assignments do not count as references unless set to `"keep_assign"`) direct variable assignments do not count as references unless set to `"keep_assign"`)
- `warnings` (default: `false`) -- display warnings when dropping unreachable code or unused - `warnings` (default: `false`) -- display warnings when dropping unreachable
declarations etc. code or unused declarations etc.
## Mangle options ## Mangle options
- `eval` (default `false`). Pass `true` to mangle names visible in scopes - `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
where `eval` or `with` are used. 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.
See also: the `keep_classnames` [compress option](#compress-options).
- `keep_fnames` (default `false`). Pass `true` to not mangle function 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` Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
[compress option](#compress-options). [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.
- `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".

View File

@@ -46,8 +46,10 @@ 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>", "Specify 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-classnames", "Do not mangle/drop class names.");
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.");
program.option("--no-rename", "Disable symbol expansion.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)"); program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map()); program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
program.option("--timings", "Display operations run time on STDERR.") program.option("--timings", "Display operations run time on STDERR.")
@@ -66,11 +68,13 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
"compress", "compress",
"ie8", "ie8",
"mangle", "mangle",
"rename",
"sourceMap", "sourceMap",
"toplevel", "toplevel",
"wrap" "wrap"
].forEach(function(name) { ].forEach(function(name) {
if (name in program) { if (name in program) {
if (name == "rename" && program[name]) return;
options[name] = program[name]; options[name] = program[name];
} }
}); });
@@ -95,6 +99,9 @@ if (program.define) {
options.compress.global_defs[expr] = program.define[expr]; options.compress.global_defs[expr] = program.define[expr];
} }
} }
if (program.keepClassnames) {
options.keep_classnames = true;
}
if (program.keepFnames) { if (program.keepFnames) {
options.keep_fnames = true; options.keep_fnames = true;
} }

View File

@@ -63,12 +63,13 @@ 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_props : !false_by_default,
hoist_vars : false, hoist_vars : false,
ie8 : false, ie8 : false,
if_return : !false_by_default, if_return : !false_by_default,
inline : !false_by_default, inline : !false_by_default,
join_vars : !false_by_default, join_vars : !false_by_default,
keep_classnames: false,
keep_fargs : true, keep_fargs : true,
keep_fnames : false, keep_fnames : false,
keep_infinity : false, keep_infinity : false,
@@ -340,7 +341,7 @@ merge(Compressor.prototype, {
} }
} }
} }
mark_escaped(d, node, value, 0); mark_escaped(d, node.scope, node, value, 0);
} }
if (node instanceof AST_SymbolCatch) { if (node instanceof AST_SymbolCatch) {
node.definition().fixed = false; node.definition().fixed = false;
@@ -626,7 +627,8 @@ merge(Compressor.prototype, {
|| !immutable || !immutable
&& parent instanceof AST_Call && parent instanceof AST_Call
&& parent.expression === node && parent.expression === node
&& (!(value instanceof AST_Function) || value.contains_this(parent))) { && (!(value instanceof AST_Function)
|| !(parent instanceof AST_New) && value.contains_this())) {
return true; return true;
} else if (parent instanceof AST_Array) { } else if (parent instanceof AST_Array) {
return is_modified(parent, parent, level + 1); return is_modified(parent, parent, level + 1);
@@ -638,10 +640,13 @@ merge(Compressor.prototype, {
} }
} }
function mark_escaped(d, node, value, level) { function mark_escaped(d, scope, node, value, level) {
var parent = tw.parent(level); var parent = tw.parent(level);
if (value instanceof AST_Constant || value instanceof AST_ClassExpression) return; if (value) {
if (level > 0 && value instanceof AST_Function) return; if (value.is_constant()) return;
if (value instanceof AST_ClassExpression) return;
if (level > 0 && value.is_constant_expression(scope)) return;
}
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression || parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
@@ -649,13 +654,13 @@ merge(Compressor.prototype, {
d.escaped = true; d.escaped = true;
return; return;
} else if (parent instanceof AST_Array) { } else if (parent instanceof AST_Array) {
mark_escaped(d, parent, parent, level + 1); mark_escaped(d, scope, parent, parent, level + 1);
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) { } else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1); var obj = tw.parent(level + 1);
mark_escaped(d, obj, obj, level + 2); mark_escaped(d, scope, obj, obj, level + 2);
} else if (parent instanceof AST_PropAccess && node === parent.expression) { } else if (parent instanceof AST_PropAccess && node === parent.expression) {
value = read_property(value, parent.property); value = read_property(value, parent.property);
mark_escaped(d, parent, value, level + 1); mark_escaped(d, scope, parent, value, level + 1);
if (value) return; if (value) return;
} }
if (level == 0) d.direct_access = true; if (level == 0) d.direct_access = true;
@@ -844,6 +849,12 @@ merge(Compressor.prototype, {
} }
} }
function is_identifier_atom(node) {
return node instanceof AST_Infinity
|| node instanceof AST_NaN
|| node instanceof AST_Undefined;
}
function tighten_body(statements, compressor) { function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10; var CHANGED, max_iter = 10;
do { do {
@@ -910,7 +921,7 @@ merge(Compressor.prototype, {
&& !(node instanceof AST_SymbolDeclaration) && !(node instanceof AST_SymbolDeclaration)
&& lhs.equivalent_to(node)) { && lhs.equivalent_to(node)) {
if (is_lhs(node, parent)) { if (is_lhs(node, parent)) {
if (candidate.multiple) replaced++; if (value_def) replaced++;
return node; return node;
} }
CHANGED = abort = true; CHANGED = abort = true;
@@ -925,19 +936,24 @@ merge(Compressor.prototype, {
return make_node(AST_UnaryPrefix, candidate, candidate); return make_node(AST_UnaryPrefix, candidate, candidate);
} }
if (candidate instanceof AST_VarDef) { if (candidate instanceof AST_VarDef) {
if (candidate.multiple) { if (value_def) {
abort = false; abort = false;
return node; return node;
} }
var def = candidate.name.definition(); var def = candidate.name.definition();
var value = candidate.value;
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++; def.replaced++;
return maintain_this_binding(parent, node, candidate.value); if (funarg && is_identifier_atom(value)) {
return value.transform(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
} }
return make_node(AST_Assign, candidate, { return make_node(AST_Assign, candidate, {
operator: "=", operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name), left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: candidate.value right: value
}); });
} }
candidate.write_only = false; candidate.write_only = false;
@@ -999,18 +1015,20 @@ merge(Compressor.prototype, {
extract_candidates(statements[stat_index]); extract_candidates(statements[stat_index]);
while (candidates.length > 0) { while (candidates.length > 0) {
var candidate = candidates.pop(); var candidate = candidates.pop();
var value_def = null;
var lhs = get_lhs(candidate); var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue; if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// 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 replace_all = candidate.multiple; var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) { if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition(); var def = lhs.definition();
replace_all = def.references.length - def.replaced == 1; replace_all = def.references.length - def.replaced == 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 funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit; var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) { if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) { for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
@@ -1021,13 +1039,12 @@ merge(Compressor.prototype, {
for (var i = stat_index; !abort && i < statements.length; i++) { for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner); statements[i].transform(scanner);
} }
if (candidate.multiple) { if (value_def) {
var def = candidate.name.definition(); var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false; if (abort && def.references.length - def.replaced > replaced) replaced = false;
else { else {
abort = false; abort = false;
hit = candidate.name instanceof AST_SymbolFunarg; hit = funarg;
var value_def = candidate.value.definition();
for (var i = stat_index; !abort && i < statements.length; i++) { for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer); statements[i].transform(multi_replacer);
} }
@@ -1130,13 +1147,13 @@ merge(Compressor.prototype, {
} }
} }
function mangleable_var(expr) { function mangleable_var(var_def) {
var value = expr.value; var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return false; if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return false; if (value.name == "arguments") return;
if (value.definition().undeclared) return false; var def = value.definition();
expr.multiple = true; if (def.undeclared) return;
return true; return value_def = def;
} }
function get_lhs(expr) { function get_lhs(expr) {
@@ -2390,6 +2407,10 @@ merge(Compressor.prototype, {
} }
return true; return true;
} }
if (node instanceof AST_This && self instanceof AST_Arrow) {
result = false;
return true;
}
})); }));
return result; return result;
}); });
@@ -2606,8 +2627,9 @@ merge(Compressor.prototype, {
// pass 3: we should drop declarations not in_use // pass 3: we should drop declarations not in_use
var tt = new TreeTransformer( var tt = new TreeTransformer(
function before(node, descend, in_list) { function before(node, descend, in_list) {
if (!compressor.option("keep_fnames") if (node.name
&& node.name && (node instanceof AST_Function || node instanceof AST_ClassExpression)) { && (!compressor.option("keep_classnames") && node instanceof AST_ClassExpression
|| !compressor.option("keep_fnames") && node instanceof AST_Function)) {
var def = node.name.definition(); var def = node.name.definition();
// any declarations with same name will overshadow // any declarations with same name will overshadow
// name of this anonymous function and can therefore // name of this anonymous function and can therefore
@@ -4056,9 +4078,7 @@ merge(Compressor.prototype, {
if (self.operator == "delete" if (self.operator == "delete"
&& !(e instanceof AST_SymbolRef && !(e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess || e instanceof AST_PropAccess
|| e instanceof AST_NaN || is_identifier_atom(e))) {
|| e instanceof AST_Infinity
|| e instanceof AST_Undefined)) {
if (e instanceof AST_Sequence) { if (e instanceof AST_Sequence) {
e = e.expressions.slice(); e = e.expressions.slice();
e.push(make_node(AST_True, self)); e.push(make_node(AST_True, self));
@@ -4582,7 +4602,7 @@ merge(Compressor.prototype, {
if (fixed instanceof AST_Defun) { if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed); d.fixed = fixed = make_node(AST_Function, fixed, fixed);
} }
if (d.single_use && fixed instanceof AST_Function) { if (d.single_use && is_func_expr(fixed)) {
if (d.scope !== self.scope if (d.scope !== self.scope
&& (!compressor.option("reduce_funcs") && (!compressor.option("reduce_funcs")
|| d.escaped || d.escaped
@@ -4595,7 +4615,7 @@ merge(Compressor.prototype, {
if (d.single_use == "f") { if (d.single_use == "f") {
var scope = self.scope; var scope = self.scope;
do { do {
if (scope instanceof AST_Defun || scope instanceof AST_Function) { if (scope instanceof AST_Defun || is_func_expr(scope)) {
scope.inlined = true; scope.inlined = true;
} }
} while (scope = scope.parent_scope); } while (scope = scope.parent_scope);
@@ -4966,6 +4986,17 @@ merge(Compressor.prototype, {
if (compressor.option("properties")) { if (compressor.option("properties")) {
var key = prop.evaluate(compressor); var key = prop.evaluate(compressor);
if (key !== prop) { if (key !== prop) {
if (typeof key == "string") {
if (key == "undefined") {
key = undefined;
} else {
var value = parseFloat(key);
if (value.toString() == key) {
key = value;
}
}
}
prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
var property = "" + key; var property = "" + key;
if (is_identifier_string(property) if (is_identifier_string(property)
&& property.length <= prop.print_to_string().length + 1) { && property.length <= prop.print_to_string().length + 1) {
@@ -4974,14 +5005,6 @@ merge(Compressor.prototype, {
property: property property: property
}).optimize(compressor); }).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
});
}
}
} }
} }
if (is_lhs(self, compressor.parent())) return self; if (is_lhs(self, compressor.parent())) return self;
@@ -4996,7 +5019,7 @@ merge(Compressor.prototype, {
&& prop instanceof AST_Number && expr instanceof AST_Array) { && prop instanceof AST_Number && expr instanceof AST_Array) {
var index = prop.getValue(); var index = prop.getValue();
var elements = expr.elements; var elements = expr.elements;
if (index in elements) { FLATTEN: if (index in elements) {
var flatten = true; var flatten = true;
var values = []; var values = [];
for (var i = elements.length; --i > index;) { for (var i = elements.length; --i > index;) {
@@ -5007,10 +5030,13 @@ merge(Compressor.prototype, {
} }
} }
var retValue = elements[index]; var retValue = elements[index];
if (retValue instanceof AST_Expansion) break FLATTEN;
retValue = retValue instanceof AST_Hole ? make_node(AST_Undefined, retValue) : retValue; retValue = retValue instanceof AST_Hole ? make_node(AST_Undefined, retValue) : retValue;
if (!flatten) values.unshift(retValue); if (!flatten) values.unshift(retValue);
while (--i >= 0) { while (--i >= 0) {
var value = elements[i].drop_side_effect_free(compressor); var value = elements[i];
if (value instanceof AST_Expansion) break FLATTEN;
value = value.drop_side_effect_free(compressor);
if (value) values.unshift(value); if (value) values.unshift(value);
else index--; else index--;
} }
@@ -5035,8 +5061,7 @@ merge(Compressor.prototype, {
return self; return self;
}); });
AST_Lambda.DEFMETHOD("contains_this", function(grandparent) { AST_Lambda.DEFMETHOD("contains_this", function() {
if (grandparent instanceof AST_New) return false;
var result; var result;
var self = this; var self = this;
self.walk(new TreeWalker(function(node) { self.walk(new TreeWalker(function(node) {
@@ -5062,7 +5087,8 @@ merge(Compressor.prototype, {
})) break; })) break;
var value = prop.value; var value = prop.value;
if ((value instanceof AST_Accessor || value instanceof AST_Function) if ((value instanceof AST_Accessor || value instanceof AST_Function)
&& value.contains_this(compressor.parent())) break; && !(compressor.parent() instanceof AST_New)
&& value.contains_this()) break;
return make_node(AST_Sub, this, { return make_node(AST_Sub, this, {
expression: make_node(AST_Array, expr, { expression: make_node(AST_Array, expr, {
elements: props.map(function(prop) { elements: props.map(function(prop) {

View File

@@ -51,11 +51,13 @@ function minify(files, options) {
compress: {}, compress: {},
ecma: undefined, ecma: undefined,
ie8: false, ie8: false,
keep_classnames: undefined,
keep_fnames: false, keep_fnames: false,
mangle: {}, mangle: {},
nameCache: null, nameCache: null,
output: {}, output: {},
parse: {}, parse: {},
rename: undefined,
sourceMap: false, sourceMap: false,
timings: false, timings: false,
toplevel: false, toplevel: false,
@@ -65,8 +67,15 @@ function minify(files, options) {
var timings = options.timings && { var timings = options.timings && {
start: Date.now() start: Date.now()
}; };
if (options.keep_classnames === undefined) {
options.keep_classnames = options.keep_fnames;
}
if (options.rename === undefined) {
options.rename = options.compress && options.mangle;
}
set_shorthand("ecma", options, [ "parse", "compress", "output" ]); set_shorthand("ecma", options, [ "parse", "compress", "output" ]);
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_classnames", options, [ "compress", "mangle" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("warnings", options, [ "compress" ]); set_shorthand("warnings", options, [ "compress" ]);
@@ -141,6 +150,11 @@ function minify(files, options) {
if (options.wrap) { if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap); toplevel = toplevel.wrap_commonjs(options.wrap);
} }
if (timings) timings.rename = Date.now();
if (options.rename) {
toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle);
}
if (timings) timings.compress = Date.now(); if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel); if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope = Date.now(); if (timings) timings.scope = Date.now();
@@ -201,7 +215,8 @@ function minify(files, options) {
if (timings) { if (timings) {
timings.end = Date.now(); timings.end = Date.now();
result.timings = { result.timings = {
parse: 1e-3 * (timings.compress - timings.parse), parse: 1e-3 * (timings.rename - timings.parse),
rename: 1e-3 * (timings.compress - timings.rename),
compress: 1e-3 * (timings.scope - timings.compress), compress: 1e-3 * (timings.scope - timings.compress),
scope: 1e-3 * (timings.mangle - timings.scope), scope: 1e-3 * (timings.mangle - timings.scope),
mangle: 1e-3 * (timings.properties - timings.mangle), mangle: 1e-3 * (timings.properties - timings.mangle),

View File

@@ -43,7 +43,7 @@
"use strict"; "use strict";
function SymbolDef(scope, index, orig) { function SymbolDef(scope, orig) {
this.name = orig.name; this.name = orig.name;
this.orig = [ orig ]; this.orig = [ orig ];
this.eliminated = 0; this.eliminated = 0;
@@ -54,7 +54,6 @@ function SymbolDef(scope, index, orig) {
this.export = false; this.export = false;
this.mangled_name = null; this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.index = index;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
}; };
@@ -346,7 +345,7 @@ AST_Toplevel.DEFMETHOD("def_global", function(node){
if (globals.has(name)) { if (globals.has(name)) {
return globals.get(name); return globals.get(name);
} else { } else {
var g = new SymbolDef(this, globals.size(), node); var g = new SymbolDef(this, node);
g.undeclared = true; g.undeclared = true;
g.global = true; g.global = true;
globals.set(name, g); globals.set(name, g);
@@ -417,7 +416,7 @@ AST_Scope.DEFMETHOD("def_function", function(symbol){
AST_Scope.DEFMETHOD("def_variable", function(symbol){ AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def; var def;
if (!this.variables.has(symbol.name)) { if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol); def = new SymbolDef(this, symbol);
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);
def.global = !this.parent_scope; def.global = !this.parent_scope;
} else { } else {
@@ -435,7 +434,7 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name reserved from mangling. // shadow a name reserved from mangling.
if (options.reserved.indexOf(m) >= 0) continue; if (member(m, options.reserved)) continue;
// we must ensure that the mangled name does not shadow a name // we must ensure that the mangled name does not shadow a name
// from some parent scope that is referenced in this or in // from some parent scope that is referenced in this or in
@@ -487,7 +486,7 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global; return this.definition().global;
}); });
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
options = defaults(options, { options = defaults(options, {
eval : false, eval : false,
ie8 : false, ie8 : false,
@@ -497,15 +496,14 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
toplevel : false, toplevel : false,
}); });
if (!Array.isArray(options.reserved)) options.reserved = []; if (!Array.isArray(options.reserved)) options.reserved = [];
// Never mangle arguments
push_uniq(options.reserved, "arguments");
return options; return options;
}); });
AST_Toplevel.DEFMETHOD("mangle_names", function(options){ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options); options = this._default_mangler_options(options);
// Never mangle arguments
options.reserved.push('arguments');
// We only need to mangle declaration nodes. Special logic wired // We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's // into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of // present (and for AST_SymbolRef-s it'll use the mangled name of
@@ -514,11 +512,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
var to_mangle = []; var to_mangle = [];
if (options.cache) { if (options.cache) {
this.globals.each(function(symbol){ this.globals.each(collect);
if (options.reserved.indexOf(symbol.name) < 0) {
to_mangle.push(symbol);
}
});
} }
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
@@ -530,13 +524,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
return true; // don't descend again in TreeWalker return true; // don't descend again in TreeWalker
} }
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
var p = tw.parent(), a = []; node.variables.each(collect);
node.variables.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) {
a.push(symbol);
}
});
to_mangle.push.apply(to_mangle, a);
return; return;
} }
if (node instanceof AST_Label) { if (node instanceof AST_Label) {
@@ -561,6 +549,74 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (options.cache) { if (options.cache) {
options.cache.cname = this.cname; options.cache.cname = this.cname;
} }
function collect(symbol) {
if (!member(symbol.name, options.reserved)) {
to_mangle.push(symbol);
}
}
});
AST_Toplevel.DEFMETHOD("find_unique_prefix", function(options) {
var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
var cache = options.cache && options.cache.props;
var prefixes = Object.create(null);
options.reserved.forEach(add_prefix);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
}));
var prefix, i = 0;
do {
prefix = create_name(i++);
} while (prefixes[prefix]);
return prefix;
function add_prefix(name) {
if (/[0-9]$/.test(name)) {
prefixes[name.replace(/[0-9]+$/, "")] = true;
}
}
function add_def(def) {
var name = def.name;
if (def.global && cache && cache.has(name)) name = cache.get(name);
else if (!def.unmangleable(options)) return;
add_prefix(name);
}
function create_name(num) {
var name = "";
do {
name += letters[num % letters.length];
num = Math.floor(num / letters.length);
} while (num);
return name;
}
});
AST_Toplevel.DEFMETHOD("expand_names", function(options) {
options = this._default_mangler_options(options);
var prefix = this.find_unique_prefix(options);
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
}));
function rename(def) {
if (def.global || def.unmangleable(options)) return;
if (member(def.name, options.reserved)) return;
var d = def.redefined();
def.name = d ? d.name : prefix + def.id;
def.orig.forEach(function(sym) {
sym.name = def.name;
});
def.references.forEach(function(sym) {
sym.name = def.name;
});
}
}); });
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){

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.10", "version": "3.2.0",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -26,11 +26,11 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"commander": "~2.11.0", "commander": "~2.12.1",
"source-map": "~0.6.1" "source-map": "~0.6.1"
}, },
"devDependencies": { "devDependencies": {
"acorn": "~5.1.1", "acorn": "~5.2.1",
"mocha": "~3.5.1", "mocha": "~3.5.1",
"semver": "~5.4.1" "semver": "~5.4.1"
}, },

View File

@@ -3880,3 +3880,81 @@ issue_2436_14: {
} }
expect_stdout: true expect_stdout: true
} }
issue_2497: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function sample() {
if (true) {
for (var i = 0; i < 1; ++i) {
for (var k = 0; k < 1; ++k) {
var value = 1;
var x = value;
value = x ? x + 1 : 0;
}
}
} else {
for (var i = 0; i < 1; ++i) {
for (var k = 0; k < 1; ++k) {
var value = 1;
}
}
}
}
}
expect: {
function sample() {
if (true)
for (i = 0; i < 1; ++i)
for (k = 0; k < 1; ++k) {
value = 1;
value = value ? value + 1 : 0;
}
else
for (var i = 0; i < 1; ++i)
for (var k = 0; k < 1; ++k)
var value=1;
}
}
}
issue_2506: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
var c = 0;
function f0(bar) {
function f1(Infinity_2) {
function f13(NaN) {
if (false <= NaN & this >> 1 >= 0) {
c++;
}
}
var b_2 = f13(NaN, c++);
}
var bar = f1(-3, -1);
}
f0(false);
console.log(c);
}
expect: {
var c = 0;
function f0(bar) {
(function(Infinity_2) {
(function(NaN) {
if (false <= 0/0 & this >> 1 >= 0)
c++;
})(0, c++);
})();
}
f0(false);
console.log(c);
}
expect_stdout: "1"
}

View File

@@ -1574,3 +1574,101 @@ issue_2288: {
} }
} }
} }
issue_2418_1: {
options = {
unused: true,
}
input: {
class C {}
function F() {}
(class c {});
(function f() {});
}
expect: {
class C {}
function F() {}
(class {});
(function() {});
}
}
issue_2418_2: {
options = {
keep_classnames: false,
keep_fnames: false,
unused: true,
}
input: {
class C {}
function F() {}
(class c {});
(function f() {});
}
expect: {
class C {}
function F() {}
(class {});
(function() {});
}
}
issue_2418_3: {
options = {
keep_classnames: false,
keep_fnames: true,
unused: true,
}
input: {
class C {}
function F() {}
(class c {});
(function f() {});
}
expect: {
class C {}
function F() {}
(class {});
(function f() {});
}
}
issue_2418_4: {
options = {
keep_classnames: true,
keep_fnames: false,
unused: true,
}
input: {
class C {}
function F() {}
(class c {});
(function f() {});
}
expect: {
class C {}
function F() {}
(class c {});
(function() {});
}
}
issue_2418_5: {
options = {
keep_classnames: true,
keep_fnames: true,
unused: true,
}
input: {
class C {}
function F() {}
(class c {});
(function f() {});
}
expect: {
class C {}
function F() {}
(class c {});
(function f() {});
}
}

View File

@@ -975,3 +975,180 @@ shorthand_keywords: {
expect_stdout: true expect_stdout: true
node_version: ">=4" node_version: ">=4"
} }
array_literal_with_spread_1: {
options = {
properties: true,
side_effects: true,
}
input: {
var f = (x) => [...x][0];
console.log(f(["PASS"]));
}
expect: {
var f = x => [ ...x ][0];
console.log(f([ "PASS" ]));
}
expect_stdout: "PASS"
node_version: ">=6"
}
array_literal_with_spread_2: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log([10, ...[], 20, ...[30, 40], 50]["length"]);
console.log([10, ...[], 20, ...[30, 40], 50][0]);
console.log([10, ...[], 20, ...[30, 40], 50][1]);
console.log([10, ...[], 20, ...[30, 40], 50][2]);
console.log([10, ...[], 20, ...[30, 40], 50][3]);
console.log([10, ...[], 20, ...[30, 40], 50][4]);
console.log([10, ...[], 20, ...[30, 40], 50][5]);
}
expect: {
console.log([ 10, ...[], 20, ...[ 30, 40 ], 50 ]["length"]);
console.log(10);
console.log([ 10, ...[], 20, ...[ 30, 40 ], 50 ][1]);
console.log([ 10, ...[], 20, ...[ 30, 40 ], 50 ][2]);
console.log([ 10, ...[], 20, ...[ 30, 40 ], 50 ][3]);
console.log([ 10, ...[], 20, ...[ 30, 40 ], 50 ][4]);
console.log([ 10, ...[], 20, ...[ 30, 40 ], 50 ][5]);
}
expect_stdout: [
"5",
"10",
"20",
"30",
"40",
"50",
"undefined",
]
node_version: ">=6"
}
array_literal_with_spread_3: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log([10, 20][0]);
console.log([10, 20][1]);
console.log([10, 20][2]);
console.log([...[], 10, 20][0]);
console.log([...[], 10, 20][1]);
console.log([...[], 10, 20][2]);
console.log([10, ...[], 20][0]);
console.log([10, ...[], 20][1]);
console.log([10, ...[], 20][2]);
console.log([10, 20, ...[]][0]);
console.log([10, 20, ...[]][1]);
console.log([10, 20, ...[]][2]);
}
expect: {
console.log(10);
console.log(20);
console.log([ 10, 20 ][2]);
console.log([...[], 10, 20][0]);
console.log([...[], 10, 20][1]);
console.log([...[], 10, 20][2]);
console.log(10);
console.log([10, ...[], 20][1]);
console.log([10, ...[], 20][2]);
console.log(10);
console.log(20);
console.log([10, 20, ...[]][2]);
}
expect_stdout: [
"10",
"20",
"undefined",
"10",
"20",
"undefined",
"10",
"20",
"undefined",
"10",
"20",
"undefined",
]
node_version: ">=6"
}
array_literal_with_spread_4: {
options = {
properties: true,
side_effects: true,
}
input: {
function t(x) {
console.log("(" + x + ")");
return 10 * x;
}
console.log([t(1), t(2)][0]);
console.log([t(1), t(2)][1]);
console.log([t(1), t(2)][2]);
console.log([...[], t(1), t(2)][0]);
console.log([...[], t(1), t(2)][1]);
console.log([...[], t(1), t(2)][2]);
console.log([t(1), ...[], t(2)][0]);
console.log([t(1), ...[], t(2)][1]);
console.log([t(1), ...[], t(2)][2]);
console.log([t(1), t(2), ...[]][0]);
console.log([t(1), t(2), ...[]][1]);
console.log([t(1), t(2), ...[]][2]);
}
expect: {
function t(x) {
console.log("(" + x + ")");
return 10 * x;
}
console.log([ t(1), t(2) ][0]);
console.log((t(1), t(2)));
console.log([ t(1), t(2) ][2]);
console.log([ ...[], t(1), t(2) ][0]);
console.log([ ...[], t(1), t(2) ][1]);
console.log([ ...[], t(1), t(2) ][2]);
console.log([ t(1), t(2) ][0]);
console.log([ t(1), ...[], t(2) ][1]);
console.log([ t(1), ...[], t(2) ][2]);
console.log([ t(1), t(2) ][0]);
console.log((t(1), t(2)));
console.log([ t(1), t(2), ...[] ][2]);
}
expect_stdout: [
"(1)", "(2)", "10",
"(1)", "(2)", "20",
"(1)", "(2)", "undefined",
"(1)", "(2)", "10",
"(1)", "(2)", "20",
"(1)", "(2)", "undefined",
"(1)", "(2)", "10",
"(1)", "(2)", "20",
"(1)", "(2)", "undefined",
"(1)", "(2)", "10",
"(1)", "(2)", "20",
"(1)", "(2)", "undefined",
]
node_version: ">=6"
}

View File

@@ -389,6 +389,7 @@ hoist_class: {
evaluate: true, evaluate: true,
hoist_props: true, hoist_props: true,
inline: true, inline: true,
keep_classnames: true,
keep_fnames: true, keep_fnames: true,
passes: 2, passes: 2,
reduce_funcs: true, reduce_funcs: true,
@@ -432,6 +433,7 @@ hoist_class_with_new: {
evaluate: true, evaluate: true,
hoist_props: true, hoist_props: true,
inline: true, inline: true,
keep_classnames: true,
keep_fnames: true, keep_fnames: true,
passes: 2, passes: 2,
reduce_funcs: true, reduce_funcs: true,
@@ -637,3 +639,136 @@ issue_2473_4: {
} }
expect_stdout: "1 2" expect_stdout: "1 2"
} }
issue_2508_1: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: [ 1 ],
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect: {
(function(x) {
console.log(x);
})([ 1 ]);
}
expect_stdout: true
}
issue_2508_2: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: { b: 2 },
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect: {
(function(x) {
console.log(x);
})({ b: 2 });
}
expect_stdout: true
}
issue_2508_3: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: [ o ],
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect: {
var o = {
a: [ o ],
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect_stdout: true
}
issue_2508_4: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: { b: o },
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect: {
var o = {
a: { b: o },
f: function(x) {
console.log(x);
}
};
o.f(o.a);
}
expect_stdout: true
}
issue_2508_5: {
options = {
collapse_vars: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
f: function(x) {
console.log(x);
}
};
o.f(o.f);
}
expect: {
var o_f = function(x) {
console.log(x);
};
o_f(o_f);
}
expect_stdout: true
}

View File

@@ -1305,3 +1305,29 @@ new_this: {
}(42); }(42);
} }
} }
issue_2513: {
options = {
evaluate: true,
properties: true,
}
input: {
!function(Infinity, NaN, undefined) {
console.log("a"[1/0], "b"["Infinity"]);
console.log("c"[0/0], "d"["NaN"]);
console.log("e"[void 0], "f"["undefined"]);
}(0, 0, 0);
}
expect: {
!function(Infinity, NaN, undefined) {
console.log("a"[1/0], "b"[1/0]);
console.log("c".NaN, "d".NaN);
console.log("e"[void 0], "f"[void 0]);
}(0, 0, 0);
}
expect_stdout: [
"undefined undefined",
"undefined undefined",
"undefined undefined",
]
}

View File

@@ -4819,3 +4819,54 @@ issue_2485: {
} }
expect_stdout: "6" expect_stdout: "6"
} }
issue_2496: {
options = {
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function execute(callback) {
callback();
}
class Foo {
constructor(message) {
this.message = message;
}
go() {
this.message = "PASS";
console.log(this.message);
}
run() {
execute(() => {
this.go();
});
}
}
new Foo("FAIL").run();
}
expect: {
class Foo {
constructor(message) {
this.message = message;
}
go() {
this.message = "PASS";
console.log(this.message);
}
run() {
(function(callback) {
callback();
})(() => {
this.go();
});
}
}
new Foo("FAIL").run();
}
expect_stdout: "PASS"
node_version: ">=6"
}

536
test/compress/rename.js Normal file
View File

@@ -0,0 +1,536 @@
mangle_catch: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(o){a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_ie8: {
rename = true
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(args){a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_var: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(o){var a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_var_ie8: {
rename = true
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var a="FAIL";try{throw 1}catch(args){var a="PASS"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_toplevel: {
rename = true
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_var_toplevel: {
rename = true
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_var_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "FAIL";
try {
throw 1;
} catch (args) {
var a = "PASS";
}
console.log(a);
}
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_1: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_ie8: {
rename = true
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_toplevel: {
rename = true
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_1_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = "PASS";
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "PASS"
}
mangle_catch_redef_2: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: false,
toplevel: false,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_ie8: {
rename = true
options = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: true,
toplevel: false,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_toplevel: {
rename = true
options = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
toplevel: true,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "undefined"
}
mangle_catch_redef_2_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
try {
throw "FAIL1";
} catch (a) {
var a = "FAIL2";
}
console.log(a);
}
expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
expect_stdout: "undefined"
}
issue_2120_1: {
rename = true
mangle = {
ie8: false,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (a) {
if (t) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2120_2: {
rename = true
mangle = {
ie8: true,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
function_iife_catch: {
rename = true
mangle = {
ie8: false,
}
input: {
function f(n) {
!function() {
try {
throw 0;
} catch (n) {
var a = 1;
console.log(n, a);
}
}();
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
expect_stdout: "0 1"
}
function_iife_catch_ie8: {
rename = true
mangle = {
ie8: true,
}
input: {
function f(n) {
!function() {
try {
throw 0;
} catch (n) {
var a = 1;
console.log(n, a);
}
}();
}
f();
}
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
expect_stdout: "0 1"
}
function_catch_catch: {
rename = true
mangle = {
ie8: false,
}
input: {
var o = 0;
function f() {
try {
throw 1;
} catch (c) {
try {
throw 2;
} catch (o) {
var o = 3;
console.log(o);
}
}
console.log(o);
}
f();
}
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_stdout: [
"3",
"undefined",
]
}
function_catch_catch_ie8: {
rename = true
mangle = {
ie8: true,
}
input: {
var o = 0;
function f() {
try {
throw 1;
} catch (c) {
try {
throw 2;
} catch (o) {
var o = 3;
console.log(o);
}
}
console.log(o);
}
f();
}
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_stdout: [
"3",
"undefined",
]
}

View File

@@ -354,7 +354,8 @@ describe("minify", function() {
Uglify.minify(ast, { Uglify.minify(ast, {
compress: { compress: {
sequences: false sequences: false
} },
mangle: false
}); });
assert.ok(stat.body); assert.ok(stat.body);
assert.strictEqual(stat.print_to_string(), "a=x()"); assert.strictEqual(stat.print_to_string(), "a=x()");

View File

@@ -117,6 +117,10 @@ function run_compress_tests() {
test.mangle.properties.reserved = quoted_props; test.mangle.properties.reserved = quoted_props;
U.reserve_quoted_keys(input, quoted_props); U.reserve_quoted_keys(input, quoted_props);
} }
if (test.rename) {
input.figure_out_scope(test.mangle);
input.expand_names(test.mangle);
}
var cmp = new U.Compressor(options, true); var cmp = new U.Compressor(options, true);
var output = cmp.compress(input); var output = cmp.compress(input);
output.figure_out_scope(test.mangle); output.figure_out_scope(test.mangle);