Compare commits

...

37 Commits

Author SHA1 Message Date
Alex Lam S.L
c46b9f361a harmony-v3.1.1 2017-09-17 15:33:55 +08:00
alexlamsl
3b0b4d6abf handle AST_Super in collapse_vars & side_effects 2017-09-17 05:09:36 +08:00
alexlamsl
d73500e8d1 Merge branch 'master' into harmony-v3.1.1 2017-09-17 04:43:43 +08:00
Alex Lam S.L
aceb0af36b v3.1.1 2017-09-17 04:36:27 +08:00
Alex Lam S.L
4f0953f7e9 handle LHS side-effects on cascade & collapse_vars (#2314)
fixes #2313
2017-09-16 11:45:19 +08:00
Alex Lam S.L
182a47bfb1 improve source mapping (#2312)
fixes #2310
2017-09-15 12:46:48 +08:00
Alex Lam S.L
d8685f528d harmony-v3.1.0 2017-09-10 20:51:33 +08:00
alexlamsl
8891495789 Merge branch 'master' into harmony-v3.1.0 2017-09-10 15:39:33 +08:00
Alex Lam S.L
cd27f4ec38 v3.1.0 2017-09-10 15:17:24 +08:00
Mateusz Burzyński
8158b1bdcf Testing all leading comments against being PURE comments (#2305) 2017-09-10 02:08:15 +08:00
Alex Lam S.L
aacf3edc68 extend unsafe on pure global functions (#2303) 2017-09-07 22:08:34 +08:00
kzc
8b89072190 add Date and other known globals to unsafe compress option (#2302) 2017-09-07 02:44:26 +08:00
Alex Lam S.L
395a17ccda fix collapse_vars on default function argument (#2299)
Avoid collision with local variable `undefined` under certain corner cases.

fixes #2298
2017-09-04 02:32:33 +08:00
Alex Lam S.L
3f355866cf correctly count declarations after hoist_vars (#2297)
fixes #2295
2017-09-03 17:23:31 +08:00
kzc
2779a29a86 avoid generating ternary with spread (#2293)
fixes #2292
2017-08-31 00:57:07 +08:00
David Šanda
71d52f147d Fix CLI example for mangle reserved list of names (#2294) 2017-08-31 00:55:32 +08:00
David Šanda
eb7adaa6fc Fix CLI source-maps examples (#2291)
fixes #2284
2017-08-29 23:49:20 +08:00
Alex Lam S.L
e5cf7972ea fix unused patching of AST_For.init blocks (#2289)
fixes #2288
2017-08-29 01:10:04 +08:00
Alex Lam S.L
067d52b6ba harmony-v3.0.28 2017-08-20 01:19:06 +08:00
alexlamsl
e0e009ace2 Merge branch 'master' into harmony-v3.0.28 2017-08-20 00:35:46 +08:00
Alex Lam S.L
f81ff10a9b v3.0.28 2017-08-20 00:27:01 +08:00
kzc
ae0f117da6 Introduce new compress option unsafe_arrows (#2278)
* Not always safe to convert a function expression to an arrow
  function when code depends on the function prototype existing.

Fixes #2271
2017-08-16 22:51:26 +08:00
Alex Lam S.L
a5461e0adc prohibit let/const redeclaration (#2277)
fixes #2270
2017-08-14 12:31:12 +08:00
Erik Desjardins
16d40915b4 don't escape null characters as \0 when followed by any digit (#2273)
fixes #2272
2017-08-14 12:30:08 +08:00
Alex Lam S.L
2bf8216e50 fix pure_getters on spread of objects (#2275) 2017-08-13 22:08:33 +08:00
kzc
2ed3f8db44 fix output of spread of a sequence (#2268)
fixes #2267
2017-08-03 00:40:19 +08:00
kzc
4700c14855 implement object rest/spread (#2265)
- improve parse errors for destructuring spread elements
- `unsafe` for object literals with rest elements

Miscellaneous
- increase mocha unicode surrogate test timeout
2017-08-02 13:47:58 +08:00
Alex Lam S.L
e7c21e87e3 fix ie8 mangling of top-level AST_SymbolCatch (#2263)
fixes #2254
2017-08-01 02:38:32 +08:00
Alex Lam S.L
f54ab16843 harmony-v3.0.27 2017-07-30 15:15:29 +08:00
alexlamsl
69cb459c16 fix-ups for #2258 2017-07-30 04:26:21 +08:00
alexlamsl
1eae8f2dcc Merge branch 'master' into harmony 2017-07-30 01:57:34 +08:00
Alex Lam S.L
c4c2ef44d0 v3.0.27 2017-07-30 01:50:42 +08:00
Alex Lam S.L
a845897758 improve mangle.properties (#2261)
- include dead code when `keep_quoted`
- unify `keep_quoted` & `reserved`
- make `test/run-tests.js` consistent with `minify()`

fixes #2256
2017-07-29 23:02:04 +08:00
kzc
d600c78d7b have keep_quoted respect quoted method names (#2258)
fixes #2257
2017-07-28 19:42:12 +08:00
kzc
32ea2c5530 issue template: describe acceptable JS input (#2255) 2017-07-27 21:38:36 +08:00
Alex Lam S.L
d3df2f985d extend collapse_vars to let and const (#2252)
fixes #2250
2017-07-25 22:07:21 +08:00
Alex Lam S.L
69861824b5 enhance test for #2242 (#2248) 2017-07-24 00:32:33 +08:00
39 changed files with 1468 additions and 241 deletions

View File

@@ -8,7 +8,14 @@
**Uglify version (`uglifyjs -V`)** **Uglify version (`uglifyjs -V`)**
**JavaScript input** <!-- ideally as small as possible --> **JavaScript input**
<!--
A complete parsable JS program exhibiting the issue with
UglifyJS alone - without third party tools or libraries.
Ideally the input should be as small as possible.
Post a link to a gist if necessary.
-->
**The `uglifyjs` CLI command executed or `minify()` options used.** **The `uglifyjs` CLI command executed or `minify()` options used.**

View File

@@ -148,19 +148,19 @@ debugging your compressed JavaScript. To get a source map, pass
Additional options: Additional options:
- `--source-map filename=<NAME>` to specify the name of the source map. - `--source-map "filename='<NAME>'"` to specify the name of the source map.
- `--source-map root=<URL>` to pass the URL where the original files can be found. - `--source-map "root='<URL>'"` to pass the URL where the original files can be found.
Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the
`//# sourceMappingURL=` directive. `//# sourceMappingURL=` directive.
- `--source-map url=<URL>` to specify the URL where the source map can be found. - `--source-map "url='<URL>'"` to specify the URL where the source map can be found.
For example: For example:
uglifyjs js/file1.js js/file2.js \ uglifyjs js/file1.js js/file2.js \
-o foo.min.js -c -m \ -o foo.min.js -c -m \
--source-map root="http://foo.com/src",url=foo.min.js.map --source-map "root='http://foo.com/src',url='foo.min.js.map'"
The above will compress and mangle `file1.js` and `file2.js`, will drop the The above will compress and mangle `file1.js` and `file2.js`, will drop the
output in `foo.min.js` and the source map in `foo.min.js.map`. The source output in `foo.min.js` and the source map in `foo.min.js.map`. The source
@@ -179,8 +179,8 @@ CoffeeScript → compiled JS, UglifyJS can generate a map from CoffeeScript →
compressed JS by mapping every token in the compiled JS to its original compressed JS by mapping every token in the compiled JS to its original
location. location.
To use this feature pass `--source-map content="/path/to/input/source.map"` To use this feature pass `--source-map "content='/path/to/input/source.map'"`
or `--source-map content=inline` if the source map is included inline with or `--source-map "content=inline"` if the source map is included inline with
the sources. the sources.
## CLI compress options ## CLI compress options
@@ -211,7 +211,7 @@ When mangling is enabled but you want to prevent certain names from being
mangled, you can declare those names with `--mangle reserved` — pass a mangled, you can declare those names with `--mangle reserved` — pass a
comma-separated list of names. For example: comma-separated list of names. For example:
uglifyjs ... -m reserved=[$,require,exports] uglifyjs ... -m reserved=['$','require','exports']
to prevent the `require`, `exports` and `$` names from being changed. to prevent the `require`, `exports` and `$` names from being changed.
@@ -646,9 +646,16 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `evaluate` -- attempt to evaluate constant expressions - `evaluate` -- attempt to evaluate constant expressions
- `arrows` (default `true`) -- convert ES5 style anonymous function expressions - `arrows` (default `true`) -- Converts `()=>{return x}` to `()=>x`. Class
to arrow functions if permissible by language semantics. and object literal methods will also be converted to arrow expressions if
Note: `arrows` requires that the `ecma` compress option is set to `6` or greater. 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.
- `unsafe_arrows` (default `false`) -- Convert ES5 style anonymous function
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
the function having a `prototype`, which arrow functions lack.
This transform requires that the `ecma` compress option is set to `6` or greater.
- `booleans` -- various optimizations for boolean context, for example `!!a - `booleans` -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c` ? b : c → a ? b : c`

View File

@@ -697,7 +697,7 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
var AST_VarDef = DEFNODE("VarDef", "name value", { var AST_VarDef = DEFNODE("VarDef", "name value", {
$documentation: "A variable declaration; only appears in a AST_Definitions node", $documentation: "A variable declaration; only appears in a AST_Definitions node",
$propdoc: { $propdoc: {
name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable", name: "[AST_Destructuring|AST_SymbolConst|AST_SymbolLet|AST_SymbolVar] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer" value: "[AST_Node?] initializer, or null of there's no initializer"
}, },
_walk: function(visitor) { _walk: function(visitor) {

View File

@@ -84,6 +84,7 @@ function Compressor(options, false_by_default) {
toplevel : !!(options && options["top_retain"]), toplevel : !!(options && options["top_retain"]),
typeofs : !false_by_default, typeofs : !false_by_default,
unsafe : false, unsafe : false,
unsafe_arrows : false,
unsafe_comps : false, unsafe_comps : false,
unsafe_Func : false, unsafe_Func : false,
unsafe_math : false, unsafe_math : false,
@@ -302,6 +303,7 @@ merge(Compressor.prototype, {
if (reduce_vars) { if (reduce_vars) {
if (node instanceof AST_Toplevel) node.globals.each(reset_def); if (node instanceof AST_Toplevel) node.globals.each(reset_def);
if (node instanceof AST_Scope) node.variables.each(reset_def); if (node instanceof AST_Scope) node.variables.each(reset_def);
if (node.block_scope) node.block_scope.variables.each(reset_def);
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);
@@ -577,6 +579,8 @@ 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_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) {
lhs = lhs.expression; lhs = lhs.expression;
@@ -728,7 +732,7 @@ merge(Compressor.prototype, {
return node instanceof AST_SymbolRef && node.definition().undeclared; return node instanceof AST_SymbolRef && node.definition().undeclared;
} }
var global_names = makePredicate("Array Boolean console Error Function Math Number RegExp Object String"); var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Math Number parseFloat parseInt RangeError ReferenceError RegExp Object setInterval setTimeout String SyntaxError TypeError unescape URIError");
AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) { AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
return !this.definition().undeclared return !this.definition().undeclared
|| compressor.option("unsafe") && global_names(this.name); || compressor.option("unsafe") && global_names(this.name);
@@ -780,7 +784,7 @@ merge(Compressor.prototype, {
while (candidates.length > 0) { while (candidates.length > 0) {
var candidate = candidates.pop(); var candidate = candidates.pop();
var lhs = get_lhs(candidate); var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs)) 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;
@@ -925,7 +929,7 @@ merge(Compressor.prototype, {
} }
} else { } else {
var arg = iife.args[i]; var arg = iife.args[i];
if (!arg) arg = make_node(AST_Undefined, sym); if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg)) arg = null; else if (has_overlapping_symbol(fn, arg)) arg = null;
if (arg) candidates.unshift(make_node(AST_VarDef, sym, { if (arg) candidates.unshift(make_node(AST_VarDef, sym, {
name: sym, name: sym,
@@ -1426,9 +1430,14 @@ merge(Compressor.prototype, {
def(AST_Object, function(compressor) { def(AST_Object, function(compressor) {
if (!is_strict(compressor)) return false; if (!is_strict(compressor)) return false;
for (var i = this.properties.length; --i >=0;) for (var i = this.properties.length; --i >=0;)
if (this.properties[i].value instanceof AST_Accessor) return true; if (this.properties[i]._dot_throw(compressor)) return true;
return false; return false;
}); });
def(AST_ObjectProperty, return_false);
def(AST_ObjectGetter, return_true);
def(AST_Expansion, function(compressor) {
return this.expression._dot_throw(compressor);
});
def(AST_Function, return_false); def(AST_Function, return_false);
def(AST_Arrow, return_false); def(AST_Arrow, return_false);
def(AST_UnaryPostfix, return_false); def(AST_UnaryPostfix, return_false);
@@ -1730,6 +1739,7 @@ merge(Compressor.prototype, {
var val = {}; var val = {};
for (var i = 0, len = this.properties.length; i < len; i++) { for (var i = 0, len = this.properties.length; i < len; i++) {
var prop = this.properties[i]; var prop = this.properties[i];
if (prop instanceof AST_Expansion) return this;
var key = prop.key; var key = prop.key;
if (key instanceof AST_Symbol) { if (key instanceof AST_Symbol) {
key = key.name; key = key.name;
@@ -2073,26 +2083,38 @@ merge(Compressor.prototype, {
if (!compressor.option("side_effects")) return false; if (!compressor.option("side_effects")) return false;
if (this.pure !== undefined) return this.pure; if (this.pure !== undefined) return this.pure;
var pure = false; var pure = false;
var comments, last_comment; var comments, pure_comment;
if (this.start if (this.start
&& (comments = this.start.comments_before) && (comments = this.start.comments_before)
&& comments.length && comments.length
&& /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) { && (pure_comment = find_if(function (comment) {
pure = last_comment; return /[@#]__PURE__/.test(comment.value);
}, comments))) {
pure = pure_comment;
} }
return this.pure = pure; return this.pure = pure;
}); });
var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
if (compressor.option("unsafe")) {
var expr = this.expression;
if (is_undeclared_ref(expr) && global_pure_fns(expr.name)) return true;
}
return this.has_pure_annotation(compressor) || !compressor.pure_funcs(this);
});
// determine if expression has side effects // determine if expression has side effects
(function(def){ (function(def){
def(AST_Node, return_true); def(AST_Node, return_true);
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){
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return true; if (!this.is_expr_pure(compressor)) return true;
for (var i = this.args.length; --i >= 0;) { for (var i = this.args.length; --i >= 0;) {
if (this.args[i].has_side_effects(compressor)) if (this.args[i].has_side_effects(compressor))
return true; return true;
@@ -2562,17 +2584,18 @@ merge(Compressor.prototype, {
// We fix it at this stage by moving the `var` outside the `for`. // We fix it at this stage by moving the `var` outside the `for`.
if (node instanceof AST_For) { if (node instanceof AST_For) {
descend(node, this); descend(node, this);
var block;
if (node.init instanceof AST_BlockStatement) { if (node.init instanceof AST_BlockStatement) {
var block = node.init; block = node.init;
node.init = block.body.pop(); node.init = block.body.pop();
block.body.push(node); block.body.push(node);
return in_list ? MAP.splice(block.body) : block; }
} else if (node.init instanceof AST_SimpleStatement) { if (node.init instanceof AST_SimpleStatement) {
node.init = node.init.body; node.init = node.init.body;
} else if (is_empty(node.init)) { } else if (is_empty(node.init)) {
node.init = null; node.init = null;
} }
return node; return !block ? node : in_list ? MAP.splice(block.body) : block;
} }
if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) { if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
descend(node, this); descend(node, this);
@@ -2766,9 +2789,10 @@ 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.has_pure_annotation(compressor) && compressor.pure_funcs(this)) { if (!this.is_expr_pure(compressor)) {
if (is_func_expr(this.expression) if (is_func_expr(this.expression)
&& (!this.expression.name || !this.expression.name.definition().references.length)) { && (!this.expression.name || !this.expression.name.definition().references.length)) {
var node = this.clone(); var node = this.clone();
@@ -3278,6 +3302,7 @@ merge(Compressor.prototype, {
}); });
a.push(var_); a.push(var_);
} }
remove(def.name.definition().orig, def.name);
return a; return a;
}, []); }, []);
if (assignments.length == 0) return null; if (assignments.length == 0) return null;
@@ -3689,7 +3714,10 @@ merge(Compressor.prototype, {
&& (left.operator == "++" || left.operator == "--")) { && (left.operator == "++" || left.operator == "--")) {
left = left.expression; left = left.expression;
} else left = null; } else left = null;
if (!left || is_lhs_read_only(left) || is_ref_of(left, AST_SymbolConst)) { if (!left
|| is_lhs_read_only(left)
|| left.has_side_effects(compressor)
|| is_ref_of(left, AST_SymbolConst)) {
expressions[++i] = cdr; expressions[++i] = cdr;
continue; continue;
} }
@@ -4492,6 +4520,8 @@ merge(Compressor.prototype, {
&& alternative.TYPE === consequent.TYPE && alternative.TYPE === consequent.TYPE
&& consequent.args.length == 1 && consequent.args.length == 1
&& alternative.args.length == 1 && alternative.args.length == 1
&& !(consequent.args[0] instanceof AST_Expansion)
&& !(alternative.args[0] instanceof AST_Expansion)
&& consequent.expression.equivalent_to(alternative.expression) && consequent.expression.equivalent_to(alternative.expression)
&& !consequent.expression.has_side_effects(compressor)) { && !consequent.expression.has_side_effects(compressor)) {
consequent.args[0] = make_node(AST_Conditional, self, { consequent.args[0] = make_node(AST_Conditional, self, {
@@ -4741,7 +4771,7 @@ merge(Compressor.prototype, {
OPT(AST_Function, function(self, compressor){ OPT(AST_Function, function(self, compressor){
tighten_body(self.body, compressor); tighten_body(self.body, compressor);
if (compressor.option("arrows") if (compressor.option("unsafe_arrows")
&& compressor.option("ecma") >= 6 && compressor.option("ecma") >= 6
&& !self.name && !self.name
&& !self.is_generator && !self.is_generator
@@ -4823,7 +4853,8 @@ merge(Compressor.prototype, {
arrow.is_generator = self.is_generator; arrow.is_generator = self.is_generator;
return make_node(AST_ObjectKeyVal, self, { return make_node(AST_ObjectKeyVal, self, {
key: self.key instanceof AST_SymbolMethod ? self.key.name : self.key, key: self.key instanceof AST_SymbolMethod ? self.key.name : self.key,
value: arrow value: arrow,
quote: self.quote,
}); });
} }
return self; return self;
@@ -4849,6 +4880,7 @@ merge(Compressor.prototype, {
name: key, name: key,
}), }),
value: make_node(AST_Accessor, value, value), value: make_node(AST_Accessor, value, value),
quote: self.quote,
}); });
} }
} }

View File

@@ -70,6 +70,7 @@ function minify(files, options) {
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" ]);
var quoted_props;
if (options.mangle) { if (options.mangle) {
options.mangle = defaults(options.mangle, { options.mangle = defaults(options.mangle, {
cache: options.nameCache && (options.nameCache.vars || {}), cache: options.nameCache && (options.nameCache.vars || {}),
@@ -82,11 +83,16 @@ function minify(files, options) {
safari10: false, safari10: false,
toplevel: false, toplevel: false,
}, true); }, true);
if (options.nameCache && options.mangle.properties) { if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") { if (typeof options.mangle.properties != "object") {
options.mangle.properties = {}; options.mangle.properties = {};
} }
if (!("cache" in options.mangle.properties)) { if (options.mangle.properties.keep_quoted) {
quoted_props = options.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
options.mangle.properties.reserved = quoted_props;
}
if (options.nameCache && !("cache" in options.mangle.properties)) {
options.mangle.properties.cache = options.nameCache.props || {}; options.mangle.properties.cache = options.nameCache.props || {};
} }
} }
@@ -129,6 +135,9 @@ function minify(files, options) {
} }
toplevel = options.parse.toplevel; toplevel = options.parse.toplevel;
} }
if (quoted_props) {
reserve_quoted_keys(toplevel, quoted_props);
}
if (options.wrap) { if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap); toplevel = toplevel.wrap_commonjs(options.wrap);
} }

View File

@@ -157,7 +157,7 @@ function OutputStream(options) {
case "\u2029": return "\\u2029"; case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff"; case "\ufeff": return "\\ufeff";
case "\0": case "\0":
return /[0-7]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0"; return /[0-9]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
} }
return s; return s;
}); });
@@ -689,6 +689,7 @@ function OutputStream(options) {
* ==> 20 (side effect, set a := 10 and b := 20) */ * ==> 20 (side effect, set a := 10 and b := 20) */
|| p instanceof AST_Arrow // x => (x, x) || p instanceof AST_Arrow // x => (x, x)
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){})) || p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
|| p instanceof AST_Expansion // [...(a, b)]
; ;
}); });
@@ -1397,7 +1398,7 @@ function OutputStream(options) {
self.expression.print(output); self.expression.print(output);
if (self instanceof AST_New && !need_constructor_parens(self, output)) if (self instanceof AST_New && !need_constructor_parens(self, output))
return; return;
if (self.expression instanceof AST_Lambda) { if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
output.add_mapping(self.start); output.add_mapping(self.start);
} }
output.with_parens(function(){ output.with_parens(function(){

View File

@@ -1522,7 +1522,7 @@ function parse($TEXT, options) {
} }
if (is_expand) { if (is_expand) {
if (!is("punc", "]")) { if (!is("punc", "]")) {
unexpected(); // Must be last element croak("Rest element must be last element");
} }
elements[elements.length - 1] = new AST_Expansion({ elements[elements.length - 1] = new AST_Expansion({
start: expand_token, start: expand_token,
@@ -1547,18 +1547,33 @@ function parse($TEXT, options) {
} else { } else {
expect(","); expect(",");
} }
if (is("expand", "...")) {
is_expand = true;
expand_token = S.token;
used_parameters.mark_spread(S.token);
next();
}
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) { if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) {
used_parameters.add_parameter(S.token); used_parameters.add_parameter(S.token);
elements.push(new AST_ObjectKeyVal({ var value = new symbol_type({
start: prev(), start: S.token,
key: S.token.value, name: S.token.value,
value: new symbol_type({ end: S.token,
start: S.token, });
name: S.token.value, if (is_expand) {
end: S.token elements.push(new AST_Expansion({
}), start: expand_token,
end: prev() expression: value,
})); end: value.end,
}));
} else {
elements.push(new AST_ObjectKeyVal({
start: prev(),
key: S.token.value,
value: value,
end: value.end,
}));
}
next(); next();
} else if (is("punc", "}")) { } else if (is("punc", "}")) {
continue; // Allow trailing hole continue; // Allow trailing hole
@@ -1589,7 +1604,12 @@ function parse($TEXT, options) {
})); }));
} }
} }
if (is("operator", "=")) { if (is_expand) {
if (!is("punc", "}")) {
croak("Rest element must be last element");
}
}
else if (is("operator", "=")) {
used_parameters.mark_default_assignment(S.token); used_parameters.mark_default_assignment(S.token);
next(); next();
elements[elements.length - 1].value = new AST_DefaultAssign({ elements[elements.length - 1].value = new AST_DefaultAssign({
@@ -2143,7 +2163,18 @@ function parse($TEXT, options) {
if (!options.strict && is("punc", "}")) if (!options.strict && is("punc", "}"))
// allow trailing comma // allow trailing comma
break; break;
start = S.token; start = S.token;
if (start.type == "expand") {
next();
a.push(new AST_Expansion({
start: start,
expression: expression(false),
end: prev(),
}));
continue;
}
var name = as_property_name(); var name = as_property_name();
var value; var value;

View File

@@ -85,6 +85,36 @@ function find_builtins(reserved) {
} }
} }
function reserve_quoted_keys(ast, reserved) {
function add(name) {
push_uniq(reserved, name);
}
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) {
add(node.key);
} else if (node instanceof AST_ObjectProperty && node.quote) {
add(node.key.name);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
}
}));
}
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.expressions[node.expressions.length - 1], add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {
addStrings(node.consequent, add);
addStrings(node.alternative, add);
}
return true;
}));
}
function mangle_properties(ast, options) { function mangle_properties(ast, options) {
options = defaults(options, { options = defaults(options, {
builtins: false, builtins: false,
@@ -109,7 +139,6 @@ function mangle_properties(ast, options) {
} }
var regex = options.regex; var regex = options.regex;
var keep_quoted = options.keep_quoted;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled). // note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true' // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
@@ -122,12 +151,11 @@ function mangle_properties(ast, options) {
var names_to_mangle = []; var names_to_mangle = [];
var unmangleable = []; var unmangleable = [];
var to_keep = {};
// step 1: find candidates to mangle // step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node){ ast.walk(new TreeWalker(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
add(node.key, keep_quoted && node.quote); add(node.key);
} }
else if (node instanceof AST_ObjectProperty) { else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above // setter or getter, since KeyVal is handled above
@@ -137,18 +165,14 @@ function mangle_properties(ast, options) {
add(node.property); add(node.property);
} }
else if (node instanceof AST_Sub) { else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted); addStrings(node.property, add);
}
else if (node instanceof AST_ConciseMethod) {
add(node.name.name);
} }
})); }));
// step 2: transform the tree, renaming properties // step 2: transform the tree, renaming properties
return ast.transform(new TreeTransformer(function(node){ return ast.transform(new TreeTransformer(function(node){
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectKeyVal) {
if (!(keep_quoted && node.quote)) node.key = mangle(node.key);
node.key = mangle(node.key);
} }
else if (node instanceof AST_ObjectProperty) { else if (node instanceof AST_ObjectProperty) {
// setter or getter // setter or getter
@@ -157,27 +181,9 @@ function mangle_properties(ast, options) {
else if (node instanceof AST_Dot) { else if (node instanceof AST_Dot) {
node.property = mangle(node.property); node.property = mangle(node.property);
} }
else if (node instanceof AST_Sub) { else if (!options.keep_quoted && node instanceof AST_Sub) {
if (!keep_quoted) node.property = mangleStrings(node.property);
node.property = mangleStrings(node.property);
} }
else if (node instanceof AST_ConciseMethod) {
if (should_mangle(node.name.name)) {
node.name.name = mangle(node.name.name);
}
}
// else if (node instanceof AST_String) {
// if (should_mangle(node.value)) {
// AST_Node.warn(
// "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
// file : node.start.file,
// line : node.start.line,
// col : node.start.col,
// prop : node.value
// }
// );
// }
// }
})); }));
// only function declarations after this line // only function declarations after this line
@@ -193,19 +199,13 @@ function mangle_properties(ast, options) {
} }
function should_mangle(name) { function should_mangle(name) {
if (keep_quoted && name in to_keep) return false;
if (regex && !regex.test(name)) return false; if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false; if (reserved.indexOf(name) >= 0) return false;
return cache.props.has(name) return cache.props.has(name)
|| names_to_mangle.indexOf(name) >= 0; || names_to_mangle.indexOf(name) >= 0;
} }
function add(name, keep) { function add(name) {
if (keep) {
to_keep[name] = true;
return;
}
if (can_mangle(name)) if (can_mangle(name))
push_uniq(names_to_mangle, name); push_uniq(names_to_mangle, name);
@@ -225,19 +225,16 @@ function mangle_properties(ast, options) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_"; var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled) && !(keep_quoted && debug_mangled in to_keep)) { if (can_mangle(debug_mangled)) {
mangled = debug_mangled; mangled = debug_mangled;
} }
} }
// either debug mode is off, or it is on and we could not use the mangled name // either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) { if (!mangled) {
// Note: `can_mangle()` does not check if the name collides with the `to_keep` set
// (filled with quoted properties when `keep_quoted` is set). Make sure we add this
// check so we don't collide with a quoted name.
do { do {
mangled = base54(++cache.cname); mangled = base54(++cache.cname);
} while (!can_mangle(mangled) || keep_quoted && mangled in to_keep); } while (!can_mangle(mangled));
} }
cache.props.set(name, mangled); cache.props.set(name, mangled);
@@ -245,32 +242,6 @@ function mangle_properties(ast, options) {
return mangled; return mangled;
} }
function addStrings(node, keep) {
var out = {};
try {
(function walk(node){
node.walk(new TreeWalker(function(node){
if (node instanceof AST_Sequence) {
walk(node.expressions[node.expressions.length - 1]);
return true;
}
if (node instanceof AST_String) {
add(node.value, keep);
return true;
}
if (node instanceof AST_Conditional) {
walk(node.consequent);
walk(node.alternative);
return true;
}
throw out;
}));
})(node);
} catch(ex) {
if (ex !== out) throw ex;
}
}
function mangleStrings(node) { function mangleStrings(node) {
return node.transform(new TreeTransformer(function(node){ return node.transform(new TreeTransformer(function(node){
if (node instanceof AST_Sequence) { if (node instanceof AST_Sequence) {

View File

@@ -116,7 +116,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node.is_block_scope()) { if (node.is_block_scope()) {
var save_scope = scope; var save_scope = scope;
scope = new AST_Scope(node); node.block_scope = scope = new AST_Scope(node);
scope.init_scope_vars(save_scope); scope.init_scope_vars(save_scope);
if (!(node instanceof AST_Scope)) { if (!(node instanceof AST_Scope)) {
scope.uses_with = save_scope.uses_with; scope.uses_with = save_scope.uses_with;
@@ -203,6 +203,21 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|| node instanceof AST_SymbolLet || node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) { || node instanceof AST_SymbolConst) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node); var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node);
if (!all(def.orig, function(sym) {
if (sym === node) return true;
if (node instanceof AST_SymbolBlockDeclaration) {
return sym instanceof AST_SymbolLambda;
}
return !(sym instanceof AST_SymbolLet || sym instanceof AST_SymbolConst);
})) {
js_error(
node.name + " redeclared",
node.start.file,
node.start.line,
node.start.col,
node.start.pos
);
}
if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2); if (!(node instanceof AST_SymbolFunarg)) mark_export(def, 2);
def.destructuring = in_destructuring; def.destructuring = in_destructuring;
if (defun !== scope) { if (defun !== scope) {
@@ -300,6 +315,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
ref.reference(options); ref.reference(options);
}); });
node.thedef = def; node.thedef = def;
node.reference(options);
return true; return true;
} }
})); }));
@@ -357,7 +373,7 @@ AST_IterationStatement.DEFMETHOD("is_block_scope", return_true);
AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments); AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false; this.uses_arguments = false;
this.def_variable(new AST_SymbolConst({ this.def_variable(new AST_SymbolFunarg({
name: "arguments", name: "arguments",
start: this.start, start: this.start,
end: this.end end: this.end

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.0.26", "version": "3.1.1",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -31,8 +31,8 @@
}, },
"devDependencies": { "devDependencies": {
"acorn": "~5.1.1", "acorn": "~5.1.1",
"mocha": "~3.4.2", "mocha": "~3.5.1",
"semver": "~5.3.0" "semver": "~5.4.1"
}, },
"scripts": { "scripts": {
"test": "node test/run-tests.js" "test": "node test/run-tests.js"

View File

@@ -213,7 +213,7 @@ no_leading_parentheses: {
async_identifiers: { async_identifiers: {
options = { options = {
arrows: true, unsafe_arrows: true,
ecma: 6, ecma: 6,
} }
input: { input: {
@@ -237,7 +237,7 @@ async_identifiers: {
async_function_expression: { async_function_expression: {
options = { options = {
arrows: true, unsafe_arrows: true,
ecma: 6, ecma: 6,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
@@ -262,7 +262,7 @@ async_function_expression: {
issue_27: { issue_27: {
options = { options = {
arrows: true, unsafe_arrows: true,
collapse_vars: true, collapse_vars: true,
ecma: 6, ecma: 6,
unused: true, unused: true,
@@ -283,7 +283,7 @@ issue_27: {
issue_2105_1: { issue_2105_1: {
options = { options = {
arrows: true, unsafe_arrows: true,
collapse_vars: true, collapse_vars: true,
ecma: 6, ecma: 6,
inline: true, inline: true,
@@ -501,7 +501,7 @@ issue_485_crashing_1530: {
issue_2084: { issue_2084: {
options = { options = {
arrows: true, unsafe_arrows: true,
collapse_vars: true, collapse_vars: true,
conditionals: true, conditionals: true,
ecma: 6, ecma: 6,
@@ -577,6 +577,7 @@ concise_methods_with_computed_property2: {
async_object_literal: { async_object_literal: {
options = { options = {
arrows: true, arrows: true,
unsafe_arrows: true,
ecma: 6, ecma: 6,
evaluate: true, evaluate: true,
} }
@@ -597,3 +598,35 @@ async_object_literal: {
}; };
} }
} }
issue_2271: {
options = {
arrows: true,
ecma: 6,
evaluate: true,
unsafe_arrows: false,
}
input: {
var Foo = function() {};
Foo.prototype.set = function(value) {
this.value = value;
return this;
}
Foo.prototype.print = function() {
console.log(this.value);
}
new Foo().set("PASS").print();
}
expect: {
var Foo = function() {};
Foo.prototype.set = function(value) {
this.value = value;
return this;
}
Foo.prototype.print = function() {
console.log(this.value);
}
new Foo().set("PASS").print();
}
expect_stdout: "PASS"
}

View File

@@ -13,7 +13,7 @@ ascii_only_true: {
"\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff"; "\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff";
} }
} }
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0e\\x0f"+"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f"+\' !"# ... }~\\x7f\\x80\\x81 ... \\xfe\\xff\\u0fff\\uffff\'}' expect_exact: 'function f(){return"\\x000\\x001\\x007\\x008\\0"+"\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0e\\x0f"+"\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f"+\' !"# ... }~\\x7f\\x80\\x81 ... \\xfe\\xff\\u0fff\\uffff\'}'
} }
ascii_only_false: { ascii_only_false: {
@@ -31,5 +31,5 @@ ascii_only_false: {
"\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff"; "\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff";
} }
} }
expect_exact: 'function f(){return"\\x000\\x001\\x007\\08\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}' expect_exact: 'function f(){return"\\x000\\x001\\x007\\x008\\0"+"\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f"+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"+\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\'}'
} }

View File

@@ -2548,3 +2548,180 @@ duplicate_argname: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_2250_1: {
options = {
collapse_vars: true,
conditionals: true,
reduce_vars: true,
}
input: {
function f(x) {
if (x) {
const a = foo();
x(a);
}
}
function g(x) {
if (x) {
let a = foo();
x(a);
}
}
function h(x) {
if (x) {
var a = foo();
x(a);
}
}
}
expect: {
function f(x) {
x && x(foo());
}
function g(x) {
x && x(foo());
}
function h(x) {
x && x(foo());
}
}
}
issue_2250_2: {
options = {
collapse_vars: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
{
const foo = function(){};
foo(bar());
}
{
let foo = function(){};
foo(bar());
}
{
var foo = function(){};
foo(bar());
}
}
expect: {
bar();
bar();
bar();
}
}
issue_2298: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
function f() {
var a = undefined;
var undefined = a++;
try {
!function g(b) {
b[1] = "foo";
}();
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
f();
}();
}
expect: {
!function() {
(function() {
var a = undefined;
var undefined = a++;
try {
!function(b) {
(void 0)[1] = "foo";
}();
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
})();
}();
}
expect_stdout: "PASS"
}
issue_2313_1: {
options = {
collapse_vars: true,
conditionals: true,
}
input: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
if (this.c) console.log(a, b);
}
}
foo.d();
}
expect: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
this.c && console.log(a, b);
}
}
foo.d();
}
expect_stdout: "2 1"
}
issue_2313_2: {
options = {
collapse_vars: true,
}
input: {
var c = 0;
!function a() {
a && c++;
var a = 0;
a && c++;
}();
console.log(c);
}
expect: {
var c = 0;
!function a() {
a && c++;
var a = 0;
a && c++;
}();
console.log(c);
}
expect_stdout: "0"
}

View File

@@ -21,7 +21,7 @@ concat_1: {
var c = 1 + x() + 2 + "boo"; var c = 1 + x() + 2 + "boo";
var d = 1 + x() + 2 + 3 + "boo"; var d = 1 + x() + 2 + 3 + "boo";
var e = 1 + x() + 2 + "X3boo"; var e = 1 + x() + 2 + "X3boo";
var f = "\x00360\08\0"; var f = "\x00360\x008\0";
} }
} }

View File

@@ -388,18 +388,54 @@ issue_2233_1: {
Array.isArray; Array.isArray;
Boolean; Boolean;
console.log; console.log;
Date;
decodeURI;
decodeURIComponent;
encodeURI;
encodeURIComponent;
Error.name; Error.name;
escape;
eval;
EvalError;
Function.length; Function.length;
isFinite;
isNaN;
JSON;
Math.random; Math.random;
Number.isNaN; Number.isNaN;
parseFloat;
parseInt;
RegExp; RegExp;
Object.defineProperty; Object.defineProperty;
String.fromCharCode; String.fromCharCode;
RangeError;
ReferenceError;
SyntaxError;
TypeError;
unescape;
URIError;
} }
expect: {} expect: {}
expect_stdout: true expect_stdout: true
} }
global_timeout_and_interval_symbols: {
options = {
pure_getters: "strict",
side_effects: true,
unsafe: true,
}
input: {
// These global symbols do not exist in the test sandbox
// and must be tested separately.
clearInterval;
clearTimeout;
setInterval;
setTimeout;
}
expect: {}
}
issue_2233_2: { issue_2233_2: {
options = { options = {
pure_getters: "strict", pure_getters: "strict",
@@ -456,3 +492,71 @@ issue_2233_3: {
UndeclaredGlobal; UndeclaredGlobal;
} }
} }
global_fns: {
options = {
side_effects: true,
unsafe: true,
}
input: {
Boolean(1, 2);
decodeURI(1, 2);
decodeURIComponent(1, 2);
Date(1, 2);
encodeURI(1, 2);
encodeURIComponent(1, 2);
Error(1, 2);
escape(1, 2);
EvalError(1, 2);
isFinite(1, 2);
isNaN(1, 2);
Number(1, 2);
Object(1, 2);
parseFloat(1, 2);
parseInt(1, 2);
RangeError(1, 2);
ReferenceError(1, 2);
String(1, 2);
SyntaxError(1, 2);
TypeError(1, 2);
unescape(1, 2);
URIError(1, 2);
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect: {
try {
Function(1, 2);
} catch (e) {
console.log(e.name);
}
try {
RegExp(1, 2);
} catch (e) {
console.log(e.name);
}
try {
Array(NaN);
} catch (e) {
console.log(e.name);
}
}
expect_stdout: [
"SyntaxError",
"SyntaxError",
"RangeError",
]
}

View File

@@ -65,10 +65,10 @@ nested_destructuring_objects: {
} }
input: { input: {
const [{a},b] = c; const [{a},b] = c;
let [{a},b] = c; let [{d},e] = f;
var [{a},b] = c; var [{g},h] = i;
} }
expect_exact: 'const[{a},b]=c;let[{a},b]=c;var[{a},b]=c;'; expect_exact: 'const[{a},b]=c;let[{d},e]=f;var[{g},h]=i;';
} }
destructuring_constdef_in_loops: { destructuring_constdef_in_loops: {
@@ -274,8 +274,8 @@ reduce_vars: {
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}}; var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}}); ({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}});
const [{a},b] = c; const [{a},b] = c;
let [{a},b] = c; let [{d},e] = f;
var [{a},b] = c; var [{g},h] = i;
[{a},b] = c; [{a},b] = c;
for (const [x,y] in pairs); for (const [x,y] in pairs);
for (let [x,y] in pairs); for (let [x,y] in pairs);
@@ -292,8 +292,8 @@ reduce_vars: {
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}}; var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}}); ({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}});
const [{a},b] = c; const [{a},b] = c;
let [{a},b] = c; let [{d},e] = f;
var [{a},b] = c; var [{g},h] = i;
[{a},b] = c; [{a},b] = c;
for (const [x,y] in pairs); for (const [x,y] in pairs);
for (let [x,y] in pairs); for (let [x,y] in pairs);

View File

@@ -1513,3 +1513,25 @@ issue_2226_3: {
} }
expect_stdout: "3" expect_stdout: "3"
} }
issue_2288: {
options = {
unused: true,
}
beautify = {
beautify: true,
}
input: {
function foo(o) {
for (var j = o.a, i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
}
}
expect: {
function foo(o) {
o.a;
for (i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
}
}
}

View File

@@ -26,3 +26,46 @@ expand_parameters: {
expect_exact: "(function(a,...b){});(function(...args){});" expect_exact: "(function(a,...b){});(function(...args){});"
} }
avoid_spread_in_ternary: {
options = {
comparisons: true,
conditionals: true,
evaluate: true,
}
input: {
function print(...x) {
console.log(...x);
}
var a = [1, 2], b = [3, 4];
if (Math)
print(a);
else
print(b);
if (Math)
print(...a);
else
print(b);
if (Math.no_such_property)
print(a);
else
print(...b);
}
expect: {
function print(...x) {
console.log(...x);
}
var a = [ 1, 2 ], b = [ 3, 4 ];
print(Math ? a : b);
Math ? print(...a) : print(b);
Math.no_such_property ? print(a) : print(...b);
}
expect_stdout: [
"[ 1, 2 ]",
"1 2",
"3 4",
]
node_version: ">=6"
}

View File

@@ -211,13 +211,13 @@ export_statement: {
} }
input: { input: {
export default 1 + 2; export default 1 + 2;
export var foo = 4; export var a = 4;
export let foo = 6; export let b = 6;
export const foo = 6; export const c = 6;
export function foo() {}; export function d() {};
export class foo { }; export class e {};
} }
expect_exact: "export default 3;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};" expect_exact: "export default 3;export var a=4;export let b=6;export const c=6;export function d(){};export class e{};"
} }
export_default_object_expression: { export_default_object_expression: {
@@ -695,3 +695,112 @@ export_default_class_decl: {
} }
expect_exact: "export default class Car{};export class Cab{};" expect_exact: "export default class Car{};export class Cab{};"
} }
object_rest_spread: {
mangle = {
toplevel: true,
}
input: {
var { w: w1, ...V } = { w: 7, x: 1, y: 2 }; console.log(w1, V);
let { w: w2, ...L } = { w: 8, x: 3, y: 4 }; console.log(w2, L);
const { w: w3, ...C } = { w: 9, x: 5, y: 6 }; console.log(w3, C);
let b;
({ b, ...V } = { a: 1, b: 2, c: 3 }); console.log(V);
({ b, ...L } = { a: 4, b: 5, c: 6 }); console.log(L);
(function({ y, ...p }){ console.log(p); })({ x: 1, y: 2, z: 3 });
(({ y, ...p }) => { console.log(p); })({ x: 4, y: 5, z: 6 });
const T = { a: 1, b: 2 }; console.log({ ...T, w: 0, ...{}, ...L, ...{K: 9} });
}
expect: {
var { w: o, ...l } = { w: 7, x: 1, y: 2 }; console.log(o, l);
let { w: c, ...n } = { w: 8, x: 3, y: 4 }; console.log(c, n);
const { w: e, ...s } = { w: 9, x: 5, y: 6 }; console.log(e, s);
let g;
({ b: g, ...l } = { a: 1, b: 2, c: 3 }); console.log(l);
({ b: g, ...n } = { a: 4, b: 5, c: 6 }); console.log(n);
(function({ y: o, ...l }) { console.log(l); })({ x: 1, y: 2, z: 3 });
(({ y: o, ...l }) => { console.log(l); })({ x: 4, y: 5, z: 6 });
const w = { a: 1, b: 2 }; console.log({ ...w, w: 0, ...{}, ...n, ...{ K: 9 } });
}
}
object_spread_unsafe: {
options = {
collapse_vars: true,
evaluate: true,
join_vars: true,
passes: 3,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
mangle = {
toplevel: true,
}
input: {
var o1 = { x: 1, y: 2 };
var o2 = { x: 3, z: 4 };
var cloned = { ...o1 };
var merged = { ...o1, ...o2 };
console.log(cloned, merged);
}
expect: {
var o = { x: 1, y: 2 }, l = { ...o }, x = { ...o, ...{ x: 3, z: 4 } };
console.log(l, x);
}
}
array_spread_of_sequence: {
mangle = {
toplevel: true,
}
input: {
var a = [1];
console.log([...(a, a)]);
console.log([...a, a]);
console.log([...(a || a)]);
console.log([...a || a]);
}
expect: {
var o = [1];
console.log([...(o, o)]);
console.log([...o, o]);
console.log([...o || o]);
console.log([...o || o]);
}
expect_stdout: [
"[ 1 ]",
"[ 1, [ 1 ] ]",
"[ 1 ]",
"[ 1 ]",
]
node_version: ">=6"
}
object_spread_of_sequence: {
mangle = {
toplevel: true,
}
input: {
var a = {x: 1};
console.log({ ...(a, a) });
console.log({ ...a, a });
console.log({ ...(a || a) });
console.log({ ...a || a });
}
expect: {
var o = { x: 1 };
console.log({ ...(o, o) });
console.log({ ...o, a: o });
console.log({ ...o || o });
console.log({ ...o || o });
}
}

View File

@@ -88,3 +88,24 @@ sequences_funs: {
} }
} }
} }
issue_2295: {
options = {
collapse_vars: true,
hoist_vars: true,
}
input: {
function foo(o) {
var a = o.a;
if (a) return a;
var a = 1;
}
}
expect: {
function foo(o) {
var a = o.a;
if (a) return a;
a = 1;
}
}
}

View File

@@ -96,6 +96,13 @@ pure_function_calls_toplevel: {
})(); })();
})(); })();
// pure top-level calls will be dropped regardless of the leading comments position
var MyClass = /*#__PURE__*//*@class*/(function(){
function MyClass() {}
MyClass.prototype.method = function() {};
return MyClass;
})();
// comment #__PURE__ comment // comment #__PURE__ comment
bar(), baz(), quux(); bar(), baz(), quux();
a.b(), /* @__PURE__ */ c.d.e(), f.g(); a.b(), /* @__PURE__ */ c.d.e(), f.g();
@@ -110,10 +117,12 @@ pure_function_calls_toplevel: {
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:92,37]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:92,37]",
"WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:92,16]", "WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:92,16]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:90,8]", "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:90,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:100,8]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:107,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:101,31]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:108,31]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:84,33]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:84,33]",
"WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]", "WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:100,45]",
"WARN: Dropping unused variable MyClass [test/compress/issue-1261.js:100,12]",
] ]
} }
@@ -148,29 +157,29 @@ should_warn: {
baz(); baz();
} }
expect_warnings: [ expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,61]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,61]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,23]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,23]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:128,23]", "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:137,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:129,23]", "WARN: Boolean || always true [test/compress/issue-1261.js:138,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:129,23]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:138,23]",
"WARN: Condition always true [test/compress/issue-1261.js:129,23]", "WARN: Condition always true [test/compress/issue-1261.js:138,23]",
"WARN: Condition left of || always true [test/compress/issue-1261.js:130,8]", "WARN: Condition left of || always true [test/compress/issue-1261.js:139,8]",
"WARN: Condition always true [test/compress/issue-1261.js:130,8]", "WARN: Condition always true [test/compress/issue-1261.js:139,8]",
"WARN: Boolean && always false [test/compress/issue-1261.js:131,23]", "WARN: Boolean && always false [test/compress/issue-1261.js:140,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:131,23]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:140,23]",
"WARN: Condition always false [test/compress/issue-1261.js:131,23]", "WARN: Condition always false [test/compress/issue-1261.js:140,23]",
"WARN: Condition left of && always false [test/compress/issue-1261.js:132,8]", "WARN: Condition left of && always false [test/compress/issue-1261.js:141,8]",
"WARN: Condition always false [test/compress/issue-1261.js:132,8]", "WARN: Condition always false [test/compress/issue-1261.js:141,8]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:133,23]", "WARN: + in boolean context always true [test/compress/issue-1261.js:142,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:133,23]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
"WARN: Condition always true [test/compress/issue-1261.js:133,23]", "WARN: Condition always true [test/compress/issue-1261.js:142,23]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:134,8]", "WARN: + in boolean context always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:134,31]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,31]",
"WARN: Condition always true [test/compress/issue-1261.js:134,8]", "WARN: Condition always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:135,23]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:136,24]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:145,24]",
"WARN: Condition always true [test/compress/issue-1261.js:136,8]", "WARN: Condition always true [test/compress/issue-1261.js:145,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,31]", "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:146,31]",
"WARN: Condition always false [test/compress/issue-1261.js:137,8]", "WARN: Condition always false [test/compress/issue-1261.js:146,8]",
] ]
} }

View File

@@ -1,6 +1,8 @@
issue_1321_no_debug: { issue_1321_no_debug: {
mangle_props = { mangle = {
keep_quoted: true properties: {
keep_quoted: true,
},
} }
input: { input: {
var x = {}; var x = {};
@@ -10,17 +12,19 @@ issue_1321_no_debug: {
} }
expect: { expect: {
var x = {}; var x = {};
x.o = 1; x.x = 1;
x["a"] = 2 * x.o; x["a"] = 2 * x.x;
console.log(x.o, x["a"]); console.log(x.x, x["a"]);
} }
expect_stdout: true expect_stdout: true
} }
issue_1321_debug: { issue_1321_debug: {
mangle_props = { mangle = {
keep_quoted: true, properties: {
debug: "" debug: "",
keep_quoted: true,
},
} }
input: { input: {
var x = {}; var x = {};
@@ -30,16 +34,18 @@ issue_1321_debug: {
} }
expect: { expect: {
var x = {}; var x = {};
x.o = 1; x.x = 1;
x["_$foo$_"] = 2 * x.o; x["_$foo$_"] = 2 * x.x;
console.log(x.o, x["_$foo$_"]); console.log(x.x, x["_$foo$_"]);
} }
expect_stdout: true expect_stdout: true
} }
issue_1321_with_quoted: { issue_1321_with_quoted: {
mangle_props = { mangle = {
keep_quoted: false properties: {
keep_quoted: false,
},
} }
input: { input: {
var x = {}; var x = {};
@@ -49,9 +55,9 @@ issue_1321_with_quoted: {
} }
expect: { expect: {
var x = {}; var x = {};
x.o = 1; x.x = 1;
x["x"] = 2 * x.o; x["o"] = 2 * x.x;
console.log(x.o, x["x"]); console.log(x.x, x["o"]);
} }
expect_stdout: true expect_stdout: true
} }

View File

@@ -1,5 +1,7 @@
mangle_props: { mangle_props: {
mangle_props = {} mangle = {
properties: true,
}
input: { input: {
var obj = { var obj = {
undefined: 1, undefined: 1,
@@ -54,10 +56,12 @@ mangle_props: {
} }
numeric_literal: { numeric_literal: {
mangle = {
properties: true,
}
beautify = { beautify = {
beautify: true, beautify: true,
} }
mangle_props = {}
input: { input: {
var obj = { var obj = {
0: 0, 0: 0,

View File

@@ -36,9 +36,10 @@ compress_new_function_with_destruct: {
compress_new_function_with_destruct_arrows: { compress_new_function_with_destruct_arrows: {
options = { options = {
arrows: true, arrows: true,
unsafe_arrows: true,
unsafe: true, unsafe: true,
unsafe_Func: true, unsafe_Func: true,
ecma: 6 ecma: 6,
} }
beautify = { beautify = {
ecma: 6 ecma: 6

View File

@@ -1,6 +1,8 @@
dont_reuse_prop: { dont_reuse_prop: {
mangle_props = { mangle = {
regex: /asd/ properties: {
regex: /asd/,
},
} }
input: { input: {
"aaaaaaaaaabbbbb"; "aaaaaaaaaabbbbb";
@@ -20,8 +22,10 @@ dont_reuse_prop: {
} }
unmangleable_props_should_always_be_reserved: { unmangleable_props_should_always_be_reserved: {
mangle_props = { mangle = {
regex: /asd/ properties: {
regex: /asd/,
},
} }
input: { input: {
"aaaaaaaaaabbbbb"; "aaaaaaaaaabbbbb";

View File

@@ -284,9 +284,11 @@ concise_methods_with_various_property_names: {
} }
concise_methods_and_mangle_props: { concise_methods_and_mangle_props: {
mangle_props = { mangle = {
regex: /_/ properties: {
}; regex: /_/,
},
}
input: { input: {
function x() { function x() {
obj = { obj = {

View File

@@ -142,11 +142,11 @@ destructuring_arguments_3: {
} }
input: { input: {
function fn3({x: {y: {z: {} = 42}}}) {} function fn3({x: {y: {z: {} = 42}}}) {}
const { cover = (function () {}), xCover = (0, function() {}) } = {}; const { a = (function () {}), b = (0, function() {}) } = {};
let { cover = (function () {}), xCover = (0, function() {}) } = {}; let { c = (function () {}), d = (0, function() {}) } = {};
var { cover = (function () {}), xCover = (0, function() {}) } = {}; var { e = (function () {}), f = (0, function() {}) } = {};
} }
expect_exact: "function fn3({x:{y:{z:{}=42}}}){}const{cover=function(){},xCover=(0,function(){})}={};let{cover=function(){},xCover=(0,function(){})}={};var{cover=function(){},xCover=(0,function(){})}={};" expect_exact: "function fn3({x:{y:{z:{}=42}}}){}const{a=function(){},b=(0,function(){})}={};let{c=function(){},d=(0,function(){})}={};var{e=function(){},f=(0,function(){})}={};"
} }
default_arguments: { default_arguments: {

View File

@@ -128,9 +128,11 @@ evaluate_string_length: {
} }
mangle_properties: { mangle_properties: {
mangle_props = { mangle = {
keep_quoted: false properties: {
}; keep_quoted: false,
},
}
input: { input: {
a["foo"] = "bar"; a["foo"] = "bar";
a.color = "red"; a.color = "red";
@@ -139,11 +141,11 @@ mangle_properties: {
a['run']({color: "blue", foo: "baz"}); a['run']({color: "blue", foo: "baz"});
} }
expect: { expect: {
a["o"] = "bar"; a["a"] = "bar";
a.a = "red"; a.b = "red";
x = {r: 10}; x = {o: 10};
a.b(x.r, a.o); a.r(x.o, a.a);
a['b']({a: "blue", o: "baz"}); a['r']({b: "blue", a: "baz"});
} }
} }
@@ -151,9 +153,11 @@ mangle_unquoted_properties: {
options = { options = {
properties: false properties: false
} }
mangle_props = { mangle = {
builtins: true, properties: {
keep_quoted: true builtins: true,
keep_quoted: true,
},
} }
beautify = { beautify = {
beautify: false, beautify: false,
@@ -182,24 +186,26 @@ mangle_unquoted_properties: {
function f1() { function f1() {
a["foo"] = "bar"; a["foo"] = "bar";
a.color = "red"; a.color = "red";
a.o = 2; a.r = 2;
x = {"bar": 10, f: 7}; x = {"bar": 10, b: 7};
a.f = 9; a.b = 9;
} }
function f2() { function f2() {
a.foo = "bar"; a.foo = "bar";
a['color'] = "red"; a['color'] = "red";
x = {bar: 10, f: 7}; x = {bar: 10, b: 7};
a.f = 9; a.b = 9;
a.o = 3; a.r = 3;
} }
} }
} }
mangle_debug: { mangle_debug: {
mangle_props = { mangle = {
debug: "" properties: {
}; debug: "",
},
}
input: { input: {
a.foo = "bar"; a.foo = "bar";
x = { baz: "ban" }; x = { baz: "ban" };
@@ -211,9 +217,11 @@ mangle_debug: {
} }
mangle_debug_true: { mangle_debug_true: {
mangle_props = { mangle = {
debug: true properties: {
}; debug: true,
},
}
input: { input: {
a.foo = "bar"; a.foo = "bar";
x = { baz: "ban" }; x = { baz: "ban" };
@@ -225,9 +233,11 @@ mangle_debug_true: {
} }
mangle_debug_suffix: { mangle_debug_suffix: {
mangle_props = { mangle = {
debug: "XYZ" properties: {
}; debug: "XYZ",
},
}
input: { input: {
a.foo = "bar"; a.foo = "bar";
x = { baz: "ban" }; x = { baz: "ban" };
@@ -242,11 +252,13 @@ mangle_debug_suffix_keep_quoted: {
options = { options = {
properties: false properties: false
} }
mangle_props = { mangle = {
builtins: true, properties: {
keep_quoted: true, builtins: true,
debug: "XYZ", debug: "XYZ",
reserved: [] keep_quoted: true,
reserved: [],
},
} }
beautify = { beautify = {
beautify: false, beautify: false,
@@ -870,3 +882,85 @@ issue_2208_9: {
expect_stdout: "42" expect_stdout: "42"
node_version: ">=4" node_version: ">=4"
} }
methods_keep_quoted_true: {
options = {
arrows: true,
ecma: 6,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
class C { "Quoted"(){} Unquoted(){} }
f1({ "Quoted"(){}, Unquoted(){}, "Prop": 3 });
f2({ "Quoted": function(){} });
f3({ "Quoted": ()=>{} });
}
expect_exact: "class C{Quoted(){}o(){}}f1({Quoted(){},o(){},Prop:3});f2({Quoted(){}});f3({Quoted(){}});"
}
methods_keep_quoted_false: {
options = {
arrows: true,
ecma: 6,
}
mangle = {
properties: {
keep_quoted: false,
},
}
input: {
class C { "Quoted"(){} Unquoted(){} }
f1({ "Quoted"(){}, Unquoted(){}, "Prop": 3 });
f2({ "Quoted": function(){} });
f3({ "Quoted": ()=>{} });
}
expect_exact: "class C{o(){}d(){}}f1({o(){},d(){},e:3});f2({o(){}});f3({o(){}});"
}
methods_keep_quoted_from_dead_code: {
options = {
arrows: true,
booleans: true,
conditionals: true,
dead_code: true,
ecma: 6,
evaluate: true,
reduce_vars: true,
side_effects: true,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
class C { Quoted(){} Unquoted(){} }
f1({ Quoted(){}, Unquoted(){}, "Prop": 3 });
f2({ Quoted: function(){} });
f3({ Quoted: ()=>{} });
0 && obj["Quoted"];
}
expect_exact: "class C{Quoted(){}o(){}}f1({Quoted(){},o(){},Prop:3});f2({Quoted(){}});f3({Quoted(){}});"
}
issue_2256: {
options = {
side_effects: true,
}
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
({ "keep": 1 });
g.keep = g.change;
}
expect: {
g.keep = g.g;
}
}

View File

@@ -385,3 +385,338 @@ set_mutable_2: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_2265_1: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
({ ...{} }).p;
({ ...g }).p;
}
expect: {
({ ...g }).p;
}
}
issue_2265_2: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = {
get b() {
throw 0;
}
};
({...a}).b;
}
expect: {
var a = {
get b() {
throw 0;
}
};
({...a}).b;
}
}
issue_2265_3: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = {
set b() {
throw 0;
}
};
({...a}).b;
}
expect: {}
}
issue_2265_4: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = { b: 1 };
({...a}).b;
}
expect: {}
}
issue_2313_1: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
return console.log(1), {
y: function() {
return console.log(2), {
z: 0
};
}
};
}
x().y().z++,
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: true,
sequences: true,
side_effects: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
return console.log(1), {
y: function() {
return console.log(2), {
z: 0
};
}
};
}
x().y().z++,
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_3: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: "strict",
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_4: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: true,
}
input: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
if (x().y().z) {
console.log(3);
}
}
expect: {
function x() {
console.log(1);
return {
y: function() {
console.log(2);
return {
z: 0
};
}
};
}
x().y().z++;
x().y().z && console.log(3);
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2313_5: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
x().y++;
x().y;
}
expect: {
x().y++;
x().y;
}
}
issue_2313_6: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
x().y++;
x().y;
}
expect: {
x().y++;
x();
}
}
issue_2313_7: {
options = {
collapse_vars: true,
conditionals: true,
pure_getters: true,
}
input: {
var a = 0, b = 0;
class foo {
get c() {
a++;
return 42;
}
set c(c) {
b++;
}
}
class bar extends foo {
d() {
super.c++;
if (super.c) console.log(a, b);
}
}
new bar().d();
}
expect: {
var a = 0, b = 0;
class foo {
get c() {
a++;
return 42;
}
set c(c) {
b++;
}
}
class bar extends foo {
d() {
super.c++;
super.c && console.log(a, b);
}
}
new bar().d();
}
expect_stdout: "2 1"
node_version: ">=6"
}

View File

@@ -325,3 +325,69 @@ issue_2120_2: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_2254_1: {
mangle = {
ie8: false,
}
input: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(s) {
try {
throw "FAIL";
} catch (e) {
return s;
}
}
}
expect: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(e) {
try {
throw "FAIL";
} catch (t) {
return e;
}
}
}
expect_stdout: "PASS"
}
issue_2254_2: {
mangle = {
ie8: true,
}
input: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(s) {
try {
throw "FAIL";
} catch (e) {
return s;
}
}
}
expect: {
"eeeeee";
try {
console.log(f("PASS"));
} catch (e) {}
function f(t) {
try {
throw "FAIL";
} catch (e) {
return t;
}
}
}
expect_stdout: "PASS"
}

View File

@@ -763,3 +763,44 @@ issue_2062: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
issue_2313: {
options = {
cascade: true,
sequences: true,
side_effects: true,
}
input: {
var a = 0, b = 0;
var foo = {
get c() {
a++;
return 42;
},
set c(c) {
b++;
},
d: function() {
this.c++;
if (this.c) console.log(a, b);
}
}
foo.d();
}
expect: {
var a = 0, b = 0;
var foo = {
get c() {
return a++, 42;
},
set c(c) {
b++;
},
d: function() {
if (this.c++, this.c) console.log(a, b);
}
}
foo.d();
}
expect_stdout: "2 1"
}

View File

@@ -8,6 +8,7 @@ exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties; exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify; exports["minify"] = minify;
exports["parse"] = parse; exports["parse"] = parse;
exports["reserve_quoted_keys"] = reserve_quoted_keys;
exports["string_template"] = string_template; exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer; exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier; exports["is_identifier"] = is_identifier;

View File

@@ -0,0 +1,10 @@
function foo() {
return function() {
console.log("PASS");
};
}
(function() {
var f = foo();
f();
})();

View File

@@ -666,6 +666,25 @@ describe("bin/uglifyjs", function () {
return JSON.stringify(map).replace(/"/g, '\\"'); return JSON.stringify(map).replace(/"/g, '\\"');
} }
}); });
it("Should include function calls in source map", function(done) {
var command = [
uglifyjscmd,
"test/input/issue-2310/input.js",
"-c",
"--source-map", "url=inline",
].join(" ");
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, [
'function foo(){return function(){console.log("PASS")}}foo()();',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMjMxMC9pbnB1dC5qcyJdLCJuYW1lcyI6WyJmb28iLCJjb25zb2xlIiwibG9nIiwiZiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsTUFDTCxPQUFPLFdBQ0hDLFFBQVFDLElBQUksU0FLUkYsS0FDUkcifQ==",
""
].join("\n"));
done();
});
});
it("Should dump AST as JSON", function(done) { it("Should dump AST as JSON", function(done) {
var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast"; var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast";
exec(command, function (err, stdout) { exec(command, function (err, stdout) {

View File

@@ -253,13 +253,19 @@ describe("Left-hand side expressions", function () {
// Multiple spreads are not allowed in destructuring array // Multiple spreads are not allowed in destructuring array
expect("[...a, ...b] = [1, 2, 3, 4]", "Spread must the be last element in destructuring array"); expect("[...a, ...b] = [1, 2, 3, 4]", "Spread must the be last element in destructuring array");
// Spread in obvious object pattern // Array spread must be last in destructuring declaration
expect("({...a} = foo)", "Unexpected token: expand (...)"); expect("let [ ...x, a ] = o;", "Rest element must be last element");
// Only one spread per destructuring array declaration
expect("let [ a, ...x, ...y ] = o;", "Rest element must be last element");
// Spread in block should not be allowed // Spread in block should not be allowed
expect("{...a} = foo", "Unexpected token: expand (...)"); expect("{...a} = foo", "Unexpected token: expand (...)");
// Not in standard yet // Object spread must be last in destructuring declaration
expect("let foo = {bar: 42}, bar; bar = {...foo}", "Unexpected token: expand (...)"); expect("let { ...x, a } = o;", "Rest element must be last element");
// Only one spread per destructuring declaration
expect("let { a, ...x, ...y } = o;", "Rest element must be last element");
}); });
}); });

View File

@@ -124,6 +124,17 @@ describe("minify", function() {
assert.strictEqual(result.code, assert.strictEqual(result.code,
'a["foo"]="bar",a.a="red",x={"bar":10};'); 'a["foo"]="bar",a.a="red",x={"bar":10};');
}); });
it("Should not mangle quoted property within dead code", function() {
var result = Uglify.minify('({ "keep": 1 }); g.keep = g.change;', {
mangle: {
properties: {
keep_quoted: true
}
}
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "g.keep=g.g;");
});
}); });
describe("inSourceMap", function() { describe("inSourceMap", function() {
@@ -280,4 +291,34 @@ describe("minify", function() {
assert.strictEqual(result.code, "alert({bar:42});"); assert.strictEqual(result.code, "alert({bar:42});");
}); });
}); });
describe("duplicated block-scoped declarations", function() {
[
"let a=1;let a=2;",
"let a=1;var a=2;",
"var a=1;let a=2;",
"let[a]=[1];var a=2;",
"let a=1;var[a]=[2];",
"let[a]=[1];var[a]=[2];",
"const a=1;const a=2;",
"const a=1;var a=2;",
"var a=1;const a=2;",
"const[a]=[1];var a=2;",
"const a=1;var[a]=[2];",
"const[a]=[1];var[a]=[2];",
].forEach(function(code) {
it(code, function() {
var result = Uglify.minify(code, {
compress: false,
mangle: false
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, code);
result = Uglify.minify(code);
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: a redeclared");
});
});
});
}); });

View File

@@ -61,9 +61,9 @@ describe("String literals", function() {
var tests = [ var tests = [
['"\\76";', ';">";'], ['"\\76";', ';">";'],
['"\\0"', '"\\0";'], ['"\\0"', '"\\0";'],
['"\\08"', '"\\08";'], ['"\\08"', '"\\x008";'],
['"\\008"', '"\\08";'], ['"\\008"', '"\\x008";'],
['"\\0008"', '"\\08";'], ['"\\0008"', '"\\x008";'],
['"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";'], ['"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";'],
['"use\\\n strict";\n"\\07";', ';"use strict";"\07";'] ['"use\\\n strict";\n"\\07";', ';"use strict";"\07";']
]; ];
@@ -75,8 +75,8 @@ describe("String literals", function() {
}); });
it("Should not throw error when digit is 8 or 9", function() { it("Should not throw error when digit is 8 or 9", function() {
assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\08";'); assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\x008";');
assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\09";'); assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\x009";');
}); });
it("Should not unescape unpaired surrogates", function() { it("Should not unescape unpaired surrogates", function() {

View File

@@ -146,8 +146,9 @@ describe("Unicode", function() {
if (semver.satisfies(process.version, ">=4")) { if (semver.satisfies(process.version, ">=4")) {
it("Should not unescape unpaired surrogates", function() { it("Should not unescape unpaired surrogates", function() {
this.timeout(5000);
var code = []; var code = [];
for (var i = 0; i <= 0xFFFF; i++) { for (var i = 0; i <= 0x20001; i++) {
code.push("\\u{" + i.toString(16) + "}"); code.push("\\u{" + i.toString(16) + "}");
} }
code = '"' + code.join() + '"'; code = '"' + code.join() + '"';
@@ -162,7 +163,7 @@ describe("Unicode", function() {
ecma: ecma ecma: ecma
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.ok(code.length > result.code.length); if (ecma > 5) assert.ok(code.length > result.code.length);
assert.strictEqual(eval(code), eval(result.code)); assert.strictEqual(eval(code), eval(result.code));
}); });
}); });

View File

@@ -111,18 +111,22 @@ function run_compress_tests() {
}; };
if (!options.warnings) options.warnings = true; if (!options.warnings) options.warnings = true;
} }
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
var quoted_props = test.mangle.properties.reserved;
if (!Array.isArray(quoted_props)) quoted_props = [];
test.mangle.properties.reserved = quoted_props;
U.reserve_quoted_keys(input, quoted_props);
}
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);
if (test.mangle || test.mangle_props) { if (test.mangle) {
U.base54.reset(); U.base54.reset();
output.compute_char_frequency(test.mangle); output.compute_char_frequency(test.mangle);
}
if (test.mangle) {
output.mangle_names(test.mangle); output.mangle_names(test.mangle);
} if (test.mangle.properties) {
if (test.mangle_props) { output = U.mangle_properties(output, test.mangle.properties);
output = U.mangle_properties(output, test.mangle_props); }
} }
output = make_code(output, output_options); output = make_code(output, output_options);
if (expect != output) { if (expect != output) {