Compare commits

..

28 Commits

Author SHA1 Message Date
Alex Lam S.L
aa2a9fbedb v3.16.2 2022-07-04 08:50:56 +08:00
Alex Lam S.L
3596b4feda fix corner case in inline (#5537)
fixes #5536
2022-07-02 00:10:02 +08:00
Alex Lam S.L
51deeff72e enhance inline (#5535) 2022-07-01 11:24:16 +08:00
Alex Lam S.L
4c227cc6bd fix corner cases in inline & unused (#5534)
fixes #5533
2022-06-30 15:34:45 +08:00
Alex Lam S.L
2426657daa fix corner case in inline (#5532)
fixes #5531
2022-06-30 04:09:53 +08:00
Alex Lam S.L
e1b03d0235 fix corner case in inline (#5529)
fixes #5528
2022-06-29 07:37:58 +08:00
Alex Lam S.L
f1b3e9df1e fix corner case in inline (#5527)
fixes #5526
2022-06-26 20:48:14 +08:00
Alex Lam S.L
fcc87edb71 fix corner cases in dead_code & if_return (#5525)
fixes #5521
fixes #5522
fixes #5523
fixes #5524
2022-06-26 18:40:56 +08:00
Alex Lam S.L
8b464331ba enhance dead_code & if_return (#5520) 2022-06-26 12:32:25 +08:00
Alex Lam S.L
9f57920566 enhance if_return (#5518) 2022-06-24 00:52:22 +08:00
Alex Lam S.L
933ca9ddd8 fix corner case in reduce_vars (#5517)
fixes #5516
2022-06-19 03:27:00 +08:00
Alex Lam S.L
74e36e4456 v3.16.1 2022-06-17 07:53:29 +08:00
Alex Lam S.L
4382bfe848 fix corner case in collapse_vars (#5513)
fixes #5512
2022-06-13 07:55:15 +08:00
Alex Lam S.L
b6f250f5c9 enhance unused (#5511) 2022-06-12 21:24:42 +08:00
Alex Lam S.L
5d69545299 enhance unsafe_comps (#5510) 2022-06-12 12:15:43 +08:00
Alex Lam S.L
139fad0c05 fix corner cases with instanceof (#5509)
- enhance `evaluate`
2022-06-12 10:01:54 +08:00
Alex Lam S.L
99946a3993 fix corner case in dead_code (#5507)
fixes #5506
2022-06-12 05:26:51 +08:00
Alex Lam S.L
053cb27fe3 fix corner case in collapse_vars (#5505)
fixes #5504
2022-06-10 09:12:59 +08:00
Alex Lam S.L
25017978e7 fix corner case in collapse_vars (#5503)
fixes #5502
2022-06-10 02:07:07 +08:00
Alex Lam S.L
f749863cb2 document ECMAScript quirks (#5501)
closes #5500
2022-06-09 03:01:00 +08:00
Alex Lam S.L
123f9cf987 fix corner case in hoist_props (#5499)
fixes #5498
2022-06-07 23:29:42 +08:00
Alex Lam S.L
a758b40e3f suppress false positives in ufuzz (#5497) 2022-06-07 23:28:06 +08:00
Alex Lam S.L
44e5e99aae parse directives within arrow functions correctly (#5496)
fixes #5495
2022-06-07 10:33:17 +08:00
Alex Lam S.L
be53c4838b fix corner case in collapse_vars (#5494)
fixes #5493
2022-06-06 23:36:19 +08:00
Alex Lam S.L
0c7b016fa7 fix corner case in inline & module (#5492)
fixes #5491
2022-06-06 22:52:22 +08:00
Alex Lam S.L
00665766da fix corner case in side_effects (#5490)
fixes #5489
2022-06-06 20:32:32 +08:00
Alex Lam S.L
88b4283200 support class static initialization block (#5488) 2022-06-06 12:01:15 +08:00
Alex Lam S.L
d2bd0d1c1c support top-level await (#5487) 2022-06-06 11:52:01 +08:00
33 changed files with 2790 additions and 186 deletions

View File

@@ -519,7 +519,8 @@ if (result.error) throw result.error;
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.
ES module, i.e. implicit `"use strict";` and support for top-level `await`,
alongside with `toplevel` enabled.
- `nameCache` (default: `null`) — pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
@@ -632,7 +633,13 @@ to be `false` and all symbol names will be omitted.
- `bare_returns` (default: `false`) — support top level `return` statements
- `html5_comments` (default: `true`)
- `expression` (default: `false`) — parse as a single expression, e.g. JSON
- `html5_comments` (default: `true`) — process HTML comment as workaround for
browsers which do not recognise `<script>` tags
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` and support for top-level `await`.
- `shebang` (default: `true`) — support `#!command` as the first line
@@ -811,8 +818,9 @@ to be `false` and all symbol names will be omitted.
- `unsafe` (default: `false`) — apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: `false`) — compress expressions like `a <= b` assuming
none of the operands can be (coerced to) `NaN`.
- `unsafe_comps` (default: `false`) — assume operands cannot be (coerced to) `NaN`
in numeric comparisons, e.g. `a <= b`. In addition, expressions involving `in`
or `instanceof` would never throw.
- `unsafe_Function` (default: `false`) — compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
@@ -1415,9 +1423,20 @@ To allow for better optimizations, the compiler makes various assumptions:
function f() {
throw 42;
}
} catch (e) {}
console.log(typeof f);
// Expected: "function"
// Actual: "undefined"
} catch (e) {
console.log(typeof f, e);
}
// Expected: "function 42"
// Actual: "undefined 42"
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
"use strict";
console.log(function f() {
return f = "PASS";
}());
// Expected: "PASS"
// Actual: TypeError: invalid assignment to const 'f'
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -534,7 +534,7 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "fn_defs may_call_this uses_eval uses_with", {
$documentation: "Base class for all statements introducing a lexical scope",
$documentation: "Base class for all statements introducing a lambda scope",
$propdoc: {
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
@@ -592,6 +592,10 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
}
}, AST_Scope);
var AST_ClassInitBlock = DEFNODE("ClassInitBlock", null, {
$documentation: "Value for `class` static initialization blocks",
}, AST_Scope);
var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_arguments", {
$documentation: "Base class for functions",
$propdoc: {
@@ -874,7 +878,7 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, {
var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
$documentation: "Base class for `class` properties",
$propdoc: {
key: "[string|AST_Node] property name (AST_Node for computed property)",
key: "[string|AST_Node?] property name (AST_Node for computed property, null for initialization block)",
private: "[boolean] whether this is a private property",
static: "[boolean] whether this is a static property",
value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)",
@@ -888,7 +892,9 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
},
_validate: function() {
if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty");
if (typeof this.key != "string") {
if (this instanceof AST_ClassInit) {
if (this.key != null) throw new Error("key must be null");
} else if (typeof this.key != "string") {
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
must_be_expression(this, "key");
}
@@ -928,6 +934,17 @@ var AST_ClassMethod = DEFNODE("ClassMethod", null, {
},
}, AST_ClassProperty);
var AST_ClassInit = DEFNODE("ClassInit", null, {
$documentation: "A `class` static initialization block",
_validate: function() {
if (!this.static) throw new Error("static must be true");
if (!(this.value instanceof AST_ClassInitBlock)) throw new Error("value must be AST_ClassInitBlock");
},
initialize: function() {
this.static = true;
},
}, AST_ClassProperty);
/* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, {

View File

@@ -1146,7 +1146,7 @@ Compressor.prototype.compress = function(node) {
}
}
props.forEach(function(prop) {
if (!prop.static || prop instanceof AST_ClassField && prop.value.contains_this()) {
if (!prop.static || is_static_field_or_init(prop) && prop.value.contains_this()) {
push(tw);
prop.value.walk(tw);
pop(tw);
@@ -1156,6 +1156,14 @@ Compressor.prototype.compress = function(node) {
});
return true;
});
def(AST_ClassInitBlock, function(tw, descend, compressor) {
var node = this;
push(tw);
reset_variables(tw, compressor, node);
descend();
pop_scope(tw, node);
return true;
});
def(AST_Conditional, function(tw) {
this.condition.walk(tw);
push(tw);
@@ -1590,8 +1598,7 @@ Compressor.prototype.compress = function(node) {
AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
function convert_symbol(type, process) {
var node = make_node(type, this, this);
process(node, this);
return node;
return process(node, this) || node;
}
AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
@@ -1843,6 +1850,10 @@ Compressor.prototype.compress = function(node) {
|| compressor.option("unsafe") && global_names[this.name];
});
function is_static_field_or_init(prop) {
return prop.static && prop.value && (prop instanceof AST_ClassField || prop instanceof AST_ClassInit);
}
function declarations_only(node) {
return all(node.definitions, function(var_def) {
return !var_def.value;
@@ -1852,8 +1863,7 @@ Compressor.prototype.compress = function(node) {
function is_declaration(stat, lexical) {
if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) {
if (prop.key instanceof AST_Node) return false;
if (prop instanceof AST_ClassField && prop.static && prop.value) return false;
return true;
return !is_static_field_or_init(prop);
});
if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat);
if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical);
@@ -1940,13 +1950,25 @@ Compressor.prototype.compress = function(node) {
return statements;
function last_of(compressor, predicate) {
var block = compressor.self(), stat, level = 0;
var block = compressor.self(), level = 0;
do {
do {
if (block instanceof AST_Catch) {
block = compressor.parent(level++);
} else if (block instanceof AST_LabeledStatement) {
block = block.body;
}
var stat = null;
while (true) {
if (predicate(block)) return true;
block = compressor.parent(level++);
} while (block instanceof AST_If && (stat = block));
} while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
if (!(block instanceof AST_If)) break;
stat = block;
}
} while (stat
&& (block instanceof AST_BlockStatement
|| block instanceof AST_Catch
|| block instanceof AST_Scope
|| block instanceof AST_Try)
&& is_last_statement(block.body, stat));
}
@@ -2172,7 +2194,7 @@ Compressor.prototype.compress = function(node) {
can_replace = replace;
return signal_abort(node);
}
return handle_custom_scan_order(node, scanner);
if (handle_custom_scan_order(node, scanner)) return signal_abort(node);
}, signal_abort);
var multi_replacer = new TreeTransformer(function(node) {
if (abort) return node;
@@ -2334,14 +2356,33 @@ Compressor.prototype.compress = function(node) {
}
function handle_custom_scan_order(node, tt) {
if (!(node instanceof AST_BlockScope)) {
if (!(node instanceof AST_ClassProperty && !node.static)) return;
// Skip non-static class property values
if (node.key instanceof AST_Node) node.key = node.key.transform(tt);
return node;
}
if (!(node instanceof AST_BlockScope)) return;
// Skip (non-executed) functions
if (node instanceof AST_Scope) return node;
// Scan computed keys, static fields & initializers in class
if (node instanceof AST_Class) {
if (node.name) node.name = node.name.transform(tt);
if (!abort && node.extends) node.extends = node.extends.transform(tt);
var fields = [], stats = [];
for (var i = 0; !abort && i < node.properties.length; i++) {
var prop = node.properties[i];
if (prop.key instanceof AST_Node) prop.key = prop.key.transform(tt);
if (!prop.static) continue;
if (prop instanceof AST_ClassField) {
if (prop.value) fields.push(prop);
} else if (prop instanceof AST_ClassInit) {
[].push.apply(stats, prop.value.body);
}
}
for (var i = 0; !abort && i < stats.length; i++) {
stats[i].transform(tt);
}
for (var i = 0; !abort && i < fields.length; i++) {
var prop = fields[i];
prop.value = prop.value.transform(tt);
}
return node;
}
// Scan object only in a for-in/of statement
if (node instanceof AST_ForEnumeration) {
node.object = node.object.transform(tt);
@@ -2427,7 +2468,7 @@ Compressor.prototype.compress = function(node) {
function is_last_node(node, parent) {
if (node instanceof AST_Await) return true;
if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right);
if (node.TYPE == "Binary") return !can_drop_op(node.operator, node.right, compressor);
if (node instanceof AST_Call) {
var def, fn = node.expression;
if (fn instanceof AST_SymbolRef) {
@@ -2820,6 +2861,7 @@ Compressor.prototype.compress = function(node) {
}
return find_stop_logical(parent, op, level);
}
if (parent instanceof AST_Await) return find_stop_value(parent, level + 1);
if (parent instanceof AST_Binary) {
var op;
if (parent.left === node || !lazy_op[op = parent.operator]) {
@@ -3360,6 +3402,7 @@ Compressor.prototype.compress = function(node) {
var changed = false;
var parent = compressor.parent();
var self = compressor.self();
var exit, exit_defs, merge_exit;
var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self;
var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences");
var multiple_if_returns = has_multiple_if_returns(statements);
@@ -3415,6 +3458,7 @@ Compressor.prototype.compress = function(node) {
stat.condition = cond;
statements[j] = stat.body;
stat.body = next;
if (next === exit) exit = null;
statements[i] = stat;
statements[i] = stat.transform(compressor);
continue;
@@ -3458,6 +3502,7 @@ Compressor.prototype.compress = function(node) {
changed = true;
stat = stat.clone();
stat.alternative = next;
if (next === exit) exit = null;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
@@ -3501,6 +3546,11 @@ Compressor.prototype.compress = function(node) {
continue;
}
}
if (stat instanceof AST_Exit) {
exit = stat;
exit_defs = null;
}
}
return changed;
@@ -3522,7 +3572,25 @@ Compressor.prototype.compress = function(node) {
}
function can_drop_abort(ab) {
if (ab instanceof AST_Return) return in_lambda && is_undefined(ab.value);
if (ab instanceof AST_Exit) {
if (exit && exit.equivalent_to(ab)) {
if (!exit_defs) {
exit_defs = new Dictionary();
exit.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) exit_defs.set(node.name, node.definition());
}));
}
var abort = false;
ab.walk(new TreeWalker(function(node) {
if (abort) return true;
if (node instanceof AST_SymbolRef && exit_defs.get(node.name) !== node.definition()) {
return abort = true;
}
}));
if (!abort) return merge_exit = true;
}
return in_lambda && ab instanceof AST_Return && is_undefined(ab.value);
}
if (!(ab instanceof AST_LoopControl)) return false;
var lct = compressor.loopcontrol_target(ab);
if (ab instanceof AST_Continue) return match_target(loop_body(lct));
@@ -3531,6 +3599,7 @@ Compressor.prototype.compress = function(node) {
}
function can_merge_flow(ab) {
merge_exit = false;
if (!can_drop_abort(ab)) return false;
for (var j = statements.length; --j > i;) {
var stat = statements[j];
@@ -3550,7 +3619,16 @@ Compressor.prototype.compress = function(node) {
function extract_functions() {
var defuns = [];
var lexical = false;
var tail = statements.splice(i + 1).filter(function(stat) {
var start = i + 1;
var end;
if (merge_exit) {
end = statements.lastIndexOf(exit);
if (end < 0) end = statements.length;
} else {
end = statements.length;
exit = null;
}
var tail = statements.splice(start, end - start).filter(function(stat) {
if (stat instanceof AST_LambdaDefinition) {
defuns.push(stat);
return false;
@@ -3569,7 +3647,7 @@ Compressor.prototype.compress = function(node) {
block = last.body;
}
block.pop();
if (ab.value) block.push(make_node(AST_SimpleStatement, ab.value, { body: ab.value }));
if (!merge_exit && ab.value) block.push(make_node(AST_SimpleStatement, ab.value, { body: ab.value }));
return body;
}
@@ -3610,6 +3688,11 @@ Compressor.prototype.compress = function(node) {
function eliminate_dead_code(statements, compressor) {
var has_quit;
var self = compressor.self();
if (self instanceof AST_Catch) {
self = compressor.parent();
} else if (self instanceof AST_LabeledStatement) {
self = self.body;
}
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_LoopControl) {
@@ -5525,7 +5608,7 @@ Compressor.prototype.compress = function(node) {
def(AST_Binary, function(compressor) {
return this.left.has_side_effects(compressor)
|| this.right.has_side_effects(compressor)
|| this.operator == "in" && !is_object(this.right);
|| !can_drop_op(this.operator, this.right, compressor);
});
def(AST_Block, function(compressor) {
return any(this.body, compressor);
@@ -5679,7 +5762,7 @@ Compressor.prototype.compress = function(node) {
def(AST_Binary, function(compressor) {
return this.left.may_throw(compressor)
|| this.right.may_throw(compressor)
|| this.operator == "in" && !is_object(this.right);
|| !can_drop_op(this.operator, this.right, compressor);
});
def(AST_Block, function(compressor) {
return any(this.body, compressor);
@@ -5796,7 +5879,7 @@ Compressor.prototype.compress = function(node) {
def(AST_Binary, function(scope) {
return this.left.is_constant_expression(scope)
&& this.right.is_constant_expression(scope)
&& (this.operator != "in" || is_object(this.right));
&& can_drop_op(this.operator, this.right);
});
def(AST_Class, function(scope) {
var base = this.extends;
@@ -5898,10 +5981,19 @@ Compressor.prototype.compress = function(node) {
});
OPT(AST_LabeledStatement, function(self, compressor) {
if (compressor.option("dead_code")
&& self.body instanceof AST_Break
&& compressor.loopcontrol_target(self.body) === self.body) {
return make_node(AST_EmptyStatement, self);
if (self.body instanceof AST_If || self.body instanceof AST_Break) {
var body = tighten_body([ self.body ], compressor);
switch (body.length) {
case 0:
self.body = make_node(AST_EmptyStatement, self);
break;
case 1:
self.body = body[0];
break;
default:
self.body = make_node(AST_BlockStatement, self, { body: body });
break;
}
}
return compressor.option("unused") && self.label.references.length == 0 ? self.body : self;
});
@@ -6608,6 +6700,7 @@ Compressor.prototype.compress = function(node) {
var for_ins = Object.create(null);
var in_use = [];
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
var lambda_ids = Object.create(null);
var value_read = Object.create(null);
var value_modified = Object.create(null);
var var_defs = Object.create(null);
@@ -6643,15 +6736,19 @@ Compressor.prototype.compress = function(node) {
in_use_ids[def.id] = true;
in_use.push(def);
}
if (node.extends) node.extends.walk(tw);
var used = tw.parent() instanceof AST_ExportDefault;
if (used) export_defaults[def.id] = true;
if (used) {
export_defaults[def.id] = true;
} else if (drop && !(def.id in lambda_ids)) {
lambda_ids[def.id] = 1;
}
if (node.extends) node.extends.walk(tw);
var values = [];
node.properties.forEach(function(prop) {
if (prop.key instanceof AST_Node) prop.key.walk(tw);
var value = prop.value;
if (!value) return;
if (prop instanceof AST_ClassField && prop.static) {
if (is_static_field_or_init(prop)) {
if (!used && value.contains_this()) used = true;
walk_class_prop(value);
} else {
@@ -6665,16 +6762,18 @@ Compressor.prototype.compress = function(node) {
}
if (node instanceof AST_LambdaDefinition) {
var def = node.name.definition();
if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
var drop = drop_funcs && !def.exported;
if (!drop && !(def.id in in_use_ids)) {
in_use_ids[def.id] = true;
in_use.push(def);
}
initializations.add(def.id, node);
if (tw.parent() instanceof AST_ExportDefault) {
export_defaults[def.id] = true;
} else {
return true;
return;
}
if (drop && !(def.id in lambda_ids)) lambda_ids[def.id] = 1;
return true;
}
if (node instanceof AST_Definitions) {
node.definitions.forEach(function(defn) {
@@ -6704,6 +6803,7 @@ Compressor.prototype.compress = function(node) {
}
assignments.add(def.id, defn);
}
unmark_lambda(def);
return true;
}, tw);
if (side_effects) value.walk(tw);
@@ -6847,7 +6947,7 @@ Compressor.prototype.compress = function(node) {
node.properties = properties;
return node;
}
if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
if (node instanceof AST_SymbolDeclaration) return trim_decl(node);
});
var tt = new TreeTransformer(function(node, descend, in_list) {
var parent = tt.parent();
@@ -6895,6 +6995,14 @@ Compressor.prototype.compress = function(node) {
});
}
}
if (node instanceof AST_Binary && node.operator == "instanceof") {
var sym = node.right;
if (!(sym instanceof AST_SymbolRef)) return;
if (sym.definition().id in in_use_ids) return;
var lhs = node.left.drop_side_effect_free(compressor);
var value = make_node(AST_False, node).optimize(compressor);
return lhs ? make_sequence(node, [ lhs, value ]) : value;
}
if (node instanceof AST_Call) {
calls_to_drop_args.push(node);
node.args = node.args.map(function(arg) {
@@ -6954,9 +7062,7 @@ Compressor.prototype.compress = function(node) {
} else {
var trimmed = trim_destructured(rest, make_node(AST_Array, parent, {
elements: args.slice(argnames.length),
}), function(node) {
return node.definition().id in in_use_ids ? node : null;
}, !node.uses_arguments, rest);
}), trim_decl, !node.uses_arguments, rest);
rest = trimmed.name;
args.length = argnames.length;
if (trimmed.value.elements.length) [].push.apply(args, trimmed.value.elements);
@@ -6986,6 +7092,8 @@ Compressor.prototype.compress = function(node) {
} else if (trim) {
log(sym, "Dropping unused function argument {name}");
argnames.pop();
def.eliminated++;
sym.unused = true;
} else {
sym.unused = true;
}
@@ -6995,9 +7103,7 @@ Compressor.prototype.compress = function(node) {
if (!args || spread < i) {
funarg = sym.transform(trimmer);
} else {
var trimmed = trim_destructured(sym, args[i], function(node) {
return node.definition().id in in_use_ids ? node : null;
}, trim_value, sym);
var trimmed = trim_destructured(sym, args[i], trim_decl, trim_value, sym);
funarg = trimmed.name;
if (trimmed.value) args[i] = trimmed.value;
}
@@ -7437,6 +7543,14 @@ Compressor.prototype.compress = function(node) {
return nodes && nodes.indexOf(node);
}
function unmark_lambda(def) {
if (lambda_ids[def.id] > 1 && !(def.id in in_use_ids)) {
in_use_ids[def.id] = true;
in_use.push(def);
}
lambda_ids[def.id] = 0;
}
function verify_safe_usage(def, read, modified) {
if (def.id in in_use_ids) return;
if (read && modified) {
@@ -7488,17 +7602,18 @@ Compressor.prototype.compress = function(node) {
var def = node.expression.definition();
if (def.scope.resolve() === self) assignments.add(def.id, node);
}
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym && ((node_def = sym.definition()).scope.resolve() === self
|| self.variables.get(sym.name) === node_def)
&& !(is_arguments(node_def) && !all(self.argnames, function(argname) {
var props = [], sym = assign_as_unused(node, props);
if (sym) {
var node_def = sym.definition();
if (node_def.scope.resolve() !== self && self.variables.get(sym.name) !== node_def) return;
if (is_arguments(node_def) && !all(self.argnames, function(argname) {
return !argname.match_symbol(function(node) {
if (node instanceof AST_SymbolFunarg) {
var def = node.definition();
return def.references.length > def.replaced;
}
}, true);
}))) {
})) return;
if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return;
var assign = props.assign;
if (assign) {
@@ -7528,6 +7643,17 @@ Compressor.prototype.compress = function(node) {
}
}
if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
unmark_lambda(node_def);
return true;
}
if (node instanceof AST_Binary) {
if (node.operator != "instanceof") return;
var sym = node.right;
if (!(sym instanceof AST_SymbolRef)) return;
var id = sym.definition().id;
if (!lambda_ids[id]) return;
node.left.walk(tw);
lambda_ids[id]++;
return true;
}
if (node instanceof AST_ForIn) {
@@ -7549,7 +7675,7 @@ Compressor.prototype.compress = function(node) {
return true;
}
if (node instanceof AST_SymbolRef) {
node_def = node.definition();
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
@@ -7577,6 +7703,12 @@ Compressor.prototype.compress = function(node) {
return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration;
}
function trim_decl(node) {
if (node.definition().id in in_use_ids) return node;
if (node instanceof AST_SymbolFunarg) node.unused = true;
return null;
}
function trim_default(trimmer, node) {
node.value = node.value.transform(tt);
var name = node.name.transform(trimmer);
@@ -8299,8 +8431,10 @@ Compressor.prototype.compress = function(node) {
if (fixed.escaped && fixed.escaped.depth == 1) return;
return right instanceof AST_Object
&& right.properties.length > 0
&& all(right.properties, can_hoist_property)
&& can_drop_symbol(sym, compressor);
&& can_drop_symbol(sym, compressor)
&& all(right.properties, function(prop) {
return can_hoist_property(prop) && prop.key !== "__proto__";
});
}
});
@@ -8505,7 +8639,7 @@ Compressor.prototype.compress = function(node) {
var left = this.left;
var right = this.right;
var op = this.operator;
if (op == "in" && !is_object(right)) {
if (!can_drop_op(op, right, compressor)) {
var lhs = left.drop_side_effect_free(compressor, first_in_statement);
if (lhs === left) return this;
var node = this.clone();
@@ -8595,16 +8729,20 @@ Compressor.prototype.compress = function(node) {
});
def(AST_ClassExpression, function(compressor, first_in_statement) {
var self = this;
var exprs = [], values = [];
var exprs = [], values = [], init = 0;
var props = self.properties;
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (prop.key instanceof AST_Node) exprs.push(prop.key);
if (prop.static && prop.value
&& prop instanceof AST_ClassField
&& prop.value.has_side_effects(compressor)) {
if (prop.value.contains_this()) return self;
values.push(prop.value);
if (!is_static_field_or_init(prop)) continue;
var value = prop.value;
if (!value.has_side_effects(compressor)) continue;
if (value.contains_this()) return self;
if (prop instanceof AST_ClassInit) {
init++;
values.push(prop);
} else {
values.push(value);
}
}
var base = self.extends;
@@ -8625,31 +8763,47 @@ Compressor.prototype.compress = function(node) {
if (!base) node.extends = null;
node.properties = [];
if (values) {
node.properties.push(make_node(AST_ClassField, self, {
if (values.length == init) {
if (exprs.length) values.unshift(make_node(AST_ClassField, self, {
key: make_sequence(self, exprs),
value: null,
}));
node.properties = values;
} else node.properties.push(make_node(AST_ClassField, self, {
static: true,
key: exprs.length ? make_sequence(self, exprs) : "c",
value: make_sequence(self, values),
value: make_value(),
}));
} else if (exprs.length) {
node.properties.push(make_node(AST_ClassMethod, self, {
key: make_sequence(self, exprs),
value: make_node(AST_Function, self, {
argnames: [],
body: [],
}).init_vars(node),
}));
}
} else if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, {
key: make_sequence(self, exprs),
value: make_node(AST_Function, self, {
argnames: [],
body: [],
}).init_vars(node),
}));
return node;
}
if (values) exprs.push(make_node(AST_Call, self, {
expression: make_node(AST_Arrow, self, {
argnames: [],
body: [],
value: make_sequence(self, values),
value: make_value(),
}).init_vars(self.parent_scope),
args: [],
}));
return make_sequence(self, exprs);
function make_value() {
return make_sequence(self, values.map(function(node) {
if (!(node instanceof AST_ClassInit)) return node;
var fn = make_node(AST_Arrow, node, node.value);
fn.argnames = [];
return make_node(AST_Call, node, {
expression: fn,
args: [],
});
}));
}
});
def(AST_Conditional, function(compressor) {
var consequent = this.consequent.drop_side_effect_free(compressor);
@@ -9927,9 +10081,12 @@ Compressor.prototype.compress = function(node) {
}) : arg);
}
function avoid_await_yield(parent_scope) {
function avoid_await_yield(compressor, parent_scope) {
if (!parent_scope) parent_scope = compressor.find_parent(AST_Scope);
var avoid = [];
if (is_async(parent_scope)) avoid.push("await");
if (is_async(parent_scope) || parent_scope instanceof AST_Toplevel && compressor.option("module")) {
avoid.push("await");
}
if (is_generator(parent_scope)) avoid.push("yield");
return avoid.length && makePredicate(avoid);
}
@@ -10291,7 +10448,7 @@ Compressor.prototype.compress = function(node) {
if (exp === fn
&& !fn.name
&& (!value || value.is_constant_expression())
&& safe_from_await_yield(fn, avoid_await_yield(compressor.find_parent(AST_Scope)))) {
&& safe_from_await_yield(fn, avoid_await_yield(compressor))) {
return make_sequence(self, convert_args(value)).optimize(compressor);
}
}
@@ -10369,7 +10526,7 @@ Compressor.prototype.compress = function(node) {
&& all(fn.body, is_empty)
&& (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
&& !(is_arrow(fn) && fn.value)
&& safe_from_await_yield(fn, avoid_await_yield(compressor.find_parent(AST_Scope)))) {
&& safe_from_await_yield(fn, avoid_await_yield(compressor))) {
return make_sequence(self, convert_args()).optimize(compressor);
}
}
@@ -10390,9 +10547,9 @@ Compressor.prototype.compress = function(node) {
return try_evaluate(compressor, self);
function make_void_lhs(orig) {
return make_node(AST_Dot, orig, {
return make_node(AST_Sub, orig, {
expression: make_node(AST_Array, orig, { elements: [] }),
property: "e",
property: make_node(AST_Number, orig, { value: 0 }),
});
}
@@ -10528,7 +10685,7 @@ Compressor.prototype.compress = function(node) {
})) return;
var scope = compressor.find_parent(AST_Scope);
var abort = false;
var avoid = avoid_await_yield(scope);
var avoid = avoid_await_yield(compressor, scope);
var begin;
var in_order = [];
var side_effects = false;
@@ -10630,7 +10787,9 @@ Compressor.prototype.compress = function(node) {
});
child = scope;
scope = compressor.parent(level++);
if (scope instanceof AST_DWLoop) {
if (scope instanceof AST_ClassField) {
if (!scope.static) return false;
} else if (scope instanceof AST_DWLoop) {
in_loop = [];
} else if (scope instanceof AST_For) {
if (scope.init === child) continue;
@@ -10643,7 +10802,7 @@ Compressor.prototype.compress = function(node) {
} while (!(scope instanceof AST_Scope));
insert = scope.body.indexOf(child) + 1;
if (!insert) return false;
if (!safe_from_await_yield(fn, avoid_await_yield(scope))) return false;
if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return false;
var safe_to_inject = (exp !== fn || fn.parent_scope.resolve() === scope) && !scope.pinned();
if (scope instanceof AST_Toplevel) {
if (compressor.toplevel.vars) {
@@ -10745,6 +10904,7 @@ Compressor.prototype.compress = function(node) {
}));
function process(ref, name) {
if (name.unused) return make_void_lhs(name);
var def = name.definition();
def.assignments++;
def.references.push(ref);
@@ -11151,6 +11311,18 @@ Compressor.prototype.compress = function(node) {
|| node instanceof AST_Object;
}
function can_drop_op(op, rhs, compressor) {
switch (op) {
case "in":
return is_object(rhs) || compressor && compressor.option("unsafe_comps");
case "instanceof":
if (rhs instanceof AST_SymbolRef) rhs = rhs.fixed_value();
return is_lambda(rhs) || compressor && compressor.option("unsafe_comps");
default:
return true;
}
}
function is_primitive(compressor, node) {
if (node.is_constant()) return true;
if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right);
@@ -11657,6 +11829,12 @@ Compressor.prototype.compress = function(node) {
}
}
break;
case "instanceof":
if (is_lambda(self.right)) return make_sequence(self, [
self,
make_node(AST_False, self),
]).optimize(compressor);
break;
}
if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
@@ -11887,6 +12065,8 @@ Compressor.prototype.compress = function(node) {
if ((def.scope !== self.scope.resolve() || def.in_loop)
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
single_use = false;
} else if (def.redefined()) {
single_use = false;
} else if (recursive_ref(compressor, def, fixed)) {
single_use = false;
} else if (fixed.name && fixed.name.definition() !== def) {
@@ -12278,7 +12458,7 @@ Compressor.prototype.compress = function(node) {
parent = compressor.parent(level++);
if (parent instanceof AST_Assign) {
if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) {
if (in_try(level, parent)) break;
if (in_try(level, parent, !local)) break;
return strip_assignment(def);
}
if (parent.left.match_symbol(function(node) {
@@ -12390,14 +12570,16 @@ Compressor.prototype.compress = function(node) {
if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
}
function in_try(level, node) {
function in_try(level, node, sync) {
var right = self.right;
self.right = make_node(AST_Null, right);
var may_throw = node.may_throw(compressor);
self.right = right;
for (var parent; parent = compressor.parent(level++); node = parent) {
if (parent === scope) return false;
if (parent instanceof AST_Try) {
if (sync && parent instanceof AST_Lambda) {
if (parent.name || is_async(parent) || is_generator(parent)) return true;
} else if (parent instanceof AST_Try) {
if (parent.bfinally && parent.bfinally !== node) return true;
if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
}
@@ -13043,7 +13225,7 @@ Compressor.prototype.compress = function(node) {
var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
if (arrow) {
if (!scope) scope = compressor.find_parent(AST_Scope);
var avoid = avoid_await_yield(scope);
var avoid = avoid_await_yield(compressor, scope);
value.each_argname(function(argname) {
if (avoid[argname.name]) arrow = false;
});
@@ -13299,7 +13481,7 @@ Compressor.prototype.compress = function(node) {
def(AST_Assign, noop);
def(AST_Await, function(compressor, scope, no_return, in_loop) {
var self = this;
var inlined = sync(self.expression).try_inline(compressor, scope, no_return, in_loop);
var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, true);
if (!inlined) return;
if (!no_return) scan_local_returns(inlined, function(node) {
node.in_bool = false;
@@ -13314,40 +13496,20 @@ Compressor.prototype.compress = function(node) {
body: make_node(AST_Await, self, { expression: make_node(AST_Number, self, { value: 0 })}),
}) ],
});
function sync(node) {
if (!no_return) return node;
if (node.TYPE != "Call") return node;
var fn = node.expression;
switch (fn.CTOR) {
case AST_AsyncArrow:
fn = make_node(AST_Arrow, fn, fn);
break;
case AST_AsyncFunction:
fn = make_node(AST_Function, fn, fn);
break;
case AST_AsyncGeneratorFunction:
fn = make_node(AST_GeneratorFunction, fn, fn);
break;
default:
return node;
}
node = node.clone();
node.expression = fn;
return node;
}
});
def(AST_Binary, function(compressor, scope, no_return, in_loop) {
def(AST_Binary, function(compressor, scope, no_return, in_loop, in_await) {
if (no_return === undefined) return;
var self = this;
var op = self.operator;
if (!lazy_op[op]) return;
var inlined = self.right.try_inline(compressor, scope, no_return, in_loop);
var inlined = self.right.try_inline(compressor, scope, no_return, in_loop, in_await);
if (!inlined) return;
return make_node(AST_If, self, {
condition: make_condition(self.left),
body: inlined,
alternative: no_return ? null : make_node(AST_Return, self, { value: null }),
alternative: no_return ? null : make_node(AST_Return, self, {
value: make_node(AST_Undefined, self).transform(compressor),
}),
});
function make_condition(cond) {
@@ -13376,7 +13538,7 @@ Compressor.prototype.compress = function(node) {
body[last] = inlined;
return this;
});
def(AST_Call, function(compressor, scope, no_return, in_loop) {
def(AST_Call, function(compressor, scope, no_return, in_loop, in_await) {
if (compressor.option("inline") < 4) return;
var call = this;
if (call.is_expr_pure(compressor)) return;
@@ -13391,7 +13553,6 @@ Compressor.prototype.compress = function(node) {
if (fn.body[0] instanceof AST_Directive) return;
if (fn.contains_this()) return;
if (!scope) scope = find_scope(compressor);
if (in_async_generator(scope)) return;
var defined = new Dictionary();
defined.set("NaN", true);
while (!(scope instanceof AST_Scope)) {
@@ -13409,7 +13570,7 @@ Compressor.prototype.compress = function(node) {
}
defined.set("arguments", true);
}
var async = is_async(fn);
var async = !in_await && is_async(fn);
if (async) {
if (!compressor.option("awaits")) return;
if (!is_async(scope)) return;
@@ -13451,16 +13612,41 @@ Compressor.prototype.compress = function(node) {
if (has_arg_refs(fn, fn.rest)) return;
simple_argnames = false;
}
if (no_return && !all(fn.body, function(stat) {
var abort = false;
stat.walk(new TreeWalker(function(node) {
if (abort) return true;
if (async && node instanceof AST_Await || node instanceof AST_Return) return abort = true;
if (node instanceof AST_Scope && node !== fn) return true;
}));
return !abort;
})) return;
if (!safe_from_await_yield(fn, avoid_await_yield(scope))) return;
var verify_body;
if (no_return) {
verify_body = function(stat) {
var abort = false;
stat.walk(new TreeWalker(function(node) {
if (abort) return true;
if (async && node instanceof AST_Await || node instanceof AST_Return) return abort = true;
if (node instanceof AST_Scope) return true;
}));
return !abort;
};
} else if (in_await && !is_async(fn) || in_async_generator(scope)) {
verify_body = function(stat) {
var abort = false;
var find_return = new TreeWalker(function(node) {
if (abort) return true;
if (node instanceof AST_Return) return abort = true;
if (node instanceof AST_Scope) return true;
});
stat.walk(new TreeWalker(function(node) {
if (abort) return true;
if (node instanceof AST_Try) {
if (node.bfinally && all(node.body, function(stat) {
stat.walk(find_return);
return !abort;
}) && node.bcatch) node.bcatch.walk(find_return);
return true;
}
if (node instanceof AST_Scope) return true;
}));
return !abort;
};
}
if (verify_body && !all(fn.body, verify_body)) return;
if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return;
fn.functions.each(function(def, name) {
scope.functions.set(name, def);
});
@@ -13477,7 +13663,7 @@ Compressor.prototype.compress = function(node) {
if (def.orig.length == 1 && fn.functions.has(name)) return;
if (!all(def.orig, function(sym) {
if (sym instanceof AST_SymbolConst) return false;
if (sym instanceof AST_SymbolFunarg) return def.scope.resolve() !== fn;
if (sym instanceof AST_SymbolFunarg) return !sym.unused && def.scope.resolve() !== fn;
if (sym instanceof AST_SymbolLet) return false;
return true;
})) return;
@@ -13547,7 +13733,9 @@ Compressor.prototype.compress = function(node) {
if (is_undefined(value)) return;
node.value = make_node(AST_Await, call, { expression: value });
});
body.push(make_node(AST_Return, call, { value: null }));
body.push(make_node(AST_Return, call, {
value: in_async_generator(scope) ? make_node(AST_Undefined, call).transform(compressor) : null,
}));
}
return inlined;
@@ -13557,10 +13745,10 @@ Compressor.prototype.compress = function(node) {
syms.add(def.id, sym);
}
});
def(AST_Conditional, function(compressor, scope, no_return, in_loop) {
def(AST_Conditional, function(compressor, scope, no_return, in_loop, in_await) {
var self = this;
var body = self.consequent.try_inline(compressor, scope, no_return, in_loop);
var alt = self.alternative.try_inline(compressor, scope, no_return, in_loop);
var body = self.consequent.try_inline(compressor, scope, no_return, in_loop, in_await);
var alt = self.alternative.try_inline(compressor, scope, no_return, in_loop, in_await);
if (!body && !alt) return;
return make_node(AST_If, self, {
condition: self.condition,
@@ -13595,7 +13783,7 @@ Compressor.prototype.compress = function(node) {
if (body) this.body = body;
var obj = this.object;
if (obj instanceof AST_Sequence) {
var inlined = inline_sequence(compressor, scope, true, in_loop, obj, 1);
var inlined = inline_sequence(compressor, scope, true, in_loop, false, obj, 1);
if (inlined) {
this.object = obj.tail_node();
inlined.body.push(this);
@@ -13614,7 +13802,7 @@ Compressor.prototype.compress = function(node) {
}
var cond = this.condition;
if (cond instanceof AST_Sequence) {
var inlined = inline_sequence(compressor, scope, true, in_loop, cond, 1);
var inlined = inline_sequence(compressor, scope, true, in_loop, false, cond, 1);
if (inlined) {
this.condition = cond.tail_node();
inlined.body.push(this);
@@ -13646,10 +13834,10 @@ Compressor.prototype.compress = function(node) {
var value = this.value;
return value && value.try_inline(compressor, scope, undefined, in_loop === "try");
});
function inline_sequence(compressor, scope, no_return, in_loop, node, skip) {
function inline_sequence(compressor, scope, no_return, in_loop, in_await, node, skip) {
var body = [], exprs = node.expressions, no_ret = no_return;
for (var i = exprs.length - (skip || 0), j = i; --i >= 0; no_ret = true) {
var inlined = exprs[i].try_inline(compressor, scope, no_ret, in_loop);
for (var i = exprs.length - (skip || 0), j = i; --i >= 0; no_ret = true, in_await = false) {
var inlined = exprs[i].try_inline(compressor, scope, no_ret, in_loop, in_await);
if (!inlined) continue;
flush();
body.push(inlined);
@@ -13668,8 +13856,8 @@ Compressor.prototype.compress = function(node) {
j = i;
}
}
def(AST_Sequence, function(compressor, scope, no_return, in_loop) {
return inline_sequence(compressor, scope, no_return, in_loop, this);
def(AST_Sequence, function(compressor, scope, no_return, in_loop, in_await) {
return inline_sequence(compressor, scope, no_return, in_loop, in_await, this);
});
def(AST_SimpleStatement, function(compressor, scope, no_return, in_loop) {
var body = this.body;
@@ -13685,12 +13873,12 @@ Compressor.prototype.compress = function(node) {
});
return body.try_inline(compressor, scope, no_return || false, in_loop);
});
def(AST_UnaryPrefix, function(compressor, scope, no_return, in_loop) {
def(AST_UnaryPrefix, function(compressor, scope, no_return, in_loop, in_await) {
var self = this;
var op = self.operator;
if (unary_side_effects[op]) return;
if (!no_return && op == "void") no_return = false;
var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop);
var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, in_await);
if (!inlined) return;
if (!no_return) scan_local_returns(inlined, function(node) {
node.in_bool = false;
@@ -13708,7 +13896,7 @@ Compressor.prototype.compress = function(node) {
if (body) this.body = body;
var exp = this.expression;
if (exp instanceof AST_Sequence) {
var inlined = inline_sequence(compressor, scope, true, in_loop, exp, 1);
var inlined = inline_sequence(compressor, scope, true, in_loop, false, exp, 1);
if (inlined) {
this.expression = exp.tail_node();
inlined.body.push(this);

View File

@@ -260,6 +260,15 @@ function OutputStream(options) {
var require_semicolon = makePredicate("( [ + * / - , .");
function require_space(prev, ch, str) {
return is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| last == "--" && ch == ">"
|| last == "!" && str == "--"
|| prev == "/" && (str == "in" || str == "instanceof");
}
var print = options.beautify
|| options.comments
|| options.max_line_len
@@ -312,12 +321,7 @@ function OutputStream(options) {
}
if (might_need_space) {
if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") {
if (require_space(prev, ch, str)) {
output += " ";
current_col++;
}
@@ -355,14 +359,7 @@ function OutputStream(options) {
}
}
if (might_need_space) {
if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") {
output += " ";
}
if (require_space(prev, ch, str)) output += " ";
if (prev != "<" || str != "!") might_need_space = false;
}
output += str;
@@ -1257,6 +1254,11 @@ function OutputStream(options) {
}
print_method(self, output);
});
DEFPRINT(AST_ClassInit, function(output) {
output.print("static");
output.space();
print_braced(this.value, output);
});
/* -----[ jumps ]----- */
function print_jump(kind, prop) {
@@ -1814,9 +1816,6 @@ function OutputStream(options) {
case "\u2029": return "\\u2029";
}
}));
var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === this)
output.print(" ");
});
function force_statement(stat, output) {

View File

@@ -1119,6 +1119,18 @@ function parse($TEXT, options) {
}));
continue;
}
if (fixed && is("punc", "{")) {
props.push(new AST_ClassInit({
start: start,
value: new AST_ClassInitBlock({
start: start,
body: block_(),
end: prev(),
}),
end: prev(),
}));
continue;
}
var internal = is("name") && /^#/.test(S.token.value);
var key = as_property_key();
if (is("punc", "(")) {
@@ -1335,9 +1347,11 @@ function parse($TEXT, options) {
var loop = S.in_loop;
var labels = S.labels;
++S.in_function;
S.input.push_directives_stack();
S.in_loop = 0;
S.labels = [];
if (is("punc", "{")) {
S.in_directives = true;
body = block_();
value = null;
} else {
@@ -1345,6 +1359,8 @@ function parse($TEXT, options) {
handle_regexp();
value = maybe_assign();
}
var is_strict = S.input.has_directive("use strict");
S.input.pop_directives_stack();
--S.in_function;
S.in_loop = loop;
S.labels = labels;
@@ -1358,7 +1374,7 @@ function parse($TEXT, options) {
value: value,
end: prev(),
});
if (S.input.has_directive("use strict")) node.each_argname(strict_verify_symbol);
if (is_strict) node.each_argname(strict_verify_symbol);
return node;
}
@@ -2541,7 +2557,10 @@ function parse($TEXT, options) {
return function() {
var start = S.token;
var body = [];
if (options.module) S.input.add_directive("use strict");
if (options.module) {
S.in_async = true;
S.input.add_directive("use strict");
}
S.input.push_directives_stack();
while (!is("eof"))
body.push(statement());

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.16.0",
"version": "3.16.2",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -1106,3 +1106,17 @@ issue_5416: {
expect_stdout: "undefined"
node_version: ">=4"
}
issue_5495: {
input: {
console.log((() => {
"use strict";
return function() {
return this;
}();
})());
}
expect_exact: 'console.log((()=>{"use strict";return function(){return this}()})());'
expect_stdout: "undefined"
node_version: ">=4"
}

View File

@@ -1293,6 +1293,21 @@ functions_inner_var: {
node_version: ">=8"
}
instanceof_lambda: {
options = {
evaluate: true,
side_effects: true,
}
input: {
console.log(42 instanceof async function() {});
}
expect: {
console.log(false);
}
expect_stdout: "false"
node_version: ">=8"
}
issue_4335_1: {
options = {
inline: true,
@@ -2961,3 +2976,202 @@ issue_5478: {
expect_stdout: "PASS"
node_version: ">=8"
}
issue_5493: {
options = {
collapse_vars: true,
reduce_vars: true,
}
input: {
(async function(a) {
var b = await [ 42 || b, a = b ];
console.log(a);
})();
}
expect: {
(async function(a) {
var b = await [ 42 || b, a = b ];
console.log(a);
})();
}
expect_stdout: "undefined"
node_version: ">=8"
}
issue_5506: {
options = {
dead_code: true,
}
input: {
console.log(function(a) {
(async function() {
a = null in (a = "PASS");
})();
return a;
}("FAIL"));
}
expect: {
console.log(function(a) {
(async function() {
a = null in (a = "PASS");
})();
return a;
}("FAIL"));
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_5528_1: {
options = {
inline: true,
}
input: {
(async function() {
await function() {
try {
return;
} finally {
console.log("foo");
}
}();
})();
console.log("bar");
}
expect: {
(async function() {
await function() {
try {
return;
} finally {
console.log("foo");
}
}();
})();
console.log("bar");
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=8"
}
issue_5528_2: {
options = {
inline: true,
}
input: {
(async function() {
await function() {
try {
return 42;
} finally {
console.log("foo");
}
}();
})();
console.log("bar");
}
expect: {
(async function() {
await function() {
try {
return 42;
} finally {
console.log("foo");
}
}();
})();
console.log("bar");
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=8"
}
issue_5528_3: {
options = {
inline: true,
}
input: {
(async function() {
await function() {
try {
FAIL;
} catch (e) {
return console.log("foo");
} finally {
console.log("bar");
}
}();
})();
console.log("baz");
}
expect: {
(async function() {
await function() {
try {
FAIL;
} catch (e) {
return console.log("foo");
} finally {
console.log("bar");
}
}();
})();
console.log("baz");
}
expect_stdout: [
"foo",
"bar",
"baz",
]
node_version: ">=8"
}
issue_5528_4: {
options = {
inline: true,
}
input: {
(async function() {
await function() {
try {
return {
then() {
console.log("foo");
},
};
} finally {
console.log("bar");
}
}();
})();
console.log("baz");
}
expect: {
(async function() {
await function() {
try {
return {
then() {
console.log("foo");
},
};
} finally {
console.log("bar");
}
}();
})();
console.log("baz");
}
expect_stdout: [
"bar",
"baz",
"foo",
]
node_version: ">=8"
}

View File

@@ -241,6 +241,208 @@ class_super: {
node_version: ">=4"
}
static_init: {
input: {
var a = "foo";
var b = null;
class A {
static {
var a = "bar";
b = true;
var c = 42;
console.log(a, b, c);
}
}
console.log(a, b, typeof c);
}
expect_exact: 'var a="foo";var b=null;class A{static{var a="bar";b=true;var c=42;console.log(a,b,c)}}console.log(a,b,typeof c);'
expect_stdout: [
"bar true 42",
"foo true undefined",
]
node_version: ">=16"
}
static_field_init: {
options = {
side_effects: true,
}
input: {
(class {
static [console.log("foo")] = console.log("bar");
static {
console.log("baz");
}
static [console.log("moo")] = console.log("moz");
});
}
expect: {
(class {
static [(console.log("foo"), console.log("moo"))] = (
console.log("bar"),
(() => {
console.log("baz");
})(),
console.log("moz")
);
});
}
expect_stdout: [
"foo",
"moo",
"bar",
"baz",
"moz",
]
node_version: ">=16"
}
static_field_init_strict: {
options = {
side_effects: true,
}
input: {
"use strict";
(class {
static [console.log("foo")] = console.log("bar");
static {
console.log("baz");
}
static [console.log("moo")] = console.log("moz");
});
}
expect: {
"use strict";
console.log("foo"),
console.log("moo"),
(() => (
console.log("bar"),
(() => {
console.log("baz");
})(),
console.log("moz")
))();
}
expect_stdout: [
"foo",
"moo",
"bar",
"baz",
"moz",
]
node_version: ">=16"
}
static_init_side_effects_1: {
options = {
merge_vars: true,
side_effects: true,
}
input: {
var a = "FAIL";
(class {
static {
a = "PASS";
}
});
console.log(a);
}
expect: {
var a = "FAIL";
(class {
static {
a = "PASS";
}
});
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=16"
}
static_init_side_effects_1_strict: {
options = {
merge_vars: true,
side_effects: true,
}
input: {
"use strict";
var a = "FAIL";
(class {
static {
a = "PASS";
}
});
console.log(a);
}
expect: {
"use strict";
var a = "FAIL";
(() => (() => {
a = "PASS";
})())();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=16"
}
static_init_side_effects_2: {
options = {
hoist_props: true,
reduce_vars: true,
side_effects: true,
}
input: {
var a = "FAIL";
(class {
static {
a = "PASS";
}
});
console.log(a);
}
expect: {
var a = "FAIL";
(class {
static {
a = "PASS";
}
});
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=16"
}
static_init_side_effects_2_strict: {
options = {
hoist_props: true,
reduce_vars: true,
side_effects: true,
}
input: {
"use strict";
var a = "FAIL";
(class {
static {
a = "PASS";
}
});
console.log(a);
}
expect: {
"use strict";
var a = "FAIL";
(() => (() => {
a = "PASS";
})())();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=16"
}
block_scoped: {
options = {
evaluate: true,
@@ -1064,6 +1266,105 @@ keep_fnames: {
node_version: ">=4"
}
instanceof_lambda: {
options = {
evaluate: true,
side_effects: true,
}
input: {
"use strict";
console.log(42 instanceof class {});
}
expect: {
"use strict";
console.log(false);
}
expect_stdout: "false"
node_version: ">=4"
}
drop_instanceof: {
options = {
booleans: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
class A {}
console.log({} instanceof A, Math instanceof A);
}
expect: {
"use strict";
console.log(!1, (Math, !1));
}
expect_stdout: "false false"
node_version: ">=4"
}
keep_instanceof_1: {
options = {
toplevel: true,
unused: true,
}
input: {
"use strict";
class A {}
var A;
console.log({} instanceof A, Math instanceof A);
}
expect: {
"use strict";
class A {}
var A;
console.log({} instanceof A, Math instanceof A);
}
expect_stdout: SyntaxError("Identifier has already been declared")
node_version: ">=4"
}
keep_instanceof_2: {
options = {
toplevel: true,
unused: true,
}
input: {
"use strict";
var A = Object;
class A {}
console.log({} instanceof A, Math instanceof A);
}
expect: {
"use strict";
var A = Object;
class A {}
console.log({} instanceof A, Math instanceof A);
}
expect_stdout: SyntaxError("Identifier has already been declared")
node_version: ">=4"
}
keep_instanceof_3: {
options = {
toplevel: true,
unused: true,
}
input: {
"use strict";
class A {}
A = Object;
console.log({} instanceof A, Math instanceof A);
}
expect: {
"use strict";
class A {}
A = Object;
console.log({} instanceof A, Math instanceof A);
}
expect_stdout: "true true"
node_version: ">=4"
}
issue_805_1: {
options = {
inline: true,
@@ -2882,3 +3183,260 @@ issue_5481: {
expect_stdout: "PASS"
node_version: ">=4"
}
issue_5489: {
options = {
side_effects: true,
}
input: {
(class {
[console.log("foo")];
static {
console.log("bar");
}
static [console.log("baz")]() {}
});
}
expect: {
(class {
[(console.log("foo"), console.log("baz"))];
static {
console.log("bar");
}
});
}
expect_stdout: [
"foo",
"baz",
"bar",
]
node_version: ">=16"
}
issue_5489_strict: {
options = {
side_effects: true,
}
input: {
"use strict";
(class {
[console.log("foo")];
static {
console.log("bar");
}
static [console.log("baz")]() {}
});
}
expect: {
"use strict";
console.log("foo"),
console.log("baz"),
(() => (() => {
console.log("bar");
})())();
}
expect_stdout: [
"foo",
"baz",
"bar",
]
node_version: ">=16"
}
issue_5502: {
options = {
collapse_vars: true,
}
input: {
"use strict";
var a = "FAIL";
class A {
static p = a;
[a = "PASS"];
}
try {
b++;
} finally {
var a, b = 42;
}
console.log(a, b);
}
expect: {
"use strict";
var a = "FAIL";
class A {
static p = a;
[a = "PASS"];
}
try {
b++;
} finally {
var a, b = 42;
}
console.log(a, b);
}
expect_stdout: "PASS 42"
node_version: ">=12"
}
issue_5504: {
options = {
collapse_vars: true,
}
input: {
"use strict";
var a;
console.log((a = 42, class {
static p;
}).p);
}
expect: {
"use strict";
var a;
console.log((a = 42, class {
static p;
}).p);
}
expect_stdout: "undefined"
node_version: ">=12"
}
issue_5512: {
options = {
collapse_vars: true,
}
input: {
"use strict";
a = "PASS";
class A {
static {
console.log(a);
}
static p = "PASS";
}
var a;
}
expect: {
"use strict";
a = "PASS";
class A {
static {
console.log(a);
}
static p = "PASS";
}
var a;
}
expect_stdout: "PASS"
node_version: ">=16"
}
issue_5531_1: {
options = {
inline: true,
toplevel: true,
}
input: {
class A {
p = function() {
var a = function f() {
if (!a)
console.log("foo");
return 42;
}(a++);
}();
}
new A();
new A();
}
expect: {
class A {
p = function() {
var a = function f() {
if (!a)
console.log("foo");
return 42;
}(a++);
}();
}
new A();
new A();
}
expect_stdout: [
"foo",
"foo",
]
node_version: ">=12"
}
issue_5531_2: {
options = {
inline: true,
toplevel: true,
}
input: {
class A {
static p = function() {
var a = function f() {
if (!a)
console.log("foo");
return 42;
}(a++);
}();
}
new A();
new A();
}
expect: {
class A {
static p = (a = function f() {
if (!a)
console.log("foo");
return 42;
}(a++), void 0);
}
var a;
new A();
new A();
}
expect_stdout: "foo"
node_version: ">=12"
}
issue_5531_3: {
options = {
inline: true,
}
input: {
class A {
static {
(function() {
var a = function f() {
if (!a)
console.log("foo");
return 42;
}(a++);
})();
}
}
new A();
new A();
}
expect: {
class A {
static {
a = function f() {
if (!a)
console.log("foo");
return 42;
}(a++),
void 0;
var a;
}
}
new A();
new A();
}
expect_stdout: "foo"
node_version: ">=16"
}

View File

@@ -40,6 +40,22 @@ unsafe_comps: {
}
}
unsafe_in_instanceof: {
options = {
side_effects: true,
unsafe_comps: true,
}
input: {
var a;
42 in a;
f() instanceof "foo";
}
expect: {
var a;
f();
}
}
dont_change_in_or_instanceof_expressions: {
input: {
1 in 1;

View File

@@ -1872,3 +1872,37 @@ issue_5476: {
}
expect_stdout: "undefined"
}
issue_5516: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
try {} catch (a) {
(function f() {
a;
})();
}
{
const a = function() {};
return a;
}
}());
}
expect: {
console.log(typeof function() {
try {} catch (a) {
void a;
}
{
const a = function() {};
return a;
}
}());
}
expect_stdout: "function"
}

View File

@@ -1669,3 +1669,41 @@ issue_5106_2: {
}
expect_stdout: "PASS"
}
issue_5506: {
options = {
dead_code: true,
}
input: {
try {
(function(a) {
var b = 1;
(function f() {
try {
b-- && f();
} catch (c) {}
console.log(a);
a = 42 in (a = "bar");
})();
})("foo");
} catch (e) {}
}
expect: {
try {
(function(a) {
var b = 1;
(function f() {
try {
b-- && f();
} catch (c) {}
console.log(a);
a = 42 in (a = "bar");
})();
})("foo");
} catch (e) {}
}
expect_stdout: [
"foo",
"bar",
]
}

View File

@@ -541,7 +541,7 @@ inline_side_effects_2: {
}
expect: {
var a = 42;
[ [].e = --a ] = [ console ];
[ [][0] = --a ] = [ console ];
console.log(a);
}
expect_stdout: "42"
@@ -1558,7 +1558,7 @@ issue_4502_4: {
(function(a, b = console.log("FAIL")) {})(..."" + console.log(42));
}
expect: {
[ , [].e = console.log("FAIL") ] = [ ..."" + console.log(42) ];
[ , [][0] = console.log("FAIL") ] = [ ..."" + console.log(42) ];
}
expect_stdout: "42"
node_version: ">=6"
@@ -2183,7 +2183,7 @@ issue_5340_2: {
}
expect: {
var a;
[ [].e = 0 ] = [ ({ p: a } = true).q ];
[ [][0] = 0 ] = [ ({ p: a } = true).q ];
console.log(a);
}
expect_stdout: "undefined"
@@ -2462,3 +2462,370 @@ issue_5485: {
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_1_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_1_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_2_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f([ b ] = []) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;) {
var [ [] = [] ] = [];
throw "PASS";
}
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_2_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f([ b ] = []) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;) {
var [ [] = [] ] = [];
throw "PASS";
}
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_3_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42, c = null) {
c;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_3_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42, c = null) {
c;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_4_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42, [ c ] = []) {
c;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;) {
var [ [] = [] ] = [];
throw "PASS";
}
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_4_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42, [ c ] = []) {
c;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;) {
var [ [] = [] ] = [];
throw "PASS";
}
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5536: {
options = {
inline: true,
keep_fargs: true,
unused: true,
}
input: {
(function*() {
(([], a = 42) => {})([]);
console.log(typeof a);
})().next();
}
expect: {
(function*() {
[ , [][0] = 0 ] = [ [] ],
void 0;
console.log(typeof a);
})().next();
}
expect_stdout: "undefined"
node_version: ">=6"
}

View File

@@ -3497,7 +3497,7 @@ issue_5314_2: {
A = this;
new function() {
[ {
[console.log(this === A ? "FAIL" : "PASS")]: [].e,
[console.log(this === A ? "FAIL" : "PASS")]: [][0],
} ] = [ 42 ];
}();
}
@@ -3646,3 +3646,87 @@ issue_5485: {
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f([ b ]) {
b;
throw "PASS";
})([]);
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f([ b ]) {
b;
throw "PASS";
})([]);
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -671,6 +671,76 @@ iife: {
}
}
drop_instanceof: {
options = {
booleans: true,
toplevel: true,
unused: true,
}
input: {
function f() {}
console.log({} instanceof f, Math instanceof f);
}
expect: {
console.log(!1, (Math, !1));
}
expect_stdout: "false false"
}
keep_instanceof_1: {
options = {
toplevel: true,
unused: true,
}
input: {
function f() {}
var f;
console.log({} instanceof f, Math instanceof f);
}
expect: {
function f() {}
var f;
console.log({} instanceof f, Math instanceof f);
}
expect_stdout: "false false"
}
keep_instanceof_2: {
options = {
toplevel: true,
unused: true,
}
input: {
function f() {}
var f = Object;
console.log({} instanceof f, Math instanceof f);
}
expect: {
function f() {}
var f = Object;
console.log({} instanceof f, Math instanceof f);
}
expect_stdout: "true true"
}
keep_instanceof_3: {
options = {
toplevel: true,
unused: true,
}
input: {
f = Object;
function f() {}
console.log({} instanceof f, Math instanceof f);
}
expect: {
f = Object;
function f() {}
console.log({} instanceof f, Math instanceof f);
}
expect_stdout: "true true"
}
issue_1539: {
options = {
collapse_vars: true,
@@ -3615,3 +3685,85 @@ issue_5271: {
}
expect_stdout: "42"
}
issue_5533_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
}
issue_5533_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
}

View File

@@ -907,6 +907,20 @@ chained_side_effects: {
]
}
instanceof_lambda: {
options = {
evaluate: true,
side_effects: true,
}
input: {
console.log(42 instanceof function() {});
}
expect: {
console.log(false);
}
expect_stdout: "false"
}
issue_1649: {
options = {
evaluate: true,

View File

@@ -417,6 +417,46 @@ hoist_funs: {
expect_exact: "export function f(){}export default async function*g(){}"
}
instanceof_default_class: {
options = {
toplevel: true,
unused: true,
}
input: {
export default class A {
f(a) {
return a instanceof A;
}
}
}
expect: {
export default class A {
f(a) {
return a instanceof A;
}
}
}
}
instanceof_default_function: {
options = {
toplevel: true,
unused: true,
}
input: {
export default function f() {
if (!(this instanceof f))
throw new Error("must instantiate");
}
}
expect: {
export default function f() {
if (!(this instanceof f))
throw new Error("must instantiate");
}
}
}
issue_4742_join_vars_1: {
options = {
join_vars: true,

View File

@@ -629,7 +629,7 @@ inline_binary_and: {
return void "moo";
return;
} else
return;
return void 0;
}());
}
expect_stdout: [
@@ -5582,7 +5582,7 @@ issue_3835: {
return f();
})();
}
expect_stdout: true
expect_stdout: RangeError("Maximum call stack size exceeded")
}
issue_3836_1: {
@@ -7835,7 +7835,7 @@ issue_5249_1: {
while (console.log("FAIL 2"));
return;
} else
return;
return void 0;
throw "FAIL 3";
}());
}

View File

@@ -1203,3 +1203,24 @@ issue_5441: {
}
expect_stdout: "object"
}
issue_5498: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
__proto__: 42,
};
while (console.log(typeof o.__proto__));
}
expect: {
var o = {
__proto__: 42,
};
while (console.log(typeof o.__proto__));
}
expect_stdout: "object"
}

View File

@@ -850,3 +850,170 @@ issue_866_2: {
})();
}
}
identical_returns_1: {
options = {
conditionals: true,
if_return: true,
}
input: {
console.log(function() {
if (console.log("foo"))
return 42;
else
while (console.log("bar"));
return 42;
}());
}
expect: {
console.log(function() {
if (!console.log("foo"))
while (console.log("bar"));
return 42;
}());
}
expect_stdout: [
"foo",
"bar",
"42",
]
}
identical_returns_2: {
options = {
conditionals: true,
if_return: true,
}
input: {
console.log(function() {
if (console.log("foo"))
while (console.log("FAIL"));
else
return "bar";
return "bar";
}());
}
expect: {
console.log(function() {
if (console.log("foo"))
while (console.log("FAIL"));
return "bar";
}());
}
expect_stdout: [
"foo",
"bar",
]
}
identical_returns_3: {
options = {
if_return: true,
}
input: {
function f(a) {
if (a)
return 42;
if (a)
return;
return 42;
}
if (f(console))
console.log("PASS");
}
expect: {
function f(a) {
if (a)
return 42;
if (a)
;
else
return 42;
}
if (f(console))
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4374: {
options = {
booleans: true,
conditionals: true,
if_return: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
console.log(f(console));
function f(a) {
if (console) return 0;
if (a) return 1;
return 0;
}
})();
}
expect: {
(function() {
console.log(function(a) {
return !console && a ? 1 : 0;
}(console));
})();
}
expect_stdout: "0"
}
issue_5521: {
options = {
if_return: true,
}
input: {
console.log(function() {
if (console)
try {
return "FAIL";
} finally {
return;
}
}());
}
expect: {
console.log(function() {
if (console)
try {
return "FAIL";
} finally {
return;
}
}());
}
expect_stdout: "undefined"
}
issue_5523: {
options = {
if_return: true,
}
input: {
console.log(function() {
if (console)
try {
FAIL;
} finally {
return;
}
}());
}
expect: {
console.log(function() {
if (console)
try {
FAIL;
} finally {
return;
}
}());
}
expect_stdout: "undefined"
}

View File

@@ -111,6 +111,7 @@ labels_5: {
labels_6: {
options = {
dead_code: true,
unused: true,
}
input: {
out: break out;
@@ -208,6 +209,59 @@ labels_10: {
expect_stdout: "PASS"
}
labels_11: {
options = {
conditionals: true,
if_return: true,
unused: true,
}
input: {
L: if (console.log("PASS"))
break L;
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
labels_12: {
options = {
conditionals: true,
dead_code: true,
if_return: true,
}
input: {
L: try {
if (console.log("foo"))
break L;
throw "bar";
} catch (e) {
console.log(e);
break L;
} finally {
if (console.log("baz"))
break L;
}
}
expect: {
L: try {
if (!console.log("foo"))
throw "bar";
} catch (e) {
console.log(e);
} finally {
if (console.log("baz"))
break L;
}
}
expect_stdout: [
"foo",
"bar",
"baz",
]
}
issue_4466_1: {
mangle = {
v8: false,
@@ -327,3 +381,53 @@ issue_4466_2_toplevel_v8: {
}
expect_stdout: "PASS"
}
issue_5522: {
options = {
dead_code: true,
}
input: {
console.log(function() {
L: try {
return "FAIL";
} finally {
break L;
}
return "PASS";
}());
}
expect: {
console.log(function() {
L: try {
return "FAIL";
} finally {
break L;
}
return "PASS";
}());
}
expect_stdout: "PASS"
}
issue_5524: {
options = {
dead_code: true,
}
input: {
L: try {
FAIL;
} finally {
break L;
}
console.log("PASS");
}
expect: {
L: try {
FAIL;
} finally {
break L;
}
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -892,6 +892,40 @@ if_return_2: {
node_version: ">=4"
}
if_return_3: {
options = {
if_return: true,
}
input: {
"use strict";
var a = "PASS";
function f(b) {
if (console) {
let b = a;
return b;
} else
while (console.log("FAIL 1"));
return b;
}
console.log(f("FAIL 2"));
}
expect: {
"use strict";
var a = "PASS";
function f(b) {
if (console) {
let b = a;
return b;
} else
while (console.log("FAIL 1"));
return b;
}
console.log(f("FAIL 2"));
}
expect_stdout: "PASS"
node_version: ">=4"
}
do_if_continue_1: {
options = {
if_return: true,

View File

@@ -308,6 +308,7 @@ issue_4679: {
issue_5266: {
options = {
inline: true,
side_effects: true,
}
input: {
[

View File

@@ -147,7 +147,7 @@ relational: {
"bar" >= "bar";
}
expect: {
bar();
0 instanceof bar();
bar();
bar(), bar();
bar();

View File

@@ -50,6 +50,22 @@ regexp_properties: {
expect_stdout: "abc true false 0 false"
}
instanceof_1: {
input: {
console.log(/foo/ instanceof RegExp);
}
expect_exact: "console.log(/foo/ instanceof RegExp);"
expect_stdout: "true"
}
instanceof_2: {
input: {
console.log(42 + /foo/ instanceof Object);
}
expect_exact: "console.log(42+/foo/ instanceof Object);"
expect_stdout: "false"
}
issue_3434_1: {
options = {
evaluate: true,

View File

@@ -648,7 +648,7 @@ drop_new_function: {
}
expect: {
void ([ ... {
[console.log("PASS")]: [].e,
[console.log("PASS")]: [][0],
}] = []);
}
expect_stdout: "PASS"
@@ -1363,3 +1363,171 @@ issue_5391: {
expect_stdout: "NaN"
node_version: ">=8.3.0"
}
issue_5533_1_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(...b) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_1_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(...b) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_2_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(...[ b ]) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_2_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(...[ b ]) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -645,3 +645,56 @@ issue_4751: {
}
expect_stdout: "PASS"
}
drop_instanceof: {
options = {
side_effects: true,
}
input: {
42 instanceof function() {};
console.log("PASS");
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
drop_instanceof_reference: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
function f() {}
42 instanceof f;
console.log("PASS");
}
expect: {
function f() {}
console.log("PASS");
}
expect_stdout: "PASS"
}
retain_instanceof: {
options = {
side_effects: true,
}
input: {
try {
42 instanceof "foo";
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
0 instanceof "foo";
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -127,7 +127,7 @@ if_return: {
if (w) {
if (y) return;
} else if (z) return;
return x == y || (x && w(), y && z()), !0;
return x != y && (x && w(), y && z()), !0;
}
}
}

View File

@@ -613,3 +613,35 @@ issue_4954: {
]
node_version: ">=4"
}
issue_5516: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
varify: true,
}
input: {
"use strict";
console.log(typeof function() {
{
let a;
}
{
const a = function() {};
return a;
}
}());
}
expect: {
"use strict";
console.log(typeof function() {
{
const a = function() {};
return a;
}
}());
}
expect_stdout: "function"
node_version: ">=4"
}

View File

@@ -907,7 +907,7 @@ drop_body: {
})([ console.log("baz") ]);
}
expect: {
[ [ , [].e = console.log("foo") ] ] = [ [ console.log("baz") ] ];
[ [ , [][0] = console.log("foo") ] ] = [ [ console.log("baz") ] ];
}
expect_stdout: [
"baz",
@@ -934,6 +934,21 @@ drop_unused_call: {
node_version: ">=4"
}
instanceof_lambda: {
options = {
evaluate: true,
side_effects: true,
}
input: {
console.log(42 instanceof function*() {});
}
expect: {
console.log(false);
}
expect_stdout: "false"
node_version: ">=4"
}
issue_4454_1: {
rename = false
options = {
@@ -1452,6 +1467,80 @@ issue_5385_2: {
node_version: ">=10"
}
issue_5385_3: {
options = {
inline: true,
}
input: {
(async function*() {
return function() {
try {
throw console.log("foo");
} catch (e) {
return console.log("bar");
}
}();
})().next();
console.log("moo");
}
expect: {
(async function*() {
try {
throw console.log("foo");
} catch (e) {
return console.log("bar");
}
return void 0;
})().next();
console.log("moo");
}
expect_stdout: [
"foo",
"bar",
"moo",
]
node_version: ">=10"
}
issue_5385_4: {
options = {
awaits: true,
inline: true,
}
input: {
(async function*() {
return async function() {
try {
return {
then(resolve) {
resolve(console.log("FAIL"));
},
};
} finally {
return "PASS";
}
}();
})().next().then(o => console.log(o.value, o.done));
}
expect: {
(async function*() {
return async function() {
try {
return {
then(resolve) {
resolve(console.log("FAIL"));
},
};
} finally {
return "PASS";
}
}();
})().next().then(o => console.log(o.value, o.done));
}
expect_stdout: "PASS true"
node_version: ">=10"
}
issue_5425: {
options = {
assignments: true,
@@ -1515,3 +1604,71 @@ issue_5456: {
expect_stdout: "foo"
node_version: ">=4"
}
issue_5506: {
options = {
dead_code: true,
}
input: {
console.log(function(a) {
var b = function*() {
a = null in (a = "PASS");
}();
try {
b.next();
} catch (e) {
return a;
}
}("FAIL"));
}
expect: {
console.log(function(a) {
var b = function*() {
a = null in (a = "PASS");
}();
try {
b.next();
} catch (e) {
return a;
}
}("FAIL"));
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_5526: {
options = {
inline: true,
side_effects: true,
}
input: {
(async function*() {
try {
return function() {
while (console.log("foo"));
}();
} finally {
console.log("bar");
}
})().next();
console.log("baz");
}
expect: {
(async function*() {
try {
while (console.log("foo"));
return void 0;
} finally {
console.log("bar");
}
})().next();
console.log("baz");
}
expect_stdout: [
"foo",
"baz",
"bar",
]
node_version: ">=10"
}

View File

@@ -1,6 +1,7 @@
var assert = require("assert");
var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code;
var semver = require("semver");
var UglifyJS = require("../..");
function read(path) {
@@ -320,6 +321,24 @@ describe("minify", function() {
});
});
describe("module", function() {
it("Should not inline `await` variables", function() {
if (semver.satisfies(process.version, "<8")) return;
var code = [
"console.log(function() {",
" return typeof await;",
"}());",
].join("\n");
assert.strictEqual(run_code("(async function(){" + code + "})();"), "undefined\n");
var result = UglifyJS.minify(code, {
module: true,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "console.log(function(){return typeof await}());");
assert.strictEqual(run_code("(async function(){" + result.code + "})();"), "undefined\n");
});
});
describe("rename", function() {
it("Should be repeatable", function() {
var code = "!function(x){return x(x)}(y);";

View File

@@ -760,9 +760,9 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) {
if (minified.error) return minified;
var toplevel = sandbox.has_toplevel(minify_options);
var unminified = run_code(code, toplevel, result_cache, max_timeout);
var unminified = run(code, max_timeout);
var timeout = Math.min(100 * unminified.elapsed, max_timeout);
var minified_result = run_code(minified.code, toplevel, result_cache, timeout).result;
var minified_result = run(minified.code, timeout).result;
if (sandbox.same_stdout(unminified.result, minified_result)) {
return is_timed_out(unminified.result) && is_timed_out(minified_result) && {
@@ -774,6 +774,16 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) {
minified_result: minified_result,
elapsed: unminified.elapsed,
};
function run(code, timeout) {
if (minify_options.module) code = [
'"use strict";',
"(async()=>{",
code,
'})().catch(e=>process.on("exit",()=>{throw e}));',
].join("\n");
return run_code(code, toplevel, result_cache, timeout);
}
}
function test_minify(code, minify_options) {

View File

@@ -140,6 +140,7 @@ var SUPPORT = function(matrix) {
class: "class C { f() {} }",
class_field: "class C { p = 0; }",
class_private: "class C { #f() {} }",
class_static_init: "class C { static {} }",
computed_key: "({[0]: 0});",
const_block: "var a; { const a = 0; }",
default_value: "[ a = 0 ] = [];",
@@ -252,7 +253,7 @@ BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
if (SUPPORT.exponentiation) BINARY_OPS.push("**");
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS.push(" in ");
BINARY_OPS.push(" in ", " instanceof ");
var ASSIGNMENTS = [ "=" ];
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
@@ -405,7 +406,7 @@ function createTopLevelCode() {
unique_vars.length = 0;
classes.length = 0;
allow_this = true;
async = false;
async = SUPPORT.async && rng(200) == 0;
has_await = false;
export_default = false;
generator = false;
@@ -413,7 +414,7 @@ function createTopLevelCode() {
funcs = 0;
clazz = 0;
imports = 0;
in_class = 0;
in_class = async;
called = Object.create(null);
var s = [
strictMode(),
@@ -1181,7 +1182,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
unique_vars.length = unique_len;
});
}
if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
if (n !== 0) s += [
" finally { ",
createStatements(rng(5) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
" }",
].join("");
return s;
case STMT_C:
return "c = c + 1;";
@@ -1839,6 +1844,16 @@ function createClassLiteral(recurmax, stmtDepth, canThrow, name) {
async = save_async;
}
s += ";\n";
} else if (SUPPORT.class_static_init && fixed && !internal && rng(10) == 0) {
async = false;
generator = false;
s += [
"{ ",
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, stmtDepth),
" }\n",
].join("");
generator = save_generator;
async = save_async;
} else {
if (!fixed && !internal && constructor && rng(10) == 0) {
internal = constructor;
@@ -1994,7 +2009,7 @@ function createBinaryOp(noComma, canThrow) {
var op;
do {
op = BINARY_OPS[rng(BINARY_OPS.length)];
} while (noComma && op == "," || !canThrow && op == " in ");
} while (noComma && op == "," || !canThrow && /^ in/.test(op));
return op;
}
@@ -2087,6 +2102,12 @@ if (require.main !== module) {
}
function run_code(code, toplevel, timeout) {
if (async && has_await) code = [
'"use strict";',
"(async()=>{",
code,
'})().catch(e=>process.on("exit",()=>{throw e}));',
].join("\n");
return sandbox.run_code(sandbox.patch_module_statements(code), toplevel, timeout);
}
@@ -2106,7 +2127,9 @@ function errorln(msg) {
}
function try_beautify(code, toplevel, result, printfn, options) {
var beautified = UglifyJS.minify(code, JSON.parse(beautify_options));
var o = JSON.parse(beautify_options);
if (async && has_await) o.module = true;
var beautified = UglifyJS.minify(code, o);
if (beautified.error) {
printfn("// !!! beautify failed !!!");
printfn(beautified.error);
@@ -2259,12 +2282,27 @@ function log(options) {
}
function sort_globals(code) {
var globals = run_code("throw Object.keys(this).sort(" + function(global) {
var injected = "throw Object.keys(this).sort(" + function(global) {
return function(m, n) {
return (typeof global[n] == "function") - (typeof global[m] == "function")
|| (m < n ? -1 : m > n ? 1 : 0);
};
} + "(this));\n" + code);
} + "(this));";
var save_async = async;
if (async && has_await) {
async = false;
injected = [
'"use strict";',
injected,
"(async function(){",
code,
"})();"
].join("\n");
} else {
injected += "\n" + code;
}
var globals = run_code(injected);
async = save_async;
if (!Array.isArray(globals)) {
errorln();
errorln();
@@ -2466,6 +2504,9 @@ var bug_class_static_await = SUPPORT.async && SUPPORT.class_field && sandbox.is_
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_async = SUPPORT.for_await_of && sandbox.is_error(sandbox.run_code("var async; for (async of []);"));
var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && sandbox.is_error(sandbox.run_code("try {} catch (e) { for (var e of []); }"));
var bug_proto_stream = function(ex) {
return ex.name == "TypeError" && ex.message == "callback is not a function";
}
if (SUPPORT.destructuring && sandbox.is_error(sandbox.run_code("console.log([ 1 ], {} = 2);"))) {
beautify_options.output.v8 = true;
minify_options.forEach(function(o) {
@@ -2496,11 +2537,17 @@ for (var round = 1; round <= num_iterations; round++) {
println();
// ignore v8 parser bug
return bug_async_arrow_rest(result)
// ignore Node.js `__proto__` quirks
|| bug_proto_stream(result)
// ignore runtime platform bugs
|| result.message == "Script execution aborted.";
})) continue;
minify_options.forEach(function(options) {
var o = JSON.parse(options);
if (async && has_await) {
o.module = true;
options = JSON.stringify(o);
}
var toplevel = sandbox.has_toplevel(o);
o.validate = true;
uglify_code = UglifyJS.minify(original_code, o);
@@ -2513,6 +2560,8 @@ for (var round = 1; round <= num_iterations; round++) {
var uglify_erred = sandbox.is_error(uglify_result);
// ignore v8 parser bug
if (!ok && uglify_erred && bug_async_arrow_rest(uglify_result)) ok = true;
// ignore Node.js `__proto__` quirks
if (!ok && uglify_erred && bug_proto_stream(uglify_result)) ok = true;
// ignore runtime platform bugs
if (!ok && uglify_erred && uglify_result.message == "Script execution aborted.") ok = true;
// handle difference caused by time-outs