Compare commits

..

45 Commits

Author SHA1 Message Date
Alex Lam S.L
25441d44f6 v3.16.0 2022-06-06 11:29:26 +08:00
Alex Lam S.L
a025392a30 fix corner case in comparisons (#5486)
fixes #5485
2022-06-05 00:47:38 +08:00
Alex Lam S.L
ad5f5ef2a3 fix corner case in webkit (#5483)
fixes #5480
2022-06-02 02:45:02 +08:00
Chen Yangjian
40e669eacb docs: toplevel webkit option sets compress.webkit as well (#5480) 2022-06-02 02:44:06 +08:00
Alex Lam S.L
ad3a331ca3 fix corner case in collapse_vars (#5482)
fixes #5481
2022-06-02 02:42:39 +08:00
Alex Lam S.L
3c9e1693d5 fix corner case in side_effects (#5479)
fixes #5478
2022-05-31 00:27:40 +08:00
Alex Lam S.L
8bc03dc6c4 fix corner case in keep_fargs (#5477)
fixes #5476
2022-05-29 12:10:19 +08:00
Alex Lam S.L
2152f00de2 enhance inline & module (#5475) 2022-05-27 11:57:05 +08:00
Alex Lam S.L
94aae05d45 fix corner case in merge_vars (#5472)
fixes #5471
2022-05-27 03:13:24 +08:00
Alex Lam S.L
0dbf2b1d3c suppress false positives in ufuzz (#5473) 2022-05-26 23:17:47 +08:00
Alex Lam S.L
59b23b8c13 fix corner case in booleans (#5470)
fixes #5469
2022-05-26 05:33:50 +08:00
Alex Lam S.L
5979b195fe suppress false positives in ufuzz (#5468) 2022-05-25 23:50:47 +08:00
Alex Lam S.L
a1cff23377 suppress false positives in ufuzz (#5467) 2022-05-25 06:35:59 +08:00
Alex Lam S.L
c82fc1ef71 implement --module (#5462) 2022-05-24 05:45:47 +08:00
Alex Lam S.L
740f93f5a9 fix corner case in merge_vars (#5466)
fixes #5465
2022-05-24 05:45:07 +08:00
Alex Lam S.L
d4caa97b88 fix corner case in reduce_vars (#5464)
fixes #5463
2022-05-23 11:08:12 +08:00
Alex Lam S.L
c2ca7b7659 drop unused extends properly (#5461) 2022-05-23 03:53:32 +08:00
Alex Lam S.L
59edda6ca5 suppress false positives in ufuzz (#5460) 2022-05-22 01:36:45 +08:00
Alex Lam S.L
1668bc33c3 improve ufuzz coverage (#5459) 2022-05-21 02:57:13 +08:00
Alex Lam S.L
01f1e3fef8 avoid v8 quirks in ufuzz (#5458) 2022-05-20 00:00:24 +08:00
Alex Lam S.L
27aa85f84b fix corner cases in merge_vars (#5457)
fixes #5456
2022-05-19 06:39:20 +08:00
Alex Lam S.L
33c9c48318 fix corner case in hoist_props & unused (#5455)
fixes #5454
2022-05-19 04:45:38 +08:00
Alex Lam S.L
63f16e4616 fix corner case in merge_vars (#5452)
fixes #5451
2022-05-18 02:41:05 +08:00
Alex Lam S.L
cb6dd34b98 avoid broken versions of Node.js (#5453) 2022-05-18 02:40:31 +08:00
Alex Lam S.L
27727e6926 fix corner cases in unused (#5449)
fixes #5448
2022-05-17 17:03:06 +08:00
Alex Lam S.L
a968ddc78c avoid webpack bug in web-tooling-benchmark (#5450) 2022-05-17 14:08:11 +08:00
Alex Lam S.L
f70462aeb2 fix corner case in merge_vars (#5445)
fixes #5444
2022-05-16 16:30:14 +08:00
Alex Lam S.L
3aa92c76cc avoid extends error in ufuzz (#5447) 2022-05-16 11:11:10 +08:00
Alex Lam S.L
fc6a66836a fix corner case in unused (#5446)
fixes #5444
2022-05-16 09:13:30 +08:00
Alex Lam S.L
31167da1a9 avoid extends error in test cases (#5443) 2022-05-16 06:50:22 +08:00
Alex Lam S.L
7db2ada880 fix corner case in hoist_props (#5442)
fixes #5441
2022-05-16 06:49:09 +08:00
Alex Lam S.L
e31bbe329a improve compatibility with use strict (#5440) 2022-05-14 12:15:54 +08:00
Alex Lam S.L
8946c87011 suppress invalid test case generation (#5439)
- document v8 quirks

closes #5438
2022-05-12 04:38:11 +08:00
Alex Lam S.L
a9ef659bcb v3.15.5 2022-05-11 05:26:10 +08:00
Alex Lam S.L
35c2149dbd fix corner case in merge_vars (#5437)
fixes #5436
2022-05-08 04:16:28 +08:00
Alex Lam S.L
89a35f9fcd fix corner case in reduce_vars (#5435)
fixes #5434
2022-05-06 09:32:47 +08:00
Alex Lam S.L
1a4e99dc2d avoid v8 quirks in ufuzz (#5431)
closes #5428
closes #5429
2022-04-25 21:33:31 +08:00
Alex Lam S.L
cb870f6fd6 document v8 quirks (#5430)
closes #5428
closes #5429
2022-04-21 02:51:53 +08:00
Alex Lam S.L
a0c0c294c5 fix corner case in assignments (#5426)
fixes #5425
2022-04-19 13:19:30 +08:00
Alex Lam S.L
fbdb7eeda3 fix corner case in merge_vars (#5424)
fixes #5423
2022-04-19 09:04:06 +08:00
Alex Lam S.L
1bc0fccc8c improve ufuzz resilience (#5422) 2022-04-18 13:03:01 +08:00
Alex Lam S.L
20252c6483 fix corner case in merge_vars (#5421)
fixes #5420
2022-04-18 06:38:08 +08:00
Alex Lam S.L
e396912ea2 suppress false positives with export & import (#5418) 2022-04-16 04:27:50 +08:00
Alex Lam S.L
5ebfa78f56 fix corner case with arguments (#5417)
fixes #5416
2022-04-15 06:52:10 +08:00
Alex Lam S.L
950609f578 fix corner case in inline (#5415)
fixes #5414
2022-04-13 06:19:37 +08:00
33 changed files with 2111 additions and 525 deletions

View File

@@ -17,13 +17,13 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- node: latest - node: '16'
os: macos-latest os: macos-latest
- node: '8' - node: '12'
os: ubuntu-latest os: ubuntu-latest
- node: '8' - node: '8'
os: ubuntu-latest os: ubuntu-latest
- node: '8' - node: '12'
os: windows-latest os: windows-latest
- node: '8' - node: '8'
os: windows-latest os: windows-latest

View File

@@ -118,6 +118,7 @@ a double dash to prevent input files being used as option arguments:
--keep-fargs Do not mangle/drop function arguments. --keep-fargs Do not mangle/drop function arguments.
--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.
--module Process input as ES module (implies --toplevel)
--name-cache <file> File to hold mangled name mappings. --name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS) --self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options: --source-map [options] Enable source map/specify source map options:
@@ -146,7 +147,7 @@ a double dash to prevent input files being used as option arguments:
--warn Print warning messages. --warn Print warning messages.
--webkit Support non-standard Safari/Webkit. --webkit Support non-standard Safari/Webkit.
Equivalent to setting `webkit: true` in `minify()` Equivalent to setting `webkit: true` in `minify()`
for `mangle` and `output` options. for `compress`, `mangle` and `output` options.
By default UglifyJS will not try to be Safari-proof. By default UglifyJS will not try to be Safari-proof.
--wrap <name> Embed everything in a big function, making the --wrap <name> Embed everything in a big function, making the
“exports” and “global” variables available. You “exports” and “global” variables available. You
@@ -517,6 +518,9 @@ if (result.error) throw result.error;
- `mangle.properties` (default: `false`) — a subcategory of the mangle option. - `mangle.properties` (default: `false`) — a subcategory of the mangle option.
Pass an object to specify custom [mangle property options](#mangle-properties-options). Pass an object to specify custom [mangle property options](#mangle-properties-options).
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` alongside with `toplevel` enabled.
- `nameCache` (default: `null`) — pass an empty object `{}` or a previously - `nameCache` (default: `null`) — pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is property names across multiple invocations of `minify()`. Note: this is
@@ -728,6 +732,9 @@ to be `false` and all symbol names will be omitted.
- `merge_vars` (default: `true`) — combine and reuse variables. - `merge_vars` (default: `true`) — combine and reuse variables.
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` alongside with `toplevel` enabled.
- `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
code generator would insert. code generator would insert.
@@ -1331,10 +1338,8 @@ To allow for better optimizations, the compiler makes various assumptions:
- Later versions of JavaScript will throw `SyntaxError` with the following: - Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript ```javascript
var await; var await;
async function f() { class A {
class A { static p = await;
static p = await;
}
} }
// SyntaxError: Unexpected reserved word // SyntaxError: Unexpected reserved word
``` ```
@@ -1373,3 +1378,46 @@ To allow for better optimizations, the compiler makes various assumptions:
// TypeError: const 'a' has already been declared // TypeError: const 'a' has already been declared
``` ```
UglifyJS may modify the input which in turn may suppress those errors. UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
try {
class A {
static 42;
static get 42() {}
}
console.log("PASS");
} catch (e) {
console.log("FAIL");
}
// Expected: "PASS"
// Actual: "FAIL"
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
(async function(a) {
(function() {
var b = await => console.log("PASS");
b();
})();
})().catch(console.error);
// Expected: "PASS"
// Actual: SyntaxError: Unexpected reserved word
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
try {
f();
function f() {
throw 42;
}
} catch (e) {}
console.log(typeof f);
// Expected: "function"
// Actual: "undefined"
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -107,6 +107,7 @@ function process_option(name, no_value) {
" --ie Support non-standard Internet Explorer.", " --ie Support non-standard Internet Explorer.",
" --keep-fargs Do not mangle/drop function arguments.", " --keep-fargs Do not mangle/drop function arguments.",
" --keep-fnames Do not mangle/drop function names. Useful for code relying on Function.prototype.name.", " --keep-fnames Do not mangle/drop function names. Useful for code relying on Function.prototype.name.",
" --module Process input as ES module (implies --toplevel)",
" --name-cache <file> File to hold mangled name mappings.", " --name-cache <file> File to hold mangled name mappings.",
" --rename Force symbol expansion.", " --rename Force symbol expansion.",
" --no-rename Disable symbol expansion.", " --no-rename Disable symbol expansion.",
@@ -152,6 +153,7 @@ function process_option(name, no_value) {
case "annotations": case "annotations":
case "ie": case "ie":
case "ie8": case "ie8":
case "module":
case "timings": case "timings":
case "toplevel": case "toplevel":
case "v8": case "v8":

View File

@@ -827,6 +827,9 @@ var AST_Class = DEFNODE("Class", "extends name properties", {
extends: "[AST_Node?] the super class, or null if not specified", extends: "[AST_Node?] the super class, or null if not specified",
properties: "[AST_ClassProperty*] array of class properties", properties: "[AST_ClassProperty*] array of class properties",
}, },
resolve: function(def_class) {
return def_class ? this : this.parent_scope.resolve();
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -851,9 +854,6 @@ var AST_DefClass = DEFNODE("DefClass", null, {
$propdoc: { $propdoc: {
name: "[AST_SymbolDefClass] the name of this class", name: "[AST_SymbolDefClass] the name of this class",
}, },
resolve: function(def_class) {
return def_class ? this : this.parent_scope.resolve();
},
_validate: function() { _validate: function() {
if (!(this.name instanceof AST_SymbolDefClass)) throw new Error("name must be AST_SymbolDefClass"); if (!(this.name instanceof AST_SymbolDefClass)) throw new Error("name must be AST_SymbolDefClass");
}, },
@@ -1837,7 +1837,7 @@ var AST_Label = DEFNODE("Label", "references", {
initialize: function() { initialize: function() {
this.references = []; this.references = [];
this.thedef = this; this.thedef = this;
} },
}, AST_Symbol); }, AST_Symbol);
var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", { var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", {
@@ -2039,16 +2039,21 @@ TreeWalker.prototype = {
return this.stack[this.stack.length - 2 - (n || 0)]; return this.stack[this.stack.length - 2 - (n || 0)];
}, },
push: function(node) { push: function(node) {
if (node instanceof AST_Lambda) { var value;
if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
value = "use strict";
} else if (node instanceof AST_Directive) {
value = node.value;
} else if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives); this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node;
} }
if (value && !this.directives[value]) this.directives[value] = node;
this.stack.push(node); this.stack.push(node);
}, },
pop: function() { pop: function() {
var node = this.stack.pop(); var node = this.stack.pop();
if (node instanceof AST_Lambda) { if (node instanceof AST_Class || node instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives); this.directives = Object.getPrototypeOf(this.directives);
} }
}, },

View File

@@ -80,6 +80,7 @@ function Compressor(options, false_by_default) {
keep_infinity : false, keep_infinity : false,
loops : !false_by_default, loops : !false_by_default,
merge_vars : !false_by_default, merge_vars : !false_by_default,
module : false,
negate_iife : !false_by_default, negate_iife : !false_by_default,
objects : !false_by_default, objects : !false_by_default,
optional_chains : !false_by_default, optional_chains : !false_by_default,
@@ -97,7 +98,7 @@ function Compressor(options, false_by_default) {
switches : !false_by_default, switches : !false_by_default,
templates : !false_by_default, templates : !false_by_default,
top_retain : null, top_retain : null,
toplevel : !!(options && options["top_retain"]), toplevel : !!(options && (options["module"] || options["top_retain"])),
typeofs : !false_by_default, typeofs : !false_by_default,
unsafe : false, unsafe : false,
unsafe_comps : false, unsafe_comps : false,
@@ -130,6 +131,7 @@ function Compressor(options, false_by_default) {
var escaped = def.escaped; var escaped = def.escaped;
return escaped && escaped.depth != 1; return escaped && escaped.depth != 1;
}; };
if (this.options["module"]) this.directives["use strict"] = true;
var pure_funcs = this.options["pure_funcs"]; var pure_funcs = this.options["pure_funcs"];
if (typeof pure_funcs == "function") { if (typeof pure_funcs == "function") {
this.pure_funcs = pure_funcs; this.pure_funcs = pure_funcs;
@@ -649,7 +651,7 @@ Compressor.prototype.compress = function(node) {
} }
if (!HOP(tw.safe_ids, def.id)) { if (!HOP(tw.safe_ids, def.id)) {
if (!safe) return false; if (!safe) return false;
if (safe.read) { if (safe.read || tw.in_loop) {
var scope = tw.find_parent(AST_BlockScope); var scope = tw.find_parent(AST_BlockScope);
if (scope instanceof AST_Class) return false; if (scope instanceof AST_Class) return false;
if (def.scope.resolve() !== scope.resolve()) return false; if (def.scope.resolve() !== scope.resolve()) return false;
@@ -766,14 +768,14 @@ Compressor.prototype.compress = function(node) {
function make_fixed_default(compressor, node, save) { function make_fixed_default(compressor, node, save) {
var prev_save, prev_seq; var prev_save, prev_seq;
return function() { return function() {
if (prev_seq === node) return node;
var current = save(); var current = save();
var ev; var ev = fuzzy_eval(compressor, current, true);
if (!is_undefined(current, compressor) && (ev = fuzzy_eval(compressor, current, true)) !== undefined) { if (ev instanceof AST_Node) {
return ev instanceof AST_Node ? node : current; prev_seq = node;
} } else if (prev_save !== current) {
if (prev_save !== current) {
prev_save = current; prev_save = current;
prev_seq = make_sequence(node, [ current, node.value ]); prev_seq = ev === undefined ? make_sequence(node, [ current, node.value ]) : current;
} }
return prev_seq; return prev_seq;
}; };
@@ -1557,7 +1559,10 @@ Compressor.prototype.compress = function(node) {
AST_SymbolRef.DEFMETHOD("is_immutable", function() { AST_SymbolRef.DEFMETHOD("is_immutable", function() {
var def = this.redef || this.definition(); var def = this.redef || this.definition();
return (this.in_arg || def.orig.length == 1) && def.orig[0] instanceof AST_SymbolLambda; if (!(def.orig[0] instanceof AST_SymbolLambda)) return false;
if (def.orig.length == 1) return true;
if (!this.in_arg) return false;
return !(def.orig[1] instanceof AST_SymbolFunarg);
}); });
AST_Node.DEFMETHOD("convert_symbol", noop); AST_Node.DEFMETHOD("convert_symbol", noop);
@@ -2464,6 +2469,13 @@ Compressor.prototype.compress = function(node) {
abort = false; abort = false;
return true; return true;
} }
if (node instanceof AST_Class) {
if (!in_try) return false;
var base = node.extends;
if (!base) return false;
if (base instanceof AST_SymbolRef) base = base.fixed_value();
return !safe_for_extends(base);
}
if (node instanceof AST_Exit) { if (node instanceof AST_Exit) {
if (in_try) { if (in_try) {
if (in_try.bfinally) return true; if (in_try.bfinally) return true;
@@ -2552,8 +2564,8 @@ Compressor.prototype.compress = function(node) {
&& all(iife.args, function(arg) { && all(iife.args, function(arg) {
return !(arg instanceof AST_Spread); return !(arg instanceof AST_Spread);
})) { })) {
var fn_strict = compressor.has_directive("use strict"); var fn_strict = fn.in_strict_mode(compressor)
if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false; && !fn.parent_scope.resolve(true).in_strict_mode(compressor);
var has_await = is_async(fn) ? function(node) { var has_await = is_async(fn) ? function(node) {
return node instanceof AST_Symbol && node.name == "await"; return node instanceof AST_Symbol && node.name == "await";
} : function(node) { } : function(node) {
@@ -4120,6 +4132,25 @@ Compressor.prototype.compress = function(node) {
&& !(compressor && node.expression.has_side_effects(compressor)); && !(compressor && node.expression.has_side_effects(compressor));
} }
// in_strict_mode()
// return true if scope executes in Strict Mode
(function(def) {
def(AST_Class, return_true);
def(AST_Scope, function(compressor) {
var body = this.body;
for (var i = 0; i < body.length; i++) {
var stat = body[i];
if (!(stat instanceof AST_Directive)) break;
if (stat.value == "use strict") return true;
}
var parent = this.parent_scope;
if (!parent) return compressor.option("module");
return parent.resolve(true).in_strict_mode(compressor);
});
})(function(node, func) {
node.DEFMETHOD("in_strict_mode", func);
});
// is_truthy() // is_truthy()
// return true if `!!node === true` // return true if `!!node === true`
(function(def) { (function(def) {
@@ -6015,7 +6046,7 @@ Compressor.prototype.compress = function(node) {
var NO_MERGE = makePredicate("arguments await yield"); var NO_MERGE = makePredicate("arguments await yield");
AST_Scope.DEFMETHOD("merge_variables", function(compressor) { AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
if (!compressor.option("merge_vars")) return; if (!compressor.option("merge_vars")) return;
var in_try, root, segment = {}, self = this; var in_arg = [], in_try, root, segment = {}, self = this;
var first = [], last = [], index = 0; var first = [], last = [], index = 0;
var declarations = new Dictionary(); var declarations = new Dictionary();
var references = Object.create(null); var references = Object.create(null);
@@ -6026,31 +6057,7 @@ Compressor.prototype.compress = function(node) {
var rhs = node.right; var rhs = node.right;
if (lhs instanceof AST_Destructured) { if (lhs instanceof AST_Destructured) {
rhs.walk(tw); rhs.walk(tw);
var marker = new TreeWalker(function(node) { walk_destructured(AST_SymbolRef, mark, lhs);
if (node instanceof AST_Destructured) return;
if (node instanceof AST_DefaultValue) {
push();
node.value.walk(tw);
pop();
node.name.walk(marker);
} else if (node instanceof AST_DestructuredKeyVal) {
if (node.key instanceof AST_Node) {
push();
segment.block = node;
node.key.walk(tw);
node.value.walk(marker);
pop();
} else {
node.value.walk(marker);
}
} else if (node instanceof AST_SymbolRef) {
mark(node);
} else {
node.walk(tw);
}
return true;
});
lhs.walk(marker);
return true; return true;
} }
if (lazy_op[node.operator.slice(0, -1)]) { if (lazy_op[node.operator.slice(0, -1)]) {
@@ -6081,18 +6088,15 @@ Compressor.prototype.compress = function(node) {
} }
if (node instanceof AST_Call) { if (node instanceof AST_Call) {
var exp = node.expression; var exp = node.expression;
var tail = exp.tail_node(); if (exp instanceof AST_LambdaExpression) {
if (!(tail instanceof AST_LambdaExpression)) { node.args.forEach(function(arg) {
arg.walk(tw);
});
exp.walk(tw);
} else {
descend(); descend();
return mark_expression(exp); mark_expression(exp);
} }
if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
node.walk(tw);
});
node.args.forEach(function(arg) {
arg.walk(tw);
});
tail.walk(tw);
return true; return true;
} }
if (node instanceof AST_Class) { if (node instanceof AST_Class) {
@@ -6105,9 +6109,10 @@ Compressor.prototype.compress = function(node) {
if (prop.static) { if (prop.static) {
prop.value.walk(tw); prop.value.walk(tw);
} else { } else {
push(tw); push();
segment.block = node;
prop.value.walk(tw); prop.value.walk(tw);
pop(tw); pop();
} }
}); });
return true; return true;
@@ -6173,32 +6178,16 @@ Compressor.prototype.compress = function(node) {
if (node instanceof AST_Lambda) { if (node instanceof AST_Lambda) {
if (node.name) references[node.name.definition().id] = false; if (node.name) references[node.name.definition().id] = false;
var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) { var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) {
if (node instanceof AST_SymbolFunarg) references[node.definition().id] = false; references[node.definition().id] = false;
} : function(node) { } : function(node) {
if (node instanceof AST_SymbolFunarg) mark(node); mark(node);
}; };
var scanner = new TreeWalker(function(ref) { in_arg.push(node);
if (ref instanceof AST_SymbolDeclaration) references[ref.definition().id] = false;
if (!(ref instanceof AST_SymbolRef)) return;
var def = ref.definition();
var ldef = node.variables.get(ref.name);
if (ldef && (ldef === def
|| def.undeclared
|| node.parent_scope.find_variable(ref.name) === def)) {
references[def.id] = false;
references[ldef.id] = false;
} else {
var save = segment;
pop();
mark(ref, true);
segment = save;
}
return true;
});
node.argnames.forEach(function(argname) { node.argnames.forEach(function(argname) {
argname.mark_symbol(marker, scanner); walk_destructured(AST_SymbolFunarg, marker, argname);
}); });
if (node.rest) node.rest.mark_symbol(marker, scanner); if (node.rest) walk_destructured(AST_SymbolFunarg, marker, node.rest);
in_arg.pop();
} }
walk_lambda(node, tw); walk_lambda(node, tw);
pop(); pop();
@@ -6214,7 +6203,8 @@ Compressor.prototype.compress = function(node) {
} else { } else {
descend(); descend();
} }
return mark_expression(exp); mark_expression(exp);
return true;
} }
if (node instanceof AST_Switch) { if (node instanceof AST_Switch) {
node.expression.walk(tw); node.expression.walk(tw);
@@ -6246,9 +6236,7 @@ Compressor.prototype.compress = function(node) {
if (node instanceof AST_Try) { if (node instanceof AST_Try) {
var save_try = in_try; var save_try = in_try;
in_try = node; in_try = node;
var save = segment;
walk_body(node, tw); walk_body(node, tw);
segment = save;
if (node.bcatch) { if (node.bcatch) {
if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) { if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
if (node instanceof AST_SymbolCatch) { if (node instanceof AST_SymbolCatch) {
@@ -6266,7 +6254,6 @@ Compressor.prototype.compress = function(node) {
} }
} }
in_try = save_try; in_try = save_try;
segment = save;
if (node.bfinally) node.bfinally.walk(tw); if (node.bfinally) node.bfinally.walk(tw);
return true; return true;
} }
@@ -6316,11 +6303,9 @@ Compressor.prototype.compress = function(node) {
} }
function mark_expression(exp) { function mark_expression(exp) {
if (compressor.option("ie")) { if (!compressor.option("ie")) return;
var sym = root_expr(exp); var sym = root_expr(exp);
if (sym instanceof AST_SymbolRef) sym.walk(tw); if (sym instanceof AST_SymbolRef) sym.walk(tw);
}
return true;
} }
function walk_cond(condition, consequent, alternative) { function walk_cond(condition, consequent, alternative) {
@@ -6416,8 +6401,45 @@ Compressor.prototype.compress = function(node) {
segment = Object.getPrototypeOf(segment); segment = Object.getPrototypeOf(segment);
} }
function walk_destructured(symbol_type, mark, lhs) {
var marker = new TreeWalker(function(node) {
if (node instanceof AST_Destructured) return;
if (node instanceof AST_DefaultValue) {
push();
node.value.walk(tw);
pop();
node.name.walk(marker);
} else if (node instanceof AST_DestructuredKeyVal) {
if (node.key instanceof AST_Node) {
push();
segment.block = node;
node.key.walk(tw);
node.value.walk(marker);
pop();
} else {
node.value.walk(marker);
}
} else if (node instanceof symbol_type) {
mark(node);
} else {
node.walk(tw);
}
return true;
});
lhs.walk(marker);
}
function mark(sym, read) { function mark(sym, read) {
var def = sym.definition(), ldef, seg = segment; var def = sym.definition(), ldef;
if (read && !all(in_arg, function(fn) {
ldef = fn.variables.get(sym.name);
if (!ldef) return true;
if (!is_funarg(ldef)) return true;
return ldef !== def
&& !def.undeclared
&& fn.parent_scope.find_variable(sym.name) !== def;
})) return references[def.id] = references[ldef.id] = false;
var seg = segment;
if (in_try) { if (in_try) {
push(); push();
seg = segment; seg = segment;
@@ -6776,7 +6798,6 @@ Compressor.prototype.compress = function(node) {
} }
}); });
// pass 3: we should drop declarations not in_use // pass 3: we should drop declarations not in_use
var unused_fn_names = [];
var calls_to_drop_args = []; var calls_to_drop_args = [];
var fns_with_marked_args = []; var fns_with_marked_args = [];
var trimmer = new TreeTransformer(function(node) { var trimmer = new TreeTransformer(function(node) {
@@ -6897,7 +6918,7 @@ Compressor.prototype.compress = function(node) {
} }
} }
if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) { if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) {
unused_fn_names.push(node); node.name = null;
} }
if (node instanceof AST_Lambda) { if (node instanceof AST_Lambda) {
if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) { if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
@@ -6913,7 +6934,7 @@ Compressor.prototype.compress = function(node) {
} }
} }
if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) { if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
unused_fn_names.push(node); node.name = null;
} }
if (!(node instanceof AST_Accessor)) { if (!(node instanceof AST_Accessor)) {
var args, spread, trim = compressor.drop_fargs(node, parent); var args, spread, trim = compressor.drop_fargs(node, parent);
@@ -6925,7 +6946,9 @@ Compressor.prototype.compress = function(node) {
} }
var argnames = node.argnames; var argnames = node.argnames;
var rest = node.rest; var rest = node.rest;
var after = false, before = false;
if (rest) { if (rest) {
before = true;
if (!args || spread < argnames.length || rest instanceof AST_SymbolFunarg) { if (!args || spread < argnames.length || rest instanceof AST_SymbolFunarg) {
rest = rest.transform(trimmer); rest = rest.transform(trimmer);
} else { } else {
@@ -6938,8 +6961,7 @@ Compressor.prototype.compress = function(node) {
args.length = argnames.length; args.length = argnames.length;
if (trimmed.value.elements.length) [].push.apply(args, trimmed.value.elements); if (trimmed.value.elements.length) [].push.apply(args, trimmed.value.elements);
} }
if (rest instanceof AST_Destructured && !rest.rest if (rest instanceof AST_Destructured && !rest.rest) {
&& (!node.uses_arguments || tt.has_directive("use strict"))) {
if (rest instanceof AST_DestructuredArray) { if (rest instanceof AST_DestructuredArray) {
if (rest.elements.length == 0) rest = null; if (rest.elements.length == 0) rest = null;
} else if (rest.properties.length == 0) { } else if (rest.properties.length == 0) {
@@ -6947,9 +6969,13 @@ Compressor.prototype.compress = function(node) {
} }
} }
node.rest = rest; node.rest = rest;
if (rest) trim = false; if (rest) {
trim = false;
after = true;
}
} }
var default_length = trim ? -1 : node.length(); var default_length = trim ? -1 : node.length();
var trim_value = args && !node.uses_arguments && parent !== compressor.parent();
for (var i = argnames.length; --i >= 0;) { for (var i = argnames.length; --i >= 0;) {
var sym = argnames[i]; var sym = argnames[i];
if (sym instanceof AST_SymbolFunarg) { if (sym instanceof AST_SymbolFunarg) {
@@ -6964,29 +6990,43 @@ Compressor.prototype.compress = function(node) {
sym.unused = true; sym.unused = true;
} }
} else { } else {
before = true;
var funarg; var funarg;
if (!args || spread < i) { if (!args || spread < i) {
funarg = sym.transform(trimmer); funarg = sym.transform(trimmer);
} else { } else {
funarg = trim_destructured(sym, args[i], function(node) { var trimmed = trim_destructured(sym, args[i], function(node) {
return node.definition().id in in_use_ids ? node : null; return node.definition().id in in_use_ids ? node : null;
}, !node.uses_arguments, sym).name; }, trim_value, sym);
funarg = trimmed.name;
if (trimmed.value) args[i] = trimmed.value;
} }
if (funarg) { if (funarg) {
trim = false; trim = false;
argnames[i] = funarg;
if (!after) after = !(funarg instanceof AST_SymbolFunarg);
} else if (trim) { } else if (trim) {
log_default(sym, "Dropping unused default argument {name}"); log_default(sym, "Dropping unused default argument {name}");
argnames.pop(); argnames.pop();
} else if (i > default_length) { } else if (i > default_length) {
log_default(sym, "Dropping unused default argument assignment {name}"); log_default(sym, "Dropping unused default argument assignment {name}");
if (sym.name instanceof AST_SymbolFunarg) sym.name.unused = true; if (sym.name instanceof AST_SymbolFunarg) {
sym.name.unused = true;
} else {
after = true;
}
argnames[i] = sym.name; argnames[i] = sym.name;
} else { } else {
log_default(sym, "Dropping unused default argument value {name}"); log_default(sym, "Dropping unused default argument value {name}");
argnames[i] = sym = sym.clone();
sym.value = make_node(AST_Number, sym, { value: 0 }); sym.value = make_node(AST_Number, sym, { value: 0 });
after = true;
} }
} }
} }
if (before && !after && node.uses_arguments && !tt.has_directive("use strict")) {
node.rest = make_node(AST_DestructuredArray, node, { elements: [] });
}
fns_with_marked_args.push(node); fns_with_marked_args.push(node);
} }
} }
@@ -7210,6 +7250,7 @@ Compressor.prototype.compress = function(node) {
}); });
var index = indexOf_assign(sym, def); var index = indexOf_assign(sym, def);
if (index >= 0) assign_in_use[sym.id][index] = assign; if (index >= 0) assign_in_use[sym.id][index] = assign;
sym.assignments++;
sym.eliminated++; sym.eliminated++;
return assign; return assign;
})); }));
@@ -7285,6 +7326,13 @@ Compressor.prototype.compress = function(node) {
} }
}, function(node, in_list) { }, function(node, in_list) {
if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list); if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
if (node instanceof AST_ExportDeclaration) {
var block = node.body;
if (!(block instanceof AST_BlockStatement)) return;
node.body = block.body.pop();
block.body.push(node);
return in_list ? List.splice(block.body) : block;
}
if (node instanceof AST_For) return patch_for_init(node, in_list); if (node instanceof AST_For) return patch_for_init(node, in_list);
if (node instanceof AST_ForIn) { if (node instanceof AST_ForIn) {
if (!drop_vars || !compressor.option("loops")) return; if (!drop_vars || !compressor.option("loops")) return;
@@ -7318,6 +7366,7 @@ Compressor.prototype.compress = function(node) {
} }
}); });
tt.push(compressor.parent()); tt.push(compressor.parent());
tt.directives = Object.create(compressor.directives);
self.transform(tt); self.transform(tt);
if (self instanceof AST_Lambda if (self instanceof AST_Lambda
&& self.body.length == 1 && self.body.length == 1
@@ -7325,9 +7374,6 @@ Compressor.prototype.compress = function(node) {
&& self.body[0].value == "use strict") { && self.body[0].value == "use strict") {
self.body.length = 0; self.body.length = 0;
} }
unused_fn_names.forEach(function(fn) {
fn.name = null;
});
calls_to_drop_args.forEach(function(call) { calls_to_drop_args.forEach(function(call) {
drop_unused_call_args(call, compressor, fns_with_marked_args); drop_unused_call_args(call, compressor, fns_with_marked_args);
}); });
@@ -7539,6 +7585,7 @@ Compressor.prototype.compress = function(node) {
var value = node.value.drop_side_effect_free(compressor); var value = node.value.drop_side_effect_free(compressor);
if (!value) return null; if (!value) return null;
log(node.name, "Side effects in default value of unused variable {name}"); log(node.name, "Side effects in default value of unused variable {name}");
node = node.clone();
node.name.unused = null; node.name.unused = null;
node.value = value; node.value = value;
} }
@@ -7616,6 +7663,7 @@ Compressor.prototype.compress = function(node) {
drop = save_drop; drop = save_drop;
if (values && newValues) { if (values && newValues) {
fill_holes(value, newValues); fill_holes(value, newValues);
value = value.clone();
value.elements = newValues; value.elements = newValues;
} }
if (!node.rest && (value instanceof AST_Array if (!node.rest && (value instanceof AST_Array
@@ -7649,20 +7697,25 @@ Compressor.prototype.compress = function(node) {
drop = false; drop = false;
value = value.fixed_value(); value = value.fixed_value();
} }
var prop_keys, prop_map; var prop_keys, prop_map, values;
if (value instanceof AST_Object) { if (value instanceof AST_Object) {
prop_keys = []; prop_keys = [];
prop_map = new Dictionary(); prop_map = new Dictionary();
value.properties.forEach(function(prop, index) { values = value.properties.map(function(prop, index) {
if (prop instanceof AST_Spread) return prop_map = false; prop = prop.clone();
var key = prop.key; if (prop instanceof AST_Spread) {
if (key instanceof AST_Node) key = key.evaluate(compressor, true);
if (key instanceof AST_Node) {
prop_map = false; prop_map = false;
} else if (prop_map && !(prop instanceof AST_ObjectSetter)) { } else {
prop_map.set(key, prop); var key = prop.key;
if (key instanceof AST_Node) key = key.evaluate(compressor, true);
if (key instanceof AST_Node) {
prop_map = false;
} else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
prop_map.set(key, prop);
}
prop_keys[index] = key;
} }
prop_keys[index] = key; return prop;
}); });
} }
if (node.rest) { if (node.rest) {
@@ -7727,27 +7780,30 @@ Compressor.prototype.compress = function(node) {
}); });
value = save_value; value = save_value;
drop = save_drop; drop = save_drop;
if (drop_keys && prop_keys) value.properties = List(value.properties, function(prop, index) { if (drop_keys && prop_keys) {
if (prop instanceof AST_Spread) return prop; value = value.clone();
var key = prop_keys[index]; value.properties = List(values, function(prop, index) {
if (key instanceof AST_Node) return prop; if (prop instanceof AST_Spread) return prop;
if (drop_keys.has(key)) { var key = prop_keys[index];
var mapped = drop_keys.get(key); if (key instanceof AST_Node) return prop;
if (!mapped) return prop; if (drop_keys.has(key)) {
if (mapped === prop) return prop_map.get(key) || List.skip; var mapped = drop_keys.get(key);
} else if (node.rest) { if (!mapped) return prop;
return prop; if (mapped === prop) return prop_map.get(key) || List.skip;
} } else if (node.rest) {
var trimmed = prop.value.drop_side_effect_free(compressor); return prop;
if (trimmed) { }
prop.value = trimmed; var trimmed = prop.value.drop_side_effect_free(compressor);
return prop; if (trimmed) {
} prop.value = trimmed;
return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, { return prop;
key: prop.key, }
value: make_node(AST_Number, prop, { value: 0 }), return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, {
}) : List.skip; key: prop.key,
}); value: make_node(AST_Number, prop, { value: 0 }),
}) : List.skip;
});
}
if (value && !node.rest) switch (properties.length) { if (value && !node.rest) switch (properties.length) {
case 0: case 0:
if (node === root) break; if (node === root) break;
@@ -8140,12 +8196,12 @@ Compressor.prototype.compress = function(node) {
var self = this; var self = this;
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false; var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
var defs_by_id = Object.create(null); var defs_by_id = Object.create(null);
self.transform(new TreeTransformer(function(node, descend) { var tt = new TreeTransformer(function(node, descend) {
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
if (node.operator != "=") return; if (node.operator != "=") return;
if (!node.write_only) return; if (!node.write_only) return;
if (!can_hoist(node.left, node.right, 1)) return; if (!can_hoist(node.left, node.right, 1)) return;
descend(node, this); descend(node, tt);
var defs = new Dictionary(); var defs = new Dictionary();
var assignments = []; var assignments = [];
var decls = []; var decls = [];
@@ -8169,15 +8225,20 @@ Compressor.prototype.compress = function(node) {
}); });
defs.value = node.right; defs.value = node.right;
defs_by_id[node.left.definition().id] = defs; defs_by_id[node.left.definition().id] = defs;
self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, { self.body.splice(self.body.indexOf(tt.stack[1]) + 1, 0, make_node(AST_Var, node, {
definitions: decls, definitions: decls,
})); }));
return make_sequence(node, assignments); return make_sequence(node, assignments);
} }
if (node instanceof AST_Scope) return node === self ? undefined : node; if (node instanceof AST_Scope) {
if (node === self) return;
var parent = tt.parent();
if (parent.TYPE == "Call" && parent.expression === node) return;
return node;
}
if (node instanceof AST_VarDef) { if (node instanceof AST_VarDef) {
if (!can_hoist(node.name, node.value, 0)) return; if (!can_hoist(node.name, node.value, 0)) return;
descend(node, this); descend(node, tt);
var defs = new Dictionary(); var defs = new Dictionary();
var var_defs = []; var var_defs = [];
var decl = node.clone(); var decl = node.clone();
@@ -8199,7 +8260,8 @@ Compressor.prototype.compress = function(node) {
defs.set(key, new_var.definition()); defs.set(key, new_var.definition());
return new_var; return new_var;
} }
})); });
self.transform(tt);
self.transform(new TreeTransformer(function(node, descend) { self.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_PropAccess) { if (node instanceof AST_PropAccess) {
if (!(node.expression instanceof AST_SymbolRef)) return; if (!(node.expression instanceof AST_SymbolRef)) return;
@@ -8229,6 +8291,7 @@ Compressor.prototype.compress = function(node) {
if (def.assignments != count) return; if (def.assignments != count) return;
if (def.references.length - def.replaced == count) return; if (def.references.length - def.replaced == count) return;
if (def.single_use) return; if (def.single_use) return;
if (self.find_variable(sym.name) !== def) return;
if (top_retain(def)) return; if (top_retain(def)) return;
if (sym.fixed_value() !== right) return; if (sym.fixed_value() !== right) return;
var fixed = sym.fixed || def.fixed; var fixed = sym.fixed || def.fixed;
@@ -8292,6 +8355,9 @@ Compressor.prototype.compress = function(node) {
var dropped = value.drop_side_effect_free(compressor); var dropped = value.drop_side_effect_free(compressor);
if (dropped !== value) { if (dropped !== value) {
changed = true; changed = true;
if (dropped && async && !is_primitive(compressor, dropped)) {
dropped = dropped.negate(compressor);
}
node.value = dropped; node.value = dropped;
} }
} }
@@ -9100,6 +9166,7 @@ Compressor.prototype.compress = function(node) {
function fuzzy_eval(compressor, node, nullish) { function fuzzy_eval(compressor, node, nullish) {
if (node.truthy) return true; if (node.truthy) return true;
if (is_undefined(node)) return undefined;
if (node.falsy && !nullish) return false; if (node.falsy && !nullish) return false;
if (node.is_truthy()) return true; if (node.is_truthy()) return true;
return node.evaluate(compressor, true); return node.evaluate(compressor, true);
@@ -9114,17 +9181,20 @@ Compressor.prototype.compress = function(node) {
child = parent; child = parent;
parent = compressor.parent(level++); parent = compressor.parent(level++);
if (parent instanceof AST_Binary) { if (parent instanceof AST_Binary) {
var op = parent.operator; switch (child) {
if (!lazy_op[op]) return; case parent.left:
var left = parent.left; if (lazy_op[parent.operator]) continue;
if (left === child) continue;
if (match(left)) switch (op) {
case "&&":
node[negated ? "falsy" : "truthy"] = true;
break; break;
case "||": case parent.right:
case "??": if (match(parent.left)) switch (parent.operator) {
node[negated ? "truthy" : "falsy"] = true; case "&&":
node[negated ? "falsy" : "truthy"] = true;
break;
case "||":
case "??":
node[negated ? "truthy" : "falsy"] = true;
break;
}
break; break;
} }
} else if (parent instanceof AST_Conditional) { } else if (parent instanceof AST_Conditional) {
@@ -9884,6 +9954,10 @@ Compressor.prototype.compress = function(node) {
return safe; return safe;
} }
function safe_from_strict_mode(fn, compressor) {
return fn.in_strict_mode(compressor) || !compressor.has_directive("use strict");
}
OPT(AST_Call, function(self, compressor) { OPT(AST_Call, function(self, compressor) {
var exp = self.expression; var exp = self.expression;
var terminated = trim_optional_chain(self, compressor); var terminated = trim_optional_chain(self, compressor);
@@ -10208,7 +10282,10 @@ Compressor.prototype.compress = function(node) {
} }
return true; return true;
}) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn, fn.rest)); }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn, fn.rest));
var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor); var can_inline = can_drop
&& compressor.option("inline")
&& !self.is_expr_pure(compressor)
&& (exp === fn || safe_from_strict_mode(fn, compressor));
if (can_inline && stat instanceof AST_Return) { if (can_inline && stat instanceof AST_Return) {
var value = stat.value; var value = stat.value;
if (exp === fn if (exp === fn
@@ -11142,9 +11219,15 @@ Compressor.prototype.compress = function(node) {
&& assign instanceof AST_Assign && assign instanceof AST_Assign
&& assign.operator == "=" && assign.operator == "="
&& self.left.equivalent_to(assign.left)) { && self.left.equivalent_to(assign.left)) {
self.right = assign.right; return make_node(AST_Assign, self, {
assign.right = self; operator: "=",
return assign; left: assign.left,
right: make_node(AST_Binary, self, {
operator: self.operator,
left: self.left,
right: assign.right,
}),
}).optimize(compressor);
} }
} }
if (compressor.option("comparisons")) switch (self.operator) { if (compressor.option("comparisons")) switch (self.operator) {
@@ -11809,8 +11892,9 @@ Compressor.prototype.compress = function(node) {
} else if (fixed.name && fixed.name.definition() !== def) { } else if (fixed.name && fixed.name.definition() !== def) {
single_use = false; single_use = false;
} else if (fixed.parent_scope !== self.scope || is_funarg(def)) { } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
single_use = fixed.is_constant_expression(self.scope); if (!safe_from_strict_mode(fixed, compressor)) {
if (single_use == "f") { single_use = false;
} else if ((single_use = fixed.is_constant_expression(self.scope)) == "f") {
var scope = self.scope; var scope = self.scope;
do { do {
if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) { if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
@@ -13382,7 +13466,7 @@ Compressor.prototype.compress = function(node) {
}); });
var body = []; var body = [];
fn.variables.each(function(def, name) { fn.variables.each(function(def, name) {
if (name == "arguments") return; if (!arrow && name == "arguments" && def.orig.length == 1) return;
names.set(name, true); names.set(name, true);
scope.enclosed.push(def); scope.enclosed.push(def);
scope.variables.set(name, def); scope.variables.set(name, def);

View File

@@ -81,13 +81,14 @@ function minify(files, options) {
keep_fargs: false, keep_fargs: false,
keep_fnames: false, keep_fnames: false,
mangle: {}, mangle: {},
module: false,
nameCache: null, nameCache: null,
output: {}, output: {},
parse: {}, parse: {},
rename: undefined, rename: undefined,
sourceMap: false, sourceMap: false,
timings: false, timings: false,
toplevel: false, toplevel: !!(options && options["module"]),
v8: false, v8: false,
validate: false, validate: false,
warnings: false, warnings: false,
@@ -101,6 +102,7 @@ function minify(files, options) {
if (options.ie) set_shorthand("ie", options, [ "compress", "mangle", "output", "rename" ]); if (options.ie) set_shorthand("ie", options, [ "compress", "mangle", "output", "rename" ]);
if (options.keep_fargs) set_shorthand("keep_fargs", options, [ "compress", "mangle", "rename" ]); if (options.keep_fargs) set_shorthand("keep_fargs", options, [ "compress", "mangle", "rename" ]);
if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle", "rename" ]); if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle", "rename" ]);
if (options.module) set_shorthand("module", options, [ "compress", "parse" ]);
if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle", "rename" ]); if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle", "rename" ]);
if (options.v8) set_shorthand("v8", options, [ "mangle", "output", "rename" ]); if (options.v8) set_shorthand("v8", options, [ "mangle", "output", "rename" ]);
if (options.webkit) set_shorthand("webkit", options, [ "compress", "mangle", "output", "rename" ]); if (options.webkit) set_shorthand("webkit", options, [ "compress", "mangle", "output", "rename" ]);

View File

@@ -237,8 +237,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
newline_before : false, newline_before : false,
regex_allowed : false, regex_allowed : false,
comments_before : [], comments_before : [],
directives : {}, directives : Object.create(null),
directive_stack : [],
read_template : with_eof_error("Unterminated template literal", function(strings) { read_template : with_eof_error("Unterminated template literal", function(strings) {
var s = ""; var s = "";
for (;;) { for (;;) {
@@ -635,24 +634,19 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
}; };
next_token.add_directive = function(directive) { next_token.add_directive = function(directive) {
S.directive_stack[S.directive_stack.length - 1].push(directive); S.directives[directive] = true;
if (S.directives[directive]) S.directives[directive]++;
else S.directives[directive] = 1;
} }
next_token.push_directives_stack = function() { next_token.push_directives_stack = function() {
S.directive_stack.push([]); S.directives = Object.create(S.directives);
} }
next_token.pop_directives_stack = function() { next_token.pop_directives_stack = function() {
var directives = S.directive_stack.pop(); S.directives = Object.getPrototypeOf(S.directives);
for (var i = directives.length; --i >= 0;) {
S.directives[directives[i]]--;
}
} }
next_token.has_directive = function(directive) { next_token.has_directive = function(directive) {
return S.directives[directive] > 0; return !!S.directives[directive];
} }
return next_token; return next_token;
@@ -699,6 +693,7 @@ function parse($TEXT, options) {
expression : false, expression : false,
filename : null, filename : null,
html5_comments : true, html5_comments : true,
module : false,
shebang : true, shebang : true,
strict : false, strict : false,
toplevel : null, toplevel : null,
@@ -1340,8 +1335,6 @@ function parse($TEXT, options) {
var loop = S.in_loop; var loop = S.in_loop;
var labels = S.labels; var labels = S.labels;
++S.in_function; ++S.in_function;
S.in_directives = true;
S.input.push_directives_stack();
S.in_loop = 0; S.in_loop = 0;
S.labels = []; S.labels = [];
if (is("punc", "{")) { if (is("punc", "{")) {
@@ -1352,8 +1345,6 @@ function parse($TEXT, options) {
handle_regexp(); handle_regexp();
value = maybe_assign(); value = maybe_assign();
} }
var is_strict = S.input.has_directive("use strict");
S.input.pop_directives_stack();
--S.in_function; --S.in_function;
S.in_loop = loop; S.in_loop = loop;
S.labels = labels; S.labels = labels;
@@ -1367,7 +1358,7 @@ function parse($TEXT, options) {
value: value, value: value,
end: prev(), end: prev(),
}); });
if (is_strict) node.each_argname(strict_verify_symbol); if (S.input.has_directive("use strict")) node.each_argname(strict_verify_symbol);
return node; return node;
} }
@@ -1412,7 +1403,7 @@ function parse($TEXT, options) {
name: name, name: name,
argnames: argnames, argnames: argnames,
rest: argnames.rest || null, rest: argnames.rest || null,
body: body body: body,
}); });
if (is_strict) { if (is_strict) {
if (name) strict_verify_symbol(name); if (name) strict_verify_symbol(name);
@@ -2550,6 +2541,7 @@ function parse($TEXT, options) {
return function() { return function() {
var start = S.token; var start = S.token;
var body = []; var body = [];
if (options.module) S.input.add_directive("use strict");
S.input.push_directives_stack(); S.input.push_directives_stack();
while (!is("eof")) while (!is("eof"))
body.push(statement()); body.push(statement());

View File

@@ -64,19 +64,20 @@ SymbolDef.prototype = {
this.references.forEach(fn); this.references.forEach(fn);
}, },
mangle: function(options) { mangle: function(options) {
var cache = options.cache && options.cache.props; if (this.mangled_name) return;
if (this.global && cache && cache.has(this.name)) { var cache = this.global && options.cache && options.cache.props;
if (cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name); this.mangled_name = cache.get(this.name);
} else if (!this.mangled_name && !this.unmangleable(options)) { } else if (this.unmangleable(options)) {
names_in_use(this.scope, options).set(this.name, true);
} else {
var def = this.redefined(); var def = this.redefined();
if (def) { if (def) {
this.mangled_name = def.mangled_name || def.name; this.mangled_name = def.mangled_name || def.name;
} else { } else {
this.mangled_name = next_mangled_name(this, options); this.mangled_name = next_mangled_name(this, options);
} }
if (this.global && cache) { if (cache) cache.set(this.name, this.mangled_name);
cache.set(this.name, this.mangled_name);
}
} }
}, },
redefined: function() { redefined: function() {
@@ -469,6 +470,7 @@ AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
this.uses_arguments = false; this.uses_arguments = false;
this.def_variable(new AST_SymbolFunarg({ this.def_variable(new AST_SymbolFunarg({
name: "arguments", name: "arguments",
scope: this,
start: this.start, start: this.start,
end: this.end, end: this.end,
})); }));
@@ -620,14 +622,10 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
var lname = -1; var lname = -1;
var redefined = []; var redefined = [];
var tw = new TreeWalker(function(node, descend) { var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_LabeledStatement) { var save_nesting;
// `lname` is incremented when we get to the `AST_Label`
var save_nesting = lname;
descend();
if (!options.v8 || !in_label(tw)) lname = save_nesting;
return true;
}
if (node instanceof AST_BlockScope) { if (node instanceof AST_BlockScope) {
// `lname` is incremented when we get to the `AST_Label`
if (node instanceof AST_LabeledStatement) save_nesting = lname;
if (options.webkit && node instanceof AST_IterationStatement && node.init instanceof AST_Let) { if (options.webkit && node instanceof AST_IterationStatement && node.init instanceof AST_Let) {
node.init.definitions.forEach(function(defn) { node.init.definitions.forEach(function(defn) {
defn.name.match_symbol(function(sym) { defn.name.match_symbol(function(sym) {
@@ -673,6 +671,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
})); }));
} }
to_mangle.forEach(mangle); to_mangle.forEach(mangle);
if (node instanceof AST_LabeledStatement && !(options.v8 && in_label(tw))) lname = save_nesting;
return true; return true;
} }
if (node instanceof AST_Label) { if (node instanceof AST_Label) {

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"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.15.4", "version": "3.16.0",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -69,9 +69,7 @@ function make_code(ast, options) {
function parse_test(file) { function parse_test(file) {
var script = fs.readFileSync(file, "utf8"); var script = fs.readFileSync(file, "utf8");
try { try {
var ast = U.parse(script, { var ast = U.parse(script, { filename: file });
filename: file
});
} catch (e) { } catch (e) {
console.error("Caught error while parsing tests in " + file); console.error("Caught error while parsing tests in " + file);
console.error(e); console.error(e);
@@ -98,14 +96,14 @@ function parse_test(file) {
file: file, file: file,
line: node.start.line, line: node.start.line,
col: node.start.col, col: node.start.col,
code: make_code(node, { beautify: false }) code: make_code(node, { beautify: false }),
})); }));
} }
function read_string(stat) { function read_string(stat) {
if (stat.TYPE == "SimpleStatement") { if (stat.TYPE == "SimpleStatement") {
var body = stat.body; var body = stat.body;
switch(body.TYPE) { switch (body.TYPE) {
case "String": case "String":
return body.value; return body.value;
case "Array": case "Array":
@@ -142,7 +140,7 @@ function parse_test(file) {
].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", { ].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name, name: label.name,
line: label.start.line, line: label.start.line,
col: label.start.col col: label.start.col,
})); }));
var stat = node.body; var stat = node.body;
if (label.name == "expect_exact" || label.name == "node_version") { if (label.name == "expect_exact" || label.name == "node_version") {
@@ -155,12 +153,12 @@ function parse_test(file) {
var ctor = global[body.expression.name]; var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", { assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line, line: label.start.line,
col: label.start.col col: label.start.col,
})); }));
test[label.name] = ctor.apply(null, body.args.map(function(node) { test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", { assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line, line: label.start.line,
col: label.start.col col: label.start.col,
})); }));
return node.value; return node.value;
})); }));

View File

@@ -1018,3 +1018,91 @@ issue_5356: {
expect_stdout: "NaN" expect_stdout: "NaN"
node_version: ">=4" node_version: ">=4"
} }
issue_5414_1: {
options = {
arrows: true,
if_return: true,
inline: true,
toplevel: true,
}
input: {
(() => {
(() => {
if (!console)
var arguments = 42;
while (console.log(arguments));
})();
})();
}
expect: {
(() => {
if (!console)
var arguments = 42;
while (console.log(arguments));
})();
}
expect_stdout: true
node_version: ">=4"
}
issue_5414_2: {
options = {
arrows: true,
inline: true,
side_effects: true,
toplevel: true,
}
input: {
(() => {
(() => {
if (!console)
var arguments = 42;
while (console.log(arguments));
})();
})();
}
expect: {
(() => {
if (!console)
var arguments = 42;
while (console.log(arguments));
})();
}
expect_stdout: true
node_version: ">=4"
}
issue_5416: {
options = {
dead_code: true,
evaluate: true,
inline: true,
loops: true,
unused: true,
}
input: {
var f = () => {
while ((() => {
console;
var a = function g(arguments) {
console.log(arguments);
}();
})());
};
f();
}
expect: {
var f = () => {
{
arguments = void 0;
console;
console.log(arguments);
var arguments;
}
};
f();
}
expect_stdout: "undefined"
node_version: ">=4"
}

View File

@@ -1748,8 +1748,8 @@ issue_4454_2: {
expect: { expect: {
function f(a) { function f(a) {
(async function(c = console.log(a)) {})(); (async function(c = console.log(a)) {})();
var a = 42..toString(); var b = 42..toString();
console.log(a); console.log(b);
} }
f("PASS"); f("PASS");
} }
@@ -2427,80 +2427,6 @@ issue_5023_2: {
node_version: ">=8" node_version: ">=8"
} }
issue_5032_normal: {
options = {
merge_vars: true,
webkit: false,
}
input: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=8"
}
issue_5032_webkit: {
options = {
merge_vars: true,
webkit: true,
}
input: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=8"
}
issue_5034: { issue_5034: {
options = { options = {
functions: true, functions: true,
@@ -2959,3 +2885,79 @@ issue_5305_3: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=8" node_version: ">=8"
} }
issue_5456: {
options = {
inline: true,
merge_vars: true,
}
input: {
var a = true;
(function() {
(function(b, c) {
var d = async function() {
c = await null;
}();
var e = function() {
if (c)
console.log(typeof d);
while (b);
}();
})(function(i) {
return console.log("foo") && i;
}(a));
})();
}
expect: {
var a = true;
(function() {
b = (i = a, console.log("foo") && i),
d = async function() {
c = await null;
}(),
e = function() {
if (c) console.log(typeof d);
while (b);
}(),
void 0;
var b, c, d, e;
var i;
})();
}
expect_stdout: "foo"
node_version: ">=8"
}
issue_5478: {
options = {
side_effects: true,
}
input: {
A = {
get then() {
a = "FAIL";
},
};
var a = "PASS";
(async function() {
for (var b in "foo")
return void A;
})();
console.log(a);
}
expect: {
A = {
get then() {
a = "FAIL";
},
};
var a = "PASS";
(async function() {
for (var b in "foo")
return !A;
})();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=8"
}

View File

@@ -762,3 +762,27 @@ issue_5228: {
} }
expect_stdout: "true" expect_stdout: "true"
} }
issue_5469: {
options = {
assignments: true,
booleans: true,
conditionals: true,
dead_code: true,
evaluate: true,
pure_getters: "strict",
side_effects: true,
}
input: {
console.log(function f(a) {
a && 42[a = A && null];
}());
}
expect: {
console.log(function f(a) {
a && A,
0;
}());
}
expect_stdout: "undefined"
}

View File

@@ -341,7 +341,7 @@ drop_extends: {
node_version: ">=4" node_version: ">=4"
} }
keep_extends: { keep_extends_1: {
options = { options = {
toplevel: true, toplevel: true,
unused: true, unused: true,
@@ -366,6 +366,43 @@ keep_extends: {
node_version: ">=4" node_version: ">=4"
} }
keep_extends_2: {
options = {
side_effects: true,
}
input: {
"use strict";
(class extends Function {});
console.log("PASS");
}
expect: {
"use strict";
(class extends Function {});
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=4"
}
keep_extends_3: {
options = {
toplevel: true,
unused: true,
}
input: {
"use strict";
class A extends Function {}
console.log("PASS");
}
expect: {
"use strict";
(class extends Function {});
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=4"
}
drop_name: { drop_name: {
options = { options = {
unused: true, unused: true,
@@ -670,6 +707,58 @@ single_use_7: {
node_version: ">=4" node_version: ">=4"
} }
single_use_extends: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
class A extends class B {
f() {
return "PASS";
}
} {}
console.log(new A().f());
}
expect: {
"use strict";
console.log(new class extends class {
f() {
return "PASS";
}
} {}().f());
}
expect_stdout: "PASS"
node_version: ">=4"
}
single_use_extends_non_strict: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
class A extends class B {
f() {
return "PASS";
}
} {}
console.log(new A().f());
}
expect: {
console.log(new class extends class {
f() {
return "PASS";
}
} {}().f());
}
expect_stdout: "PASS"
node_version: ">=6"
}
collapse_non_strict: { collapse_non_strict: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -742,6 +831,38 @@ collapse_rhs_static: {
node_version: ">=12" node_version: ">=12"
} }
inline_non_strict: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
return a.p = "PASS";
}
class A {
g() {
return f(42);
}
}
console.log(new A().g());
}
expect: {
function f(a) {
return a.p = "PASS";
}
console.log(new class {
g() {
return f(42);
}
}().g());
}
expect_stdout: "PASS"
node_version: ">=6"
}
self_comparison: { self_comparison: {
options = { options = {
booleans: true, booleans: true,
@@ -1758,12 +1879,14 @@ issue_4962_1: {
})(function g() {}); })(function g() {});
} }
expect: { expect: {
(function g() {}), (function() {
void class { function f() {
static c = function() {
while (console.log(typeof g)); while (console.log(typeof g));
}(); }
}; (class {
static c = f();
});
})(function g() {});
} }
expect_stdout: "undefined" expect_stdout: "undefined"
node_version: ">=12" node_version: ">=12"
@@ -1796,6 +1919,37 @@ issue_4962_1_strict: {
node_version: ">=12" node_version: ">=12"
} }
issue_4962_1_strict_direct: {
options = {
ie: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
function f() {
"use strict";
while (console.log(typeof g));
}
class A {
static p = f();
}
})(function g() {});
}
expect: {
(function g() {}),
void class {
static c = function() {
"use strict";
while (console.log(typeof g));
}();
};
}
expect_stdout: "undefined"
node_version: ">=12"
}
issue_4962_2: { issue_4962_2: {
options = { options = {
ie: true, ie: true,
@@ -1815,8 +1969,11 @@ issue_4962_2: {
} }
expect: { expect: {
console.log(function f() {}(function g() { console.log(function f() {}(function g() {
function h() {
f;
}
(class { (class {
static c = f; static c = h();
}); });
})); }));
} }
@@ -1852,6 +2009,69 @@ issue_4962_2_strict: {
node_version: ">=12" node_version: ">=12"
} }
issue_4962_2_strict_direct: {
options = {
ie: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function f() {}(function g() {
function h() {
"use strict";
f;
}
class A {
static p = h();
}
}, typeof g));
}
expect: {
console.log(function f() {}(function g() {
(class {
static c = function() {
"use strict";
f;
}();
});
}));
}
expect_stdout: "undefined"
node_version: ">=12"
}
issue_4962_2_strict_direct_inline: {
options = {
directives: true,
ie: true,
inline: true,
passes: 2,
reduce_vars: true,
unused: true,
}
input: {
console.log(function f() {}(function g() {
function h() {
"use strict";
f;
}
class A {
static p = h();
}
}, typeof g));
}
expect: {
console.log(function f() {}(function g() {
(class {
static c = f;
});
}));
}
expect_stdout: "undefined"
node_version: ">=12"
}
issue_4982_1: { issue_4982_1: {
options = { options = {
dead_code: true, dead_code: true,
@@ -2541,7 +2761,7 @@ issue_5387: {
node_version: ">=4" node_version: ">=4"
} }
issue_5389: { issue_5389_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true, toplevel: true,
@@ -2571,3 +2791,94 @@ issue_5389: {
expect_stdout: "PASS PASS" expect_stdout: "PASS PASS"
node_version: ">=12" node_version: ">=12"
} }
issue_5389_2: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
function log(m, n) {
console.log(m, n);
}
var a = log;
var A = class {
[a = "FAIL"] = a = "PASS";
};
var b = new A();
log(a, b.FAIL);
}
expect: {
function log(m, n) {
console.log(m, n);
}
var a = log;
var A;
var b = new class {
[a = "FAIL"] = a = "PASS";
}();
log(a, b.FAIL);
}
expect_stdout: "PASS PASS"
node_version: ">=12"
}
issue_5436: {
options = {
merge_vars: true,
}
input: {
function f(a) {
class A {
p = a;
}
var b = "FAIL";
A == b && b();
return new A();
}
console.log(f("PASS").p);
}
expect: {
function f(a) {
class A {
p = a;
}
var b = "FAIL";
A == b && b();
return new A();
}
console.log(f("PASS").p);
}
expect_stdout: "PASS"
node_version: ">=12"
}
issue_5481: {
options = {
collapse_vars: true,
}
input: {
"use strict";
var a = "FAIL 1", log = console.log;
try {
a = "PASS";
(class extends 42 {});
log("FAIL 2", a);
} catch (e) {
log(a);
}
}
expect: {
"use strict";
var a = "FAIL 1", log = console.log;
try {
a = "PASS";
(class extends 42 {});
log("FAIL 2", a);
} catch (e) {
log(a);
}
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -1855,3 +1855,20 @@ issue_5338: {
} }
expect_stdout: true expect_stdout: true
} }
issue_5476: {
mangle = {
keep_fargs: true,
}
input: {
console.log(function(n) {
const a = 42;
}());
}
expect: {
console.log(function(n) {
const o = 42;
}());
}
expect_stdout: "undefined"
}

View File

@@ -2239,3 +2239,226 @@ issue_5407: {
] ]
node_version: ">=6" node_version: ">=6"
} }
issue_5444_1: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a = 42;
var b = function({} = setImmediate(function() {
console.log(a++);
})) {
return this;
}();
console.log(typeof b);
}
expect: {
var a = 42;
var b = function({} = setImmediate(function() {
console.log(a++);
})) {
return this;
}();
console.log(typeof b);
}
expect_stdout: [
"object",
"42",
]
node_version: ">=6"
}
issue_5444_2: {
options = {
merge_vars: true,
}
input: {
function f(a, b = a++) {
return b;
}
console.log(f("FAIL") || "PASS");
}
expect: {
function f(a, b = a++) {
return b;
}
console.log(f("FAIL") || "PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5444_3: {
options = {
merge_vars: true,
}
input: {
function f(a, b = function(c = a *= this) {
return c;
}()) {
return b;
}
console.log(f("FAIL") || "PASS");
}
expect: {
function f(a, b = function(c = a *= this) {
return c;
}()) {
return b;
}
console.log(f("FAIL") || "PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5448_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(a = typeof console.log) {
do {
var b = [ ...a ];
} while (console.log("PASS"));
})();
}
expect: {
(function(a = console.log) {
do {} while (console.log("PASS"));
})();
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5448_2: {
options = {
keep_fargs: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a = typeof console) {
do {
var b = [ ...a ];
} while (console.log("PASS"));
})();
}
expect: {
(function(a = 0) {
do {} while (console.log("PASS"));
})();
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5448_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var [ a = typeof console ] = [ void console.log("PASS") ];
var b = [ ...a ];
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5448_4: {
options = {
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var { p: a = typeof console } = { p: void console.log("PASS") };
var b = [ ...a ];
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5463: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
if (console.log("PASS"))
var a = void 0,
b = void 0,
b = ([ a = FAIL ] = b && b);
}
expect: {
var a, b, b;
console.log("PASS") && (
b = a = void 0,
b = [a = FAIL] = a && a
);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5465: {
options = {
inline: true,
merge_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a, b) {
(function(c = b = "FAIL 2") {
this && console.log(b || "PASS");
})(42 - a && a);
}
f("FAIL 1");
}
expect: {
a = "FAIL 1",
void function(c = b = "FAIL 2") {
this && console.log(b || "PASS");
}(42 - a && a);
var a, b;
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5485: {
options = {
comparisons: true,
}
input: {
(function f(f, a = console.log(void 0 === f ? "PASS" : "FAIL")) {})();
}
expect: {
(function f(f, a = console.log(void 0 === f ? "PASS" : "FAIL")) {})();
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -1848,8 +1848,8 @@ issue_4294: {
}) {}({ }) {}({
[a]: 0, [a]: 0,
}); });
var a = A; var b = A;
console.log(a); console.log(b);
})(); })();
} }
expect_stdout: "PASS" expect_stdout: "PASS"
@@ -3020,6 +3020,7 @@ issue_5074_method_pure_getters: {
issue_5085_1: { issue_5085_1: {
options = { options = {
evaluate: true, evaluate: true,
passes: 2,
reduce_vars: true, reduce_vars: true,
toplevel: true, toplevel: true,
unsafe: true, unsafe: true,
@@ -3032,8 +3033,7 @@ issue_5085_1: {
} }
expect: { expect: {
var a = "PASS"; var a = "PASS";
var b = [ 42 ][0]; 42;
b;
console.log(a); console.log(a);
} }
expect_stdout: "PASS" expect_stdout: "PASS"
@@ -3043,6 +3043,7 @@ issue_5085_1: {
issue_5085_2: { issue_5085_2: {
options = { options = {
evaluate: true, evaluate: true,
passes: 2,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
unsafe: true, unsafe: true,
@@ -3059,7 +3060,7 @@ issue_5085_2: {
expect: { expect: {
var a = "PASS"; var a = "PASS";
(function(b) { (function(b) {
b = [ 42 ][0]; 0;
})(); })();
console.log(a); console.log(a);
} }
@@ -3400,7 +3401,7 @@ issue_5222: {
node_version: ">=6" node_version: ">=6"
} }
issue_5288: { issue_5288_1: {
options = { options = {
conditionals: true, conditionals: true,
inline: true, inline: true,
@@ -3420,7 +3421,37 @@ issue_5288: {
}() ])); }() ]));
} }
expect: { expect: {
while (console ? console.log("PASS") : 0, void 0); while (function() {
if (console)
console.log("PASS");
}(), void 0);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5288_2: {
options = {
conditionals: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
varify: true,
}
input: {
while (function([]) {}([ function f() {
if (console)
return console.log("PASS");
else {
let a = 0;
}
}() ]));
}
expect: {
while (console && console.log("PASS"), void 0);
} }
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=6" node_version: ">=6"
@@ -3535,3 +3566,83 @@ issue_5405_2: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=6" node_version: ">=6"
} }
issue_5423: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a, b;
function f({
[function() {
if (++a)
return 42;
}()]: c
}) {}
f(b = f);
console.log(typeof b);
}
expect: {
var a, b;
function f({
[function() {
if (++a)
return 42;
}()]: c
}) {}
f(b = f);
console.log(typeof b);
}
expect_stdout: "function"
node_version: ">=6"
}
issue_5454: {
options = {
hoist_props: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
var a = 42, a = {
p: [ a ] = [],
};
return "PASS";
}
console.log(f());
}
expect: {
console.log(function(a) {
a = 42, a = {
p: [ a ] = [],
};
return "PASS";
}());
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5485: {
options = {
comparisons: true,
}
input: {
(function f({
p: f,
[console.log(void 0 === f ? "PASS" : "FAIL")]: a,
}) {})(42);
}
expect: {
(function f({
p: f,
[console.log(void 0 === f ? "PASS" : "FAIL")]: a,
}) {})(42);
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -496,3 +496,16 @@ issue_4766: {
export var a = "bar"; export var a = "bar";
} }
} }
issue_5444: {
options = {
unused: true,
}
input: {
export var a = (console, console);
}
expect: {
console;
export var a = console;
}
}

View File

@@ -8398,3 +8398,251 @@ issue_5409: {
} }
expect_stdout: "undefined" expect_stdout: "undefined"
} }
mixed_mode_inline_1: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return this;
}
console.log(function() {
return f();
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function() {
return this;
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
mixed_mode_inline_1_strict: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {
return this;
}
console.log(function() {
return f();
}() ? "FAIL" : "PASS");
}
expect: {
"use strict";
console.log(function() {
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_2: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
"use strict";
return this;
}
console.log(function() {
return f();
}() ? "FAIL" : "PASS");
}
expect: {
console.log(function() {
"use strict";
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_2_strict: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {
"use strict";
return this;
}
console.log(function() {
return f();
}() ? "FAIL" : "PASS");
}
expect: {
"use strict";
console.log(function() {
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_3: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "PASS" : "FAIL");
}
expect: {
function f() {
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
mixed_mode_inline_3_strict: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "FAIL" : "PASS");
}
expect: {
"use strict";
console.log(function() {
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_4: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
"use strict";
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "FAIL" : "PASS");
}
expect: {
console.log(function() {
"use strict";
return function() {
"use strict";
return this;
}();
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_4_strict: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {
"use strict";
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "FAIL" : "PASS");
}
expect: {
"use strict";
console.log(function() {
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
module_inline: {
options = {
inline: true,
module: true,
reduce_vars: true,
}
input: {
var a = f;
function f() {
return a;
}
console.log(f() === a);
}
expect: {
var a = f;
function f() {
return a;
}
console.log(a === a);
}
expect_stdout: "true"
}

View File

@@ -1176,3 +1176,30 @@ issue_5182: {
"42", "42",
] ]
} }
issue_5441: {
options = {
hoist_props: true,
passes: 2,
reduce_vars: true,
side_effects: true,
}
input: {
console.log(function(a) {
(function() {
a = { p: this };
})();
return typeof a;
}());
}
expect: {
console.log(function(a) {
(function() {
a_p = this;
})();
var a_p;
return typeof {};
}());
}
expect_stdout: "object"
}

View File

@@ -1604,48 +1604,6 @@ issue_4305_2: {
node_version: ">=4" node_version: ">=4"
} }
issue_1753: {
mangle = {
toplevel: false,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_1753_toplevel: {
mangle = {
toplevel: true,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let e = 0; e < 1; e++)
console.log(e);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_4438: { issue_4438: {
options = { options = {
if_return: true, if_return: true,
@@ -2020,3 +1978,23 @@ issue_5338: {
expect_stdout: ReferenceError("a is not defined") expect_stdout: ReferenceError("a is not defined")
node_version: ">=4" node_version: ">=4"
} }
issue_5476: {
mangle = {
keep_fargs: true,
}
input: {
"use strict";
console.log(function(n) {
let a;
}());
}
expect: {
"use strict";
console.log(function(n) {
let o;
}());
}
expect_stdout: "undefined"
node_version: ">=4"
}

View File

@@ -3702,3 +3702,146 @@ issue_5182: {
] ]
node_version: ">=4" node_version: ">=4"
} }
issue_5420: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
do {
var a = "FAIL 1";
a && a.p;
a = "FAIL 2";
try {
continue;
} catch (e) {}
var b = "FAIL 3";
} while (console.log(b || "PASS"));
}
expect: {
do {
var a = "FAIL 1";
a && a.p;
a = "FAIL 2";
try {
continue;
} catch (e) {}
var b = "FAIL 3";
} while (console.log(b || "PASS"));
}
expect_stdout: "PASS"
}
issue_5451: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
A = 1;
var a = 1, b;
console.log(function f() {
return a-- && f(b = A, b);
}());
}
expect: {
A = 1;
var a = 1, b;
console.log(function f() {
return a-- && f(b = A, b);
}());
}
expect_stdout: "0"
}
issue_5471_1: {
options = {
conditionals: true,
inline: true,
merge_vars: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL 1";
function f(b, c) {
function g() {
if (console)
return 42;
else
c = "FAIL 2";
}
var d = g();
console.log(c || "PASS");
var e = function h() {
while (b && e);
}();
}
f(a++) && a;
}
expect: {
var a = "FAIL 1";
var b, c, e;
b = +a,
function() {
if (console)
return;
c = "FAIL 2";
}(),
console.log(c || "PASS"),
e = function() {
while (b && e);
}();
}
expect_stdout: "PASS"
}
issue_5471_2: {
options = {
conditionals: true,
evaluate: true,
inline: true,
merge_vars: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL 1";
function f(b, c) {
function g() {
if (console)
return 42;
else
c = "FAIL 2";
}
var d = g();
console.log(c || "PASS");
var e = function h() {
while (b && e);
}();
}
f(a++) && a;
}
expect: {
var a = "FAIL 1";
var b, c, e;
b = +a,
function() {
if (console)
return;
c = "FAIL 2";
}(),
console.log(c || "PASS"),
e = function() {
while (b && e);
}(),
void 0;
}
expect_stdout: "PASS"
}

View File

@@ -7861,3 +7861,38 @@ issue_5324: {
} }
expect_stdout: "NaN" expect_stdout: "NaN"
} }
issue_5434: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
for (var i = 0; i < 2; i++) {
var b = "FAIL";
f && f();
a = b;
var f = function() {
b = "PASS";
};
}
return a;
}());
}
expect: {
console.log(function(a) {
for (var i = 0; i < 2; i++) {
var b = "FAIL";
f && f();
a = b;
var f = function() {
b = "PASS";
};
}
return a;
}());
}
expect_stdout: "PASS"
}

View File

@@ -111,7 +111,7 @@ hoist_props_const: {
} }
} }
expect: { expect: {
var o = 0, o_p = "PASS"; var o, o_p = "PASS";
console.log(o_p); console.log(o_p);
} }
expect_stdout: "PASS" expect_stdout: "PASS"

View File

@@ -107,3 +107,209 @@ function_name_mangle_ie8: {
expect_exact: "(function(){console.log(typeof function n(o){})})();" expect_exact: "(function(){console.log(typeof function n(o){})})();"
expect_stdout: "function" expect_stdout: "function"
} }
issue_1753: {
mangle = {
toplevel: false,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_1753_toplevel: {
mangle = {
toplevel: true,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let e = 0; e < 1; e++)
console.log(e);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_5032_await: {
options = {
merge_vars: true,
webkit: false,
}
input: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=8"
}
issue_5032_await_webkit: {
options = {
merge_vars: true,
webkit: true,
}
input: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=8"
}
issue_5032_yield: {
options = {
merge_vars: true,
webkit: false,
}
input: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS").next();
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=4"
}
issue_5032_yield_webkit: {
options = {
merge_vars: true,
webkit: true,
}
input: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=4"
}
issue_5480: {
mangle = {
webkit: true,
}
input: {
"use strict";
L: for (let a in console.log("PASS"));
}
expect: {
"use strict";
o: for (let o in console.log("PASS"));
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -978,8 +978,8 @@ issue_4454_2: {
expect: { expect: {
function f(a) { function f(a) {
(function*(c = console.log(a)) {})(); (function*(c = console.log(a)) {})();
var a = 42..toString(); var b = 42..toString();
console.log(a); console.log(b);
} }
f("PASS"); f("PASS");
} }
@@ -1267,80 +1267,6 @@ issue_5019_2: {
node_version: ">=4" node_version: ">=4"
} }
issue_5032_normal: {
options = {
merge_vars: true,
webkit: false,
}
input: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS").next();
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=4"
}
issue_5032_webkit: {
options = {
merge_vars: true,
webkit: true,
}
input: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=4"
}
issue_5034: { issue_5034: {
options = { options = {
functions: true, functions: true,
@@ -1525,3 +1451,67 @@ issue_5385_2: {
] ]
node_version: ">=10" node_version: ">=10"
} }
issue_5425: {
options = {
assignments: true,
ie: true,
toplevel: true,
unused: true,
yields: true,
}
input: {
var a = "FAIL";
var b = function* f() {}(a ? a = "PASS" : 42);
console.log(a, typeof f);
}
expect: {
var a = "FAIL";
(function* f() {})(a && (a = "PASS"));
console.log(a, typeof f);
}
expect_stdout: "PASS undefined"
node_version: ">=4"
}
issue_5456: {
options = {
inline: true,
merge_vars: true,
}
input: {
var a = true;
(function() {
(function(b, c) {
var d = function*() {
c = null;
}();
var e = function() {
if (c)
console.log(typeof d);
while (b);
}();
})(function(i) {
return console.log("foo") && i;
}(a));
})();
}
expect: {
var a = true;
(function() {
b = (i = a, console.log("foo") && i),
d = function*() {
c = null;
}(),
e = function() {
if (c) console.log(typeof d);
while (b);
}(),
void 0;
var b, c, d, e;
var i;
})();
}
expect_stdout: "foo"
node_version: ">=4"
}

View File

@@ -0,0 +1 @@
function n(){return this||arguments[0]+arguments[1]}function o(){return this||arguments[0]+arguments[1]}console.log(n(n(1,3),5)),console.log(o(o(2,4),6));

View File

@@ -0,0 +1,13 @@
console.log(function() {
function sum(...params) {
return this || arguments[0] + arguments[1];
}
return sum(sum(1, 3), 5);
}());
console.log(function() {
"use strict";
function sum(...params) {
return this || arguments[0] + arguments[1];
}
return sum(sum(2, 4), 6);
}());

View File

@@ -928,6 +928,14 @@ describe("bin/uglifyjs", function() {
done(); done();
}); });
}); });
it("Should work with --module", function(done) {
var command = uglifyjscmd + " test/input/module/input.js --module -mc";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/module/expect.js"));
done();
});
});
it("Should compress swarm of unused variables with reasonable performance", function(done) { it("Should compress swarm of unused variables with reasonable performance", function(done) {
var code = [ var code = [
"console.log(function() {", "console.log(function() {",

View File

@@ -210,10 +210,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
if (node.expression instanceof U.AST_Function) { if (node.expression instanceof U.AST_Function) {
// hoist and return expressions from the IIFE function expression // hoist and return expressions from the IIFE function expression
var seq = []; var scope = tt.find_parent(U.AST_Scope), seq = [];
node.expression.body.forEach(function(node) { node.expression.body.forEach(function(node) {
var expr = node instanceof U.AST_Exit ? node.value : node.body; var expr = node instanceof U.AST_Exit ? node.value : node.body;
if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr)) { if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr, scope)) {
// collect expressions from each statement's body // collect expressions from each statement's body
seq.push(expr); seq.push(expr);
} }
@@ -264,11 +264,12 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true; CHANGED = true;
return List.skip; return List.skip;
default: default:
if (!has_exit(node) && can_hoist(node)) { if (can_hoist(node, tt.find_parent(U.AST_Scope))) {
// hoist function declaration body // hoist function declaration body
var body = node.body; var body = node.body;
node.body = []; node.body = [];
body.push(node); // retain function with empty body to be dropped later // retain function with empty body to be dropped later
body.push(node);
CHANGED = true; CHANGED = true;
return List.splice(body); return List.splice(body);
} }
@@ -382,7 +383,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) { if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
// hoist simple statement IIFE function expression body // hoist simple statement IIFE function expression body
node.start._permute++; node.start._permute++;
if (!has_exit(node.body.expression) && can_hoist(node.body.expression)) { if (can_hoist(node.body.expression, tt.find_parent(U.AST_Scope))) {
CHANGED = true; CHANGED = true;
return List.splice(node.body.expression.body); return List.splice(node.body.expression.body);
} }
@@ -647,21 +648,6 @@ function trim_trailing_whitespace(value) {
return ("" + value).replace(/\s+$/, ""); return ("" + value).replace(/\s+$/, "");
} }
function has_exit(fn) {
var found = false;
var tw = new U.TreeWalker(function(node) {
if (found) return found;
if (node instanceof U.AST_Exit) {
return found = true;
}
if (node instanceof U.AST_Scope && node !== fn) {
return true; // don't descend into nested functions
}
});
fn.walk(tw);
return found;
}
function has_loopcontrol(body, loop, label) { function has_loopcontrol(body, loop, label) {
var found = false; var found = false;
var tw = new U.TreeWalker(function(node) { var tw = new U.TreeWalker(function(node) {
@@ -676,17 +662,31 @@ function has_loopcontrol(body, loop, label) {
return found; return found;
} }
function can_hoist(body) { function can_hoist(body, scope) {
var found = false; var found = false;
body.walk(new U.TreeWalker(function(node) { var tw = new U.TreeWalker(function(node) {
if (found) return true; if (found) return true;
if (node instanceof U.AST_Exit) return found = true;
if (node instanceof U.AST_NewTarget) return found = true; if (node instanceof U.AST_NewTarget) return found = true;
if (node instanceof U.AST_Scope) { if (node instanceof U.AST_Scope) {
if (node === body) return; if (node === body) return;
if (node instanceof U.AST_Arrow || node instanceof U.AST_AsyncArrow) node.argnames.forEach(function(sym) {
sym.walk(tw);
});
// don't descend into nested functions
return true; return true;
} }
if (node instanceof U.AST_Super) return found = true; if (node instanceof U.AST_Super) return found = true;
})); if (node instanceof U.AST_SymbolDeclaration || node instanceof U.AST_SymbolRef) switch (node.name) {
case "await":
if (/^Async/.test(scope.TYPE)) return found = true;
return;
case "yield":
if (/Generator/.test(scope.TYPE)) return found = true;
return;
}
});
body.walk(tw);
return !found; return !found;
} }

View File

@@ -54,7 +54,7 @@ ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "src" \ minify_in_situ "src" \
&& minify_in_situ "third_party" \ && minify_in_situ "third_party" \
&& rm -rf node_modules \ && rm -rf node_modules \
&& npm_install \ && npm_install --package-lock \
&& rm -rf build/* \ && rm -rf build/* \
&& npm run build:terser-bundled \ && npm run build:terser-bundled \
&& npm run build:uglify-js-bundled \ && npm run build:uglify-js-bundled \

View File

@@ -52,8 +52,11 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual); return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
}; };
exports.patch_module_statements = function(code) { exports.patch_module_statements = function(code) {
var count = 0, has_default = "", imports = []; var count = 0, has_default = "", imports = [], strict_mode = "";
code = code.replace(/\bexport(?:\s*\{[^{}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) { code = code.replace(/^\s*("|')use strict\1\s*;?/, function(match) {
strict_mode = match;
return "";
}).replace(/\bexport(?:\s*\{[^{}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) {
if (/^export\s+default/.test(match)) has_default = "var _uglify_export_default_;"; if (/^export\s+default/.test(match)) has_default = "var _uglify_export_default_;";
if (!header) return ""; if (!header) return "";
if (header.length == 1) return "0, " + header; if (header.length == 1) return "0, " + header;
@@ -78,7 +81,7 @@ exports.patch_module_statements = function(code) {
return ""; return "";
}); });
imports.push(""); imports.push("");
return has_default + imports.join("\n") + code; return strict_mode + has_default + imports.join("\n") + code;
}; };
function is_error(result) { function is_error(result) {

View File

@@ -128,7 +128,7 @@ for (var i = 2; i < process.argv.length; ++i) {
var SUPPORT = function(matrix) { var SUPPORT = function(matrix) {
for (var name in matrix) { for (var name in matrix) {
matrix[name] = typeof sandbox.run_code(matrix[name]) == "string"; matrix[name] = !sandbox.is_error(sandbox.run_code(matrix[name]));
} }
return matrix; return matrix;
}({ }({
@@ -1805,7 +1805,7 @@ function createClassLiteral(recurmax, stmtDepth, canThrow, name) {
if (canThrow && rng(20) == 0) { if (canThrow && rng(20) == 0) {
s += p; s += p;
} else { } else {
s += "(" + p + " && " + p + ".constructor === Function ? " + p + " : function() {})"; s += "(typeof " + p + ' == "function" && typeof ' + p + '.prototype == "object" && ' + p + ".constructor === Function ? " + p + " : function() {})";
} }
} }
s += " {\n"; s += " {\n";
@@ -1824,9 +1824,15 @@ function createClassLiteral(recurmax, stmtDepth, canThrow, name) {
declared.push(internal); declared.push(internal);
} }
if (SUPPORT.class_field && rng(2)) { if (SUPPORT.class_field && rng(2)) {
s += internal || createObjectKey(recurmax, stmtDepth, canThrow); if (internal) {
s += internal;
} else if (fixed && bug_class_static_nontrivial) {
s += getDotKey();
} else {
s += createObjectKey(recurmax, stmtDepth, canThrow);
}
if (rng(5)) { if (rng(5)) {
async = bug_async_class_await && fixed && 0; async = false;
generator = false; generator = false;
s += " = " + createExpression(recurmax, NO_COMMA, stmtDepth, fixed ? canThrow : CANNOT_THROW); s += " = " + createExpression(recurmax, NO_COMMA, stmtDepth, fixed ? canThrow : CANNOT_THROW);
generator = save_generator; generator = save_generator;
@@ -2031,7 +2037,7 @@ function isBannedKeyword(name) {
case "let": case "let":
return in_class; return in_class;
case "await": case "await":
return async !== false; return async || in_class && bug_class_static_await;
case "yield": case "yield":
return generator || in_class; return generator || in_class;
} }
@@ -2110,7 +2116,7 @@ function try_beautify(code, toplevel, result, printfn, options) {
} else if (options) { } else if (options) {
var uglified = UglifyJS.minify(beautified.code, JSON.parse(options)); var uglified = UglifyJS.minify(beautified.code, JSON.parse(options));
var expected, actual; var expected, actual;
if (typeof uglify_code != "string" || uglified.error) { if (sandbox.is_error(uglify_code) || uglified.error) {
expected = uglify_code; expected = uglify_code;
actual = uglified.error; actual = uglified.error;
} else { } else {
@@ -2147,7 +2153,7 @@ function log_suspects(minify_options, component) {
m[component] = o; m[component] = o;
m.validate = true; m.validate = true;
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (typeof uglify_code != "string") { if (sandbox.is_error(uglify_code)) {
return !sandbox.same_stdout(uglify_code, result.error); return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) { } else if (result.error) {
errorln("Error testing options." + component + "." + name); errorln("Error testing options." + component + "." + name);
@@ -2175,7 +2181,7 @@ function log_suspects_global(options, toplevel) {
m[component] = false; m[component] = false;
m.validate = true; m.validate = true;
var result = UglifyJS.minify(original_code, m); var result = UglifyJS.minify(original_code, m);
if (typeof uglify_code != "string") { if (sandbox.is_error(uglify_code)) {
return !sandbox.same_stdout(uglify_code, result.error); return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) { } else if (result.error) {
errorln("Error testing options." + component); errorln("Error testing options." + component);
@@ -2204,7 +2210,16 @@ function log(options) {
errorln(); errorln();
errorln(); errorln();
errorln("//-------------------------------------------------------------"); errorln("//-------------------------------------------------------------");
if (typeof uglify_code == "string") { if (sandbox.is_error(uglify_code)) {
errorln("// !!! uglify failed !!!");
errorln(uglify_code);
if (original_erred) {
errorln();
errorln();
errorln("original stacktrace:");
errorln(original_result);
}
} else {
errorln("// uglified code"); errorln("// uglified code");
try_beautify(uglify_code, toplevel, uglify_result, errorln); try_beautify(uglify_code, toplevel, uglify_result, errorln);
errorln(); errorln();
@@ -2213,15 +2228,6 @@ function log(options) {
errorln(original_result); errorln(original_result);
errorln("uglified result:"); errorln("uglified result:");
errorln(uglify_result); errorln(uglify_result);
} else {
errorln("// !!! uglify failed !!!");
errorln(uglify_code);
if (errored) {
errorln();
errorln();
errorln("original stacktrace:");
errorln(original_result);
}
} }
errorln("//-------------------------------------------------------------"); errorln("//-------------------------------------------------------------");
if (!ok) { if (!ok) {
@@ -2318,20 +2324,28 @@ function is_error_in(ex) {
return ex.name == "TypeError" && /'in'/.test(ex.message); return ex.name == "TypeError" && /'in'/.test(ex.message);
} }
function is_error_tdz(ex) {
return ex.name == "ReferenceError";
}
function is_error_spread(ex) { function is_error_spread(ex) {
return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| not a function/.test(ex.message); return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| not a function|Symbol\(Symbol\.iterator\)/.test(ex.message);
} }
function is_error_recursion(ex) { function is_error_recursion(ex) {
return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message); return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message);
} }
function is_error_set_property(ex) {
return ex.name == "TypeError" && /^Cannot set propert[\s\S]+? of (null|undefined)/.test(ex.message);
}
function is_error_redeclaration(ex) { function is_error_redeclaration(ex) {
return ex.name == "SyntaxError" && /already been declared|redeclaration/.test(ex.message); return ex.name == "SyntaxError" && /already been declared|redeclaration/.test(ex.message);
} }
function is_error_destructuring(ex) { function is_error_destructuring(ex) {
return ex.name == "TypeError" && /^Cannot destructure /.test(ex.message); return ex.name == "TypeError" && /^Cannot (destructure|read propert)/.test(ex.message);
} }
function is_error_class_constructor(ex) { function is_error_class_constructor(ex) {
@@ -2345,6 +2359,8 @@ function is_error_getter_only_property(ex) {
} }
function patch_try_catch(orig, toplevel) { function patch_try_catch(orig, toplevel) {
var patched = Object.create(null);
var patches = [];
var stack = [ { var stack = [ {
code: orig, code: orig,
index: 0, index: 0,
@@ -2382,7 +2398,7 @@ function patch_try_catch(orig, toplevel) {
"throw " + match[1] + ";", "throw " + match[1] + ";",
].join("\n"); ].join("\n");
} }
var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw; var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw + "var UFUZZ_ERROR;";
var result = run_code(new_code, toplevel); var result = run_code(new_code, toplevel);
if (!sandbox.is_error(result)) { if (!sandbox.is_error(result)) {
if (!stack.filled && match[1]) stack.push({ if (!stack.filled && match[1]) stack.push({
@@ -2394,27 +2410,35 @@ function patch_try_catch(orig, toplevel) {
offset += insert.length; offset += insert.length;
code = new_code; code = new_code;
} else if (is_error_in(result)) { } else if (is_error_in(result)) {
index = result.ufuzz_catch; patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("invalid `in`");');
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("invalid `in`");' + orig.slice(index); } else if (is_error_tdz(result)) {
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("TDZ");');
} else if (is_error_spread(result)) { } else if (is_error_spread(result)) {
index = result.ufuzz_catch; patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("spread not iterable");');
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("spread not iterable");' + orig.slice(index);
} else if (is_error_recursion(result)) { } else if (is_error_recursion(result)) {
index = result.ufuzz_try; patch(result.ufuzz_try, 'throw new Error("skipping infinite recursion");');
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index); } else if (is_error_set_property(result)) {
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("cannot set property");');
} else if (is_error_destructuring(result)) { } else if (is_error_destructuring(result)) {
index = result.ufuzz_catch; patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("cannot destructure");');
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("cannot destructure");' + orig.slice(index);
} else if (is_error_class_constructor(result)) { } else if (is_error_class_constructor(result)) {
index = result.ufuzz_catch; patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("missing new for class");');
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("missing new for class");' + orig.slice(index);
} else if (is_error_getter_only_property(result)) { } else if (is_error_getter_only_property(result)) {
index = result.ufuzz_catch; patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("setting getter-only property");');
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("setting getter-only property");' + orig.slice(index);
} }
} }
stack.filled = true; stack.filled = true;
} }
if (patches.length) return patches.reduce(function(code, patch) {
var index = patch[0];
return code.slice(0, index) + patch[1] + code.slice(index);
}, orig);
function patch(index, code) {
if (patched[index]) return;
patched[index] = true;
patches.unshift([ index, code ]);
}
} }
var beautify_options = { var beautify_options = {
@@ -2426,22 +2450,23 @@ var beautify_options = {
}, },
}; };
var minify_options = require("./options.json"); var minify_options = require("./options.json");
if (typeof sandbox.run_code("A:if (0) B:; else B:;") != "string") { if (sandbox.is_error(sandbox.run_code("A:if (0) B:; else B:;"))) {
minify_options.forEach(function(o) { minify_options.forEach(function(o) {
if (!("mangle" in o)) o.mangle = {}; if (!("mangle" in o)) o.mangle = {};
if (o.mangle) o.mangle.v8 = true; if (o.mangle) o.mangle.v8 = true;
}); });
} }
var bug_async_arrow_rest = function() {}; var bug_async_arrow_rest = function() {};
if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && typeof sandbox.run_code("async (a = f(...[], b)) => 0;") != "string") { if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && sandbox.is_error(sandbox.run_code("async (a = f(...[], b)) => 0;"))) {
bug_async_arrow_rest = function(ex) { bug_async_arrow_rest = function(ex) {
return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter"; return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter";
}; };
} }
var bug_async_class_await = SUPPORT.async && SUPPORT.class_field && typeof sandbox.run_code("var await; async function f() { class A { static p = await; } }") != "string"; var bug_class_static_await = SUPPORT.async && SUPPORT.class_field && sandbox.is_error(sandbox.run_code("var await; class A { static p = await; }"));
var bug_for_of_async = SUPPORT.for_await_of && typeof sandbox.run_code("var async; for (async of []);") != "string"; var bug_class_static_nontrivial = SUPPORT.class_field && sandbox.is_error(sandbox.run_code("class A { static 42; static get 42() {} }"));
var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && typeof sandbox.run_code("try {} catch (e) { for (var e of []); }") != "string"; var bug_for_of_async = SUPPORT.for_await_of && sandbox.is_error(sandbox.run_code("var async; for (async of []);"));
if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") { var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && sandbox.is_error(sandbox.run_code("try {} catch (e) { for (var e of []); }"));
if (SUPPORT.destructuring && sandbox.is_error(sandbox.run_code("console.log([ 1 ], {} = 2);"))) {
beautify_options.output.v8 = true; beautify_options.output.v8 = true;
minify_options.forEach(function(o) { minify_options.forEach(function(o) {
if (!("output" in o)) o.output = {}; if (!("output" in o)) o.output = {};
@@ -2450,7 +2475,7 @@ if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2)
} }
beautify_options = JSON.stringify(beautify_options); beautify_options = JSON.stringify(beautify_options);
minify_options = minify_options.map(JSON.stringify); minify_options = minify_options.map(JSON.stringify);
var original_code, original_result, errored; var original_code, original_result, original_erred;
var uglify_code, uglify_result, ok; var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r"); process.stdout.write(round + " of " + num_iterations + "\r");
@@ -2458,7 +2483,7 @@ for (var round = 1; round <= num_iterations; round++) {
original_code = createTopLevelCode(); original_code = createTopLevelCode();
var orig_result = [ run_code(original_code), run_code(original_code, true) ]; var orig_result = [ run_code(original_code), run_code(original_code, true) ];
if (orig_result.some(function(result, toplevel) { if (orig_result.some(function(result, toplevel) {
if (typeof result == "string") return; if (!sandbox.is_error(result)) return;
println(); println();
println(); println();
println("//============================================================="); println("//=============================================================");
@@ -2480,19 +2505,20 @@ for (var round = 1; round <= num_iterations; round++) {
o.validate = true; o.validate = true;
uglify_code = UglifyJS.minify(original_code, o); uglify_code = UglifyJS.minify(original_code, o);
original_result = orig_result[toplevel ? 1 : 0]; original_result = orig_result[toplevel ? 1 : 0];
errored = typeof original_result != "string"; original_erred = sandbox.is_error(original_result);
if (!uglify_code.error) { if (!uglify_code.error) {
uglify_code = uglify_code.code; uglify_code = uglify_code.code;
uglify_result = run_code(uglify_code, toplevel); uglify_result = run_code(uglify_code, toplevel);
ok = sandbox.same_stdout(original_result, uglify_result); ok = sandbox.same_stdout(original_result, uglify_result);
var uglify_erred = sandbox.is_error(uglify_result);
// ignore v8 parser bug // ignore v8 parser bug
if (!ok && bug_async_arrow_rest(uglify_result)) ok = true; if (!ok && uglify_erred && bug_async_arrow_rest(uglify_result)) ok = true;
// ignore runtime platform bugs // ignore runtime platform bugs
if (!ok && uglify_result.message == "Script execution aborted.") ok = true; if (!ok && uglify_erred && uglify_result.message == "Script execution aborted.") ok = true;
// handle difference caused by time-outs // handle difference caused by time-outs
if (!ok) { if (!ok) {
if (errored && is_error_timeout(original_result)) { if (original_erred && is_error_timeout(original_result)) {
if (is_error_timeout(uglify_result)) { if (uglify_erred && is_error_timeout(uglify_result)) {
// ignore difference in error message // ignore difference in error message
ok = true; ok = true;
} else { } else {
@@ -2500,21 +2526,23 @@ for (var round = 1; round <= num_iterations; round++) {
if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = run_code(original_code, toplevel, 10000); if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = run_code(original_code, toplevel, 10000);
ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result); ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result);
} }
} else if (is_error_timeout(uglify_result)) { } else if (uglify_erred && is_error_timeout(uglify_result)) {
// ignore spurious time-outs // ignore spurious time-outs
var waited_result = run_code(uglify_code, toplevel, 10000); var waited_result = run_code(uglify_code, toplevel, 10000);
ok = sandbox.same_stdout(original_result, waited_result); ok = sandbox.same_stdout(original_result, waited_result);
} }
} }
// ignore declaration order of global variables // ignore declaration order of global variables
if (!ok && !toplevel && uglify_result.name != "SyntaxError" && original_result.name != "SyntaxError") { if (!ok && !toplevel) {
ok = sandbox.same_stdout(run_code(sort_globals(original_code)), run_code(sort_globals(uglify_code))); if (!(original_erred && original_result.name == "SyntaxError") && !(uglify_erred && uglify_result.name == "SyntaxError")) {
ok = sandbox.same_stdout(run_code(sort_globals(original_code)), run_code(sort_globals(uglify_code)));
}
} }
// ignore numerical imprecision caused by `unsafe_math` // ignore numerical imprecision caused by `unsafe_math`
if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == typeof uglify_result) { if (!ok && o.compress && o.compress.unsafe_math) {
if (typeof original_result == "string") { if (typeof original_result == "string" && typeof uglify_result == "string") {
ok = fuzzy_match(original_result, uglify_result); ok = fuzzy_match(original_result, uglify_result);
} else if (sandbox.is_error(original_result)) { } else if (original_erred && uglify_erred) {
ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message); ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message);
} }
if (!ok) { if (!ok) {
@@ -2522,46 +2550,33 @@ for (var round = 1; round <= num_iterations; round++) {
ok = sandbox.same_stdout(fuzzy_result, uglify_result); ok = sandbox.same_stdout(fuzzy_result, uglify_result);
} }
} }
// ignore difference in error message caused by Temporal Dead Zone if (!ok && original_erred && uglify_erred && (
if (!ok && errored && uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError") ok = true; // ignore difference in error message caused by `in`
// ignore difference due to implicit strict-mode in `class` is_error_in(original_result) && is_error_in(uglify_result)
if (!ok && /\bclass\b/.test(original_code)) { // ignore difference in error message caused by Temporal Dead Zone
var original_strict = run_code('"use strict";\n' + original_code, toplevel); || is_error_tdz(original_result) && is_error_tdz(uglify_result)
if (/^(Syntax|Type)Error$/.test(uglify_result.name)) { // ignore difference in error message caused by spread syntax
ok = typeof original_strict != "string"; || is_error_spread(original_result) && is_error_spread(uglify_result)
} else { // ignore difference in error message caused by destructuring assignment
ok = sandbox.same_stdout(original_strict, uglify_result); || is_error_set_property(original_result) && is_error_set_property(uglify_result)
} // ignore difference in error message caused by `import` symbol redeclaration
} || /\bimport\b/.test(original_code) && is_error_redeclaration(original_result) && is_error_redeclaration(uglify_result)
// ignore difference in error message caused by `import` symbol redeclaration // ignore difference in error message caused by destructuring
if (!ok && errored && /\bimport\b/.test(original_code)) { || is_error_destructuring(original_result) && is_error_destructuring(uglify_result)
if (is_error_redeclaration(uglify_result) && is_error_redeclaration(original_result)) ok = true; // ignore difference in error message caused by call on class
} || is_error_class_constructor(original_result) && is_error_class_constructor(uglify_result)
// ignore difference in error message caused by setting getter-only property
|| is_error_getter_only_property(original_result) && is_error_getter_only_property(uglify_result)
)) ok = true;
// ignore difference due to `__proto__` assignment // ignore difference due to `__proto__` assignment
if (!ok && /\b__proto__\b/.test(original_code)) { if (!ok && /\b__proto__\b/.test(original_code)) {
var original_proto = run_code("(" + patch_proto + ")();\n" + original_code, toplevel); var original_proto = run_code("(" + patch_proto + ")();\n" + original_code, toplevel);
var uglify_proto = run_code("(" + patch_proto + ")();\n" + uglify_code, toplevel); var uglify_proto = run_code("(" + patch_proto + ")();\n" + uglify_code, toplevel);
ok = sandbox.same_stdout(original_proto, uglify_proto); ok = sandbox.same_stdout(original_proto, uglify_proto);
} }
// ignore difference in error message caused by `in`
if (!ok && errored && is_error_in(uglify_result) && is_error_in(original_result)) ok = true;
// ignore difference in error message caused by spread syntax
if (!ok && errored && is_error_spread(uglify_result) && is_error_spread(original_result)) ok = true;
// ignore difference in depth of termination caused by infinite recursion // ignore difference in depth of termination caused by infinite recursion
if (!ok && errored && is_error_recursion(original_result)) { if (!ok && original_erred && is_error_recursion(original_result)) {
if (is_error_recursion(uglify_result) || typeof uglify_result == "string") ok = true; if (!uglify_erred || is_error_recursion(uglify_result)) ok = true;
}
// ignore difference in error message caused by destructuring
if (!ok && errored && is_error_destructuring(uglify_result) && is_error_destructuring(original_result)) {
ok = true;
}
// ignore difference in error message caused by call on class
if (!ok && errored && is_error_class_constructor(uglify_result) && is_error_class_constructor(original_result)) {
ok = true;
}
// ignore difference in error message caused by setting getter-only property
if (!ok && errored && is_error_getter_only_property(uglify_result) && is_error_getter_only_property(original_result)) {
ok = true;
} }
// ignore errors above when caught by try-catch // ignore errors above when caught by try-catch
if (!ok) { if (!ok) {
@@ -2573,7 +2588,7 @@ for (var round = 1; round <= num_iterations; round++) {
} }
} else { } else {
uglify_code = uglify_code.error; uglify_code = uglify_code.error;
ok = errored && uglify_code.name == original_result.name; ok = original_erred && uglify_code.name == original_result.name;
} }
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
if (!ok && isFinite(num_iterations)) { if (!ok && isFinite(num_iterations)) {