Compare commits

...

14 Commits

Author SHA1 Message Date
Alex Lam S.L
4a1da492dd v3.17.3 2022-10-06 02:14:42 +08:00
Alex Lam S.L
94a954c3d1 minor clean-ups (#5701) 2022-10-06 02:06:21 +08:00
Alex Lam S.L
be8ccc3ab5 fix corner case in varify (#5698)
fixes #5697
2022-10-04 16:03:24 +08:00
Alex Lam S.L
58d997a3d6 fix corner case in booleans & conditionals (#5696) 2022-10-04 12:58:55 +08:00
Alex Lam S.L
dabcc39b51 fix corner cases in booleans & conditionals (#5695)
fixes #5694
2022-10-03 22:47:15 +08:00
Alex Lam S.L
140e4e0da8 fix corner case in inline (#5693)
fixes #5692
2022-10-03 08:01:56 +08:00
Alex Lam S.L
80fc862547 enhance assignments, booleans & conditionals (#5691) 2022-10-03 08:01:23 +08:00
Alex Lam S.L
6cdc035b2f fix corner case in if_return (#5689)
fixes #5688
2022-09-30 06:49:51 +08:00
Alex Lam S.L
e1e3516397 fix corner case in reduce_vars (#5687) 2022-09-30 02:00:37 +08:00
Alex Lam S.L
bd5fc4cb1b implement mangle.properties.domprops (#5686)
- support destructuring syntax
- fix corner case with comma operator
2022-09-29 12:18:04 +08:00
Alex Lam S.L
a570c00251 fix corner case in conditionals & if_return (#5685)
fixes #5684
2022-09-28 00:04:32 +08:00
Alex Lam S.L
3fa2086681 improve usability of mangle.properties (#5683)
fixes #5682
2022-09-27 13:52:58 +08:00
Alex Lam S.L
8e65413b99 fix corner cases in conditionals & if_return (#5680)
fixes #5679
2022-09-27 01:28:03 +08:00
Alex Lam S.L
8ca40070a4 workaround GitHub Actions quirks (#5678) 2022-09-27 00:16:40 +08:00
40 changed files with 1469 additions and 222 deletions

View File

@@ -3,7 +3,7 @@ Contributing
## Documentation ## Documentation
Every new feature and API change should be accompanied by a README additon. Every new feature and API change should be accompanied by a README addition.
## Testing ## Testing

View File

@@ -646,7 +646,7 @@ to be `false` and all symbol names will be omitted.
- `bare_returns` (default: `false`) — support top level `return` statements - `bare_returns` (default: `false`) — support top level `return` statements
- `html5_comments` (default: `true`) — process HTML comment as workaround for - `html5_comments` (default: `true`) — process HTML comment as workaround for
browsers which do not recognise `<script>` tags browsers which do not recognize `<script>` tags
- `module` (default: `false`) — set to `true` if you wish to process input as - `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`. ES module, i.e. implicit `"use strict";` and support for top-level `await`.
@@ -753,7 +753,7 @@ to be `false` and all symbol names will be omitted.
ES module, i.e. implicit `"use strict";` alongside with `toplevel` enabled. 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 parentheses that the
code generator would insert. code generator would insert.
- `objects` (default: `true`) — compact duplicate keys in object literals. - `objects` (default: `true`) — compact duplicate keys in object literals.
@@ -851,7 +851,7 @@ to be `false` and all symbol names will be omitted.
- `unused` (default: `true`) — drop unreferenced functions and variables (simple - `unused` (default: `true`) — drop unreferenced functions and variables (simple
direct variable assignments do not count as references unless set to `"keep_assign"`) direct variable assignments do not count as references unless set to `"keep_assign"`)
- `varify` (default: `true`) — convert block-scoped declaractions into `var` - `varify` (default: `true`) — convert block-scoped declarations into `var`
whenever safe to do so whenever safe to do so
- `yields` (default: `true`) — apply optimizations to `yield` expressions - `yields` (default: `true`) — apply optimizations to `yield` expressions
@@ -891,12 +891,15 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options ### Mangle properties options
- `builtins` (default: `false`) — Use `true` to allow the mangling of builtin - `builtins` (default: `false`) — Use `true` to allow the mangling of built-in
DOM properties. Not recommended to override this setting. properties of JavaScript API. Not recommended to override this setting.
- `debug` (default: `false`) — Mangle names with the original name still present. - `debug` (default: `false`) — Mangle names with the original name still present.
Pass an empty string `""` to enable, or a non-empty string to set the debug suffix. Pass an empty string `""` to enable, or a non-empty string to set the debug suffix.
- `domprops` (default: `false`) — Use `true` to allow the mangling of properties
commonly found in Document Object Model. Not recommended to override this setting.
- `keep_fargs` (default: `false`) — Use `true` to prevent mangling of function - `keep_fargs` (default: `false`) — Use `true` to prevent mangling of function
arguments. arguments.

View File

@@ -238,17 +238,6 @@ if (specified["beautify"] && specified["output-opts"]) fatal("--beautify cannot
[ "compress", "mangle" ].forEach(function(name) { [ "compress", "mangle" ].forEach(function(name) {
if (!(name in options)) options[name] = false; if (!(name in options)) options[name] = false;
}); });
if (options.mangle && options.mangle.properties) {
if (options.mangle.properties.domprops) {
delete options.mangle.properties.domprops;
} else {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
if (!Array.isArray(options.mangle.properties.reserved)) options.mangle.properties.reserved = [];
require("../tools/domprops").forEach(function(name) {
UglifyJS.push_uniq(options.mangle.properties.reserved, name);
});
}
}
if (/^ast|spidermonkey$/.test(output)) { if (/^ast|spidermonkey$/.test(output)) {
if (typeof options.output != "object") options.output = {}; if (typeof options.output != "object") options.output = {};
options.output.ast = true; options.output.ast = true;

View File

@@ -332,7 +332,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
var AST_BlockScope = DEFNODE("BlockScope", "_var_names enclosed functions make_def parent_scope variables", { var AST_BlockScope = DEFNODE("BlockScope", "_var_names enclosed functions make_def parent_scope variables", {
$documentation: "Base class for all statements introducing a lexical scope", $documentation: "Base class for all statements introducing a lexical scope",
$propdoc: { $propdoc: {
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any inner scopes",
functions: "[Dictionary/S] like `variables`, but only lists function declarations", functions: "[Dictionary/S] like `variables`, but only lists function declarations",
parent_scope: "[AST_Scope?/S] link to the parent scope", parent_scope: "[AST_Scope?/S] link to the parent scope",
variables: "[Dictionary/S] a map of name ---> SymbolDef for all variables/functions defined in this scope", variables: "[Dictionary/S] a map of name ---> SymbolDef for all variables/functions defined in this scope",

View File

@@ -368,7 +368,6 @@ Compressor.prototype.compress = function(node) {
args: [], args: [],
}); });
} }
return self;
}); });
AST_Node.DEFMETHOD("wrap_expression", function() { AST_Node.DEFMETHOD("wrap_expression", function() {
var self = this; var self = this;
@@ -497,6 +496,7 @@ Compressor.prototype.compress = function(node) {
function has_escaped(d, scope, node, parent) { function has_escaped(d, scope, node, parent) {
if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node; if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New; if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
if (parent instanceof AST_ClassField) return parent.value === node && !parent.static;
if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve(); if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve();
if (parent instanceof AST_VarDef) return parent.value === node; if (parent instanceof AST_VarDef) return parent.value === node;
} }
@@ -1164,7 +1164,11 @@ Compressor.prototype.compress = function(node) {
if (node.extends) node.extends.walk(tw); if (node.extends) node.extends.walk(tw);
var props = node.properties.filter(function(prop) { var props = node.properties.filter(function(prop) {
reset_flags(prop); reset_flags(prop);
if (prop.key instanceof AST_Node) prop.key.walk(tw); if (prop.key instanceof AST_Node) {
tw.push(prop);
prop.key.walk(tw);
tw.pop();
}
return prop.value; return prop.value;
}); });
if (node.name) { if (node.name) {
@@ -1184,6 +1188,7 @@ Compressor.prototype.compress = function(node) {
} }
} }
props.forEach(function(prop) { props.forEach(function(prop) {
tw.push(prop);
if (!prop.static || is_static_field_or_init(prop) && prop.value.contains_this()) { if (!prop.static || is_static_field_or_init(prop) && prop.value.contains_this()) {
push(tw); push(tw);
prop.value.walk(tw); prop.value.walk(tw);
@@ -1191,6 +1196,7 @@ Compressor.prototype.compress = function(node) {
} else { } else {
prop.value.walk(tw); prop.value.walk(tw);
} }
tw.pop();
}); });
return true; return true;
}); });
@@ -1741,6 +1747,7 @@ Compressor.prototype.compress = function(node) {
var identifier_atom = makePredicate("Infinity NaN undefined"); var identifier_atom = makePredicate("Infinity NaN undefined");
function is_lhs_read_only(lhs, compressor) { function is_lhs_read_only(lhs, compressor) {
if (lhs instanceof AST_Atom) return true;
if (lhs instanceof AST_ObjectIdentity) return true; if (lhs instanceof AST_ObjectIdentity) return true;
if (lhs instanceof AST_PropAccess) { if (lhs instanceof AST_PropAccess) {
if (lhs.property === "__proto__") return true; if (lhs.property === "__proto__") return true;
@@ -3529,6 +3536,7 @@ Compressor.prototype.compress = function(node) {
var declare_only, jump, merge_jump; var declare_only, jump, merge_jump;
var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self; 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 chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences");
var drop_return_void = !(in_try && in_try.bfinally && in_async_generator(in_lambda));
var multiple_if_returns = has_multiple_if_returns(statements); var multiple_if_returns = has_multiple_if_returns(statements);
for (var i = statements.length; --i >= 0;) { for (var i = statements.length; --i >= 0;) {
var stat = statements[i]; var stat = statements[i];
@@ -3536,8 +3544,7 @@ Compressor.prototype.compress = function(node) {
var next = statements[j]; var next = statements[j];
if (in_lambda && declare_only && !next && stat instanceof AST_Return if (in_lambda && declare_only && !next && stat instanceof AST_Return
&& !(self instanceof AST_SwitchBranch) && drop_return_void && !(self instanceof AST_SwitchBranch)) {
&& !(in_try && in_try.bfinally && in_async_generator(in_lambda))) {
var body = stat.value; var body = stat.value;
if (!body) { if (!body) {
changed = true; changed = true;
@@ -3632,7 +3639,8 @@ Compressor.prototype.compress = function(node) {
var value = stat.body.value; var value = stat.body.value;
var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool; var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
// if (foo()) return x; return y; ---> return foo() ? x : y; // if (foo()) return x; return y; ---> return foo() ? x : y;
if (!stat.alternative && next instanceof AST_Return) { if (!stat.alternative && next instanceof AST_Return
&& (drop_return_void || !value == !next.value)) {
changed = true; changed = true;
stat = stat.clone(); stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, { stat.alternative = make_node(AST_BlockStatement, next, {
@@ -3737,14 +3745,17 @@ Compressor.prototype.compress = function(node) {
return true; return true;
} }
if (!(ab instanceof AST_LoopControl)) return false; if (!(ab instanceof AST_LoopControl)) return false;
if (jump && self instanceof AST_SwitchBranch) { if (self instanceof AST_SwitchBranch) {
if (jump instanceof AST_Exit) { if (jump instanceof AST_Exit) {
if (!in_lambda) return false; if (!in_lambda) return false;
if (jump.value) return false; if (jump.value) return false;
} else if (compressor.loopcontrol_target(jump) !== parent) { merge_jump = true;
} else if (jump) {
if (compressor.loopcontrol_target(jump) !== parent) return false;
merge_jump = true;
} else if (jump === false) {
return false; return false;
} }
merge_jump = true;
} }
var lct = compressor.loopcontrol_target(ab); var lct = compressor.loopcontrol_target(ab);
if (ab instanceof AST_Continue) return match_target(loop_body(lct)); if (ab instanceof AST_Continue) return match_target(loop_body(lct));
@@ -3781,7 +3792,7 @@ Compressor.prototype.compress = function(node) {
end = statements.lastIndexOf(stop); end = statements.lastIndexOf(stop);
} else { } else {
stop = statements[end]; stop = statements[end];
if (stop !== jump) jump = null; if (stop !== jump) jump = false;
} }
var tail = statements.splice(start, end - start).filter(function(stat) { var tail = statements.splice(start, end - start).filter(function(stat) {
if (stat instanceof AST_LambdaDefinition) { if (stat instanceof AST_LambdaDefinition) {
@@ -5071,7 +5082,7 @@ Compressor.prototype.compress = function(node) {
return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random"); return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random");
} }
// Accomodate when compress option evaluate=false // Accommodate when compress option evaluate=false
// as well as the common constant expressions !0 and -1 // as well as the common constant expressions !0 and -1
(function(def) { (function(def) {
def(AST_Node, return_false); def(AST_Node, return_false);
@@ -9335,7 +9346,7 @@ Compressor.prototype.compress = function(node) {
return !same_scope(def) || may_overlap(compressor, def); return !same_scope(def) || may_overlap(compressor, def);
} }
}, true)) { }, true)) {
self.init = to_var(self.init); self.init = to_var(self.init, self.resolve());
} }
} }
return self; return self;
@@ -9677,17 +9688,26 @@ Compressor.prototype.compress = function(node) {
alternative: null, alternative: null,
}); });
if (self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) { if (self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) {
var cons_value = self.body.value;
var alt_value = self.alternative.value;
if (!cons_value && !alt_value) return make_node(AST_BlockStatement, self, {
body: [
make_node(AST_SimpleStatement, self, { body: self.condition }),
self.body,
],
}).optimize(compressor);
if (cons_value && alt_value || !keep_return_void()) {
var exit = make_node(self.body.CTOR, self, { var exit = make_node(self.body.CTOR, self, {
value: make_node(AST_Conditional, self, { value: make_node(AST_Conditional, self, {
condition: self.condition, condition: self.condition,
consequent: self.body.value || make_node(AST_Undefined, self.body).transform(compressor), consequent: cons_value || make_node(AST_Undefined, self.body).transform(compressor),
alternative: self.alternative.value alternative: alt_value || make_node(AST_Undefined, self.alternative).transform(compressor),
|| make_node(AST_Undefined, self.alternative).transform(compressor),
}), }),
}); });
if (exit instanceof AST_Return) exit.in_bool = self.body.in_bool || self.alternative.in_bool; if (exit instanceof AST_Return) exit.in_bool = self.body.in_bool || self.alternative.in_bool;
return exit; return exit;
} }
}
if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) { if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) {
self = make_node(AST_If, self, { self = make_node(AST_If, self, {
condition: make_node(AST_Binary, self.condition, { condition: make_node(AST_Binary, self.condition, {
@@ -9752,6 +9772,22 @@ Compressor.prototype.compress = function(node) {
return node instanceof AST_BlockStatement ? node.body : [ node ]; return node instanceof AST_BlockStatement ? node.body : [ node ];
} }
function keep_return_void() {
var has_finally = false, level = 0, node = compressor.self();
do {
if (node instanceof AST_Catch) {
if (compressor.parent(level).bfinally) has_finally = true;
level++;
} else if (node instanceof AST_Finally) {
level++;
} else if (node instanceof AST_Scope) {
return has_finally && in_async_generator(node);
} else if (node instanceof AST_Try) {
if (node.bfinally) has_finally = true;
}
} while (node = compressor.parent(level++));
}
function last_index(stats) { function last_index(stats) {
for (var index = stats.length; --index >= 0;) { for (var index = stats.length; --index >= 0;) {
if (!is_declaration(stats[index], true)) break; if (!is_declaration(stats[index], true)) break;
@@ -10124,14 +10160,13 @@ Compressor.prototype.compress = function(node) {
} }
} }
function to_var(stat) { function to_var(stat, scope) {
return make_node(AST_Var, stat, { return make_node(AST_Var, stat, {
definitions: stat.definitions.map(function(defn) { definitions: stat.definitions.map(function(defn) {
return make_node(AST_VarDef, defn, { return make_node(AST_VarDef, defn, {
name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) { name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
var def = name.definition(); var def = name.definition();
def.orig[def.orig.indexOf(node)] = name; def.orig[def.orig.indexOf(node)] = name;
var scope = def.scope.resolve();
if (def.scope === scope) return; if (def.scope === scope) return;
def.scope = scope; def.scope = scope;
scope.variables.set(def.name, def); scope.variables.set(def.name, def);
@@ -10157,7 +10192,7 @@ Compressor.prototype.compress = function(node) {
return !defn.name.match_symbol(function(node) { return !defn.name.match_symbol(function(node) {
if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node); if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
}, true); }, true);
}) ? to_var(self) : self; }) ? to_var(self, compressor.find_parent(AST_Scope)) : self;
} }
OPT(AST_Const, varify); OPT(AST_Const, varify);
@@ -11553,6 +11588,15 @@ Compressor.prototype.compress = function(node) {
if (node instanceof AST_Unary) return true; if (node instanceof AST_Unary) return true;
} }
function extract_lhs(node, compressor) {
if (node instanceof AST_Assign) return is_lhs_read_only(node.left, compressor) ? node : node.left;
if (node instanceof AST_Sequence) return extract_lhs(node.tail_node(), compressor);
if (node instanceof AST_UnaryPrefix && UNARY_POSTFIX[node.operator]) {
return is_lhs_read_only(node.expression, compressor) ? node : node.expression;
}
return node;
}
function repeatable(compressor, node) { function repeatable(compressor, node) {
if (node instanceof AST_Dot) return repeatable(compressor, node.expression); if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
if (node instanceof AST_Sub) { if (node instanceof AST_Sub) {
@@ -11595,31 +11639,32 @@ Compressor.prototype.compress = function(node) {
if (seq !== self) return seq.optimize(compressor); if (seq !== self) return seq.optimize(compressor);
} }
if (compressor.option("assignments") && lazy_op[self.operator]) { if (compressor.option("assignments") && lazy_op[self.operator]) {
var lhs = extract_lhs(self.left, compressor);
var right = self.right; var right = self.right;
// a || (a = x) ---> a = a || x // a || (a = x) ---> a = a || x
// a && (a = x) ---> a = a && x // (a = x) && (a = y) ---> a = (a = x) && y
if (self.left instanceof AST_SymbolRef if (lhs instanceof AST_SymbolRef
&& right instanceof AST_Assign && right instanceof AST_Assign
&& right.operator == "=" && right.operator == "="
&& self.left.equals(right.left)) { && lhs.equals(right.left)) {
var left = right.left.clone(); lhs = lhs.clone();
var assign = make_node(AST_Assign, self, { var assign = make_node(AST_Assign, self, {
operator: "=", operator: "=",
left: left, left: lhs,
right: make_node(AST_Binary, self, { right: make_node(AST_Binary, self, {
operator: self.operator, operator: self.operator,
left: self.left, left: self.left,
right: right.right, right: right.right,
}), }),
}); });
if (left.fixed) { if (lhs.fixed) {
left.fixed = function() { lhs.fixed = function() {
return assign.right; return assign.right;
}; };
left.fixed.assigns = [ assign ]; lhs.fixed.assigns = [ assign ];
} }
var def = left.definition(); var def = lhs.definition();
def.references.push(left); def.references.push(lhs);
def.replaced++; def.replaced++;
return assign.optimize(compressor); return assign.optimize(compressor);
} }
@@ -11674,31 +11719,32 @@ Compressor.prototype.compress = function(node) {
case "||": case "||":
// void 0 !== x && null !== x ---> null != x // void 0 !== x && null !== x ---> null != x
// void 0 === x || null === x ---> null == x // void 0 === x || null === x ---> null == x
var lhs = self.left; var left = self.left;
if (lhs.operator == self.operator) lhs = lhs.right; if (!(left instanceof AST_Binary)) break;
var expr = lhs.right; if (left.operator != (self.operator == "&&" ? "!==" : "===")) break;
if (!(self.right instanceof AST_Binary)) break;
if (left.operator != self.right.operator) break;
if (is_undefined(left.left, compressor) && self.right.left instanceof AST_Null
|| left.left instanceof AST_Null && is_undefined(self.right.left, compressor)) {
var expr = left.right;
if (expr instanceof AST_Assign && expr.operator == "=") expr = expr.left; if (expr instanceof AST_Assign && expr.operator == "=") expr = expr.left;
if (lhs instanceof AST_Binary if (expr.has_side_effects(compressor)) break;
&& lhs.operator == (self.operator == "&&" ? "!==" : "===") if (!expr.equals(self.right.right)) break;
&& self.right instanceof AST_Binary left.operator = left.operator.slice(0, -1);
&& lhs.operator == self.right.operator left.left = make_node(AST_Null, self);
&& (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null return left;
|| lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
&& !expr.has_side_effects(compressor)
&& expr.equals(self.right.right)) {
lhs.operator = lhs.operator.slice(0, -1);
lhs.left = make_node(AST_Null, self);
return self.left;
} }
break; break;
} }
var in_bool = false; var in_bool = false;
var parent = compressor.parent(); var parent = compressor.parent();
if (compressor.option("booleans")) { if (compressor.option("booleans")) {
var lhs = self.left; var lhs = extract_lhs(self.left, compressor);
if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) { if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
// a || a ---> a
// (a = x) && a --> a = x
if (lhs.equals(self.right)) { if (lhs.equals(self.right)) {
return maintain_this_binding(parent, compressor.self(), lhs).optimize(compressor); return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor);
} }
mark_duplicate_condition(compressor, lhs); mark_duplicate_condition(compressor, lhs);
} }
@@ -11746,13 +11792,22 @@ Compressor.prototype.compress = function(node) {
case ">=": reverse("<="); break; case ">=": reverse("<="); break;
} }
} }
if (compressor.option("conditionals") && lazy_op[self.operator]) {
if (self.left instanceof AST_Binary && self.operator == self.left.operator) {
var before = make_node(AST_Binary, self, {
operator: self.operator,
left: self.left.right,
right: self.right,
});
var after = before.optimize(compressor);
if (before !== after) {
self.left = self.left.left;
self.right = after;
}
}
// x && (y && z) ---> x && y && z // x && (y && z) ---> x && y && z
// x || (y || z) ---> x || y || z // x || (y || z) ---> x || y || z
if (compressor.option("conditionals") if (self.right instanceof AST_Binary && self.operator == self.right.operator) swap_chain(self, compressor);
&& lazy_op[self.operator]
&& self.right instanceof AST_Binary
&& self.operator == self.right.operator) {
swap_chain(self, compressor);
} }
if (compressor.option("strings") && self.operator == "+") { if (compressor.option("strings") && self.operator == "+") {
// "foo" + 42 + "" ---> "foo" + 42 // "foo" + 42 + "" ---> "foo" + 42
@@ -11826,7 +11881,7 @@ Compressor.prototype.compress = function(node) {
if (nullish ? ll == null : !ll) { if (nullish ? ll == null : !ll) {
AST_Node.warn("Condition left of {operator} always {value} [{start}]", { AST_Node.warn("Condition left of {operator} always {value} [{start}]", {
operator: self.operator, operator: self.operator,
value: nullish ? "nulish" : "false", value: nullish ? "nullish" : "false",
start: self.start, start: self.start,
}); });
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
@@ -12270,7 +12325,7 @@ Compressor.prototype.compress = function(node) {
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor)); var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
if (single_use) { if (single_use) {
if (is_lambda(fixed)) { if (is_lambda(fixed)) {
if ((def.scope !== self.scope.resolve() || def.in_loop) if ((def.scope !== self.scope.resolve(true) || def.in_loop)
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) { && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
single_use = false; single_use = false;
} else if (def.redefined()) { } else if (def.redefined()) {
@@ -12845,15 +12900,16 @@ Compressor.prototype.compress = function(node) {
} }
var consequent = self.consequent; var consequent = self.consequent;
var alternative = self.alternative; var alternative = self.alternative;
if (repeatable(compressor, condition)) { var cond_lhs = extract_lhs(condition, compressor);
if (repeatable(compressor, cond_lhs)) {
// x ? x : y ---> x || y // x ? x : y ---> x || y
if (condition.equals(consequent)) return make_node(AST_Binary, self, { if (cond_lhs.equals(consequent)) return make_node(AST_Binary, self, {
operator: "||", operator: "||",
left: condition, left: condition,
right: alternative, right: alternative,
}).optimize(compressor); }).optimize(compressor);
// x ? y : x ---> x && y // x ? y : x ---> x && y
if (condition.equals(alternative)) return make_node(AST_Binary, self, { if (cond_lhs.equals(alternative)) return make_node(AST_Binary, self, {
operator: "&&", operator: "&&",
left: condition, left: condition,
right: consequent, right: consequent,
@@ -13834,7 +13890,10 @@ Compressor.prototype.compress = function(node) {
var abort = false; var abort = false;
stat.walk(new TreeWalker(function(node) { stat.walk(new TreeWalker(function(node) {
if (abort) return true; if (abort) return true;
if (async && node instanceof AST_Await || node instanceof AST_Return) return abort = true; if (async && (node instanceof AST_Await || node instanceof AST_ForAwaitOf)
|| node instanceof AST_Return) {
return abort = true;
}
if (node instanceof AST_Scope) return true; if (node instanceof AST_Scope) return true;
})); }));
return !abort; return !abort;

View File

@@ -76,7 +76,7 @@ function OutputStream(options) {
wrap_iife : false, wrap_iife : false,
}, true); }, true);
// Convert comment option to RegExp if neccessary and set up comments filter // Convert comment option to RegExp if necessary and set up comments filter
var comment_filter = return_false; // Default case, throw all comments away var comment_filter = return_false; // Default case, throw all comments away
if (options.comments) { if (options.comments) {
var comments = options.comments; var comments = options.comments;
@@ -997,7 +997,7 @@ function OutputStream(options) {
if (self.init instanceof AST_Definitions) { if (self.init instanceof AST_Definitions) {
self.init.print(output); self.init.print(output);
} else { } else {
parenthesize_for_noin(self.init, output, true); parenthesize_for_no_in(self.init, output, true);
} }
output.print(";"); output.print(";");
output.space(); output.space();
@@ -1413,7 +1413,7 @@ function OutputStream(options) {
print_braced(this, output); print_braced(this, output);
}); });
function print_definitinos(type) { function print_definitions(type) {
return function(output) { return function(output) {
var self = this; var self = this;
output.print(type); output.print(type);
@@ -1426,15 +1426,15 @@ function OutputStream(options) {
if (!(p instanceof AST_IterationStatement && p.init === self)) output.semicolon(); if (!(p instanceof AST_IterationStatement && p.init === self)) output.semicolon();
}; };
} }
DEFPRINT(AST_Const, print_definitinos("const")); DEFPRINT(AST_Const, print_definitions("const"));
DEFPRINT(AST_Let, print_definitinos("let")); DEFPRINT(AST_Let, print_definitions("let"));
DEFPRINT(AST_Var, print_definitinos("var")); DEFPRINT(AST_Var, print_definitions("var"));
function parenthesize_for_noin(node, output, noin) { function parenthesize_for_no_in(node, output, no_in) {
var parens = false; var parens = false;
// need to take some precautions here: // need to take some precautions here:
// https://github.com/mishoo/UglifyJS/issues/60 // https://github.com/mishoo/UglifyJS/issues/60
if (noin) node.walk(new TreeWalker(function(node) { if (no_in) node.walk(new TreeWalker(function(node) {
if (parens) return true; if (parens) return true;
if (node instanceof AST_Binary && node.operator == "in") return parens = true; if (node instanceof AST_Binary && node.operator == "in") return parens = true;
if (node instanceof AST_Scope && !(is_arrow(node) && node.value)) return true; if (node instanceof AST_Scope && !(is_arrow(node) && node.value)) return true;
@@ -1450,8 +1450,8 @@ function OutputStream(options) {
output.print("="); output.print("=");
output.space(); output.space();
var p = output.parent(1); var p = output.parent(1);
var noin = p instanceof AST_For || p instanceof AST_ForEnumeration; var no_in = p instanceof AST_For || p instanceof AST_ForEnumeration;
parenthesize_for_noin(self.value, output, noin); parenthesize_for_no_in(self.value, output, no_in);
} }
}); });

View File

@@ -782,7 +782,7 @@ function parse($TEXT, options) {
else if (!optional && !can_insert_semicolon()) expect(";"); else if (!optional && !can_insert_semicolon()) expect(";");
} }
function parenthesised() { function parenthesized() {
expect("("); expect("(");
var exp = expression(); var exp = expression();
expect(")"); expect(")");
@@ -920,18 +920,18 @@ function parse($TEXT, options) {
next(); next();
var body = in_loop(statement); var body = in_loop(statement);
expect_token("keyword", "while"); expect_token("keyword", "while");
var condition = parenthesised(); var condition = parenthesized();
semicolon(true); semicolon(true);
return new AST_Do({ return new AST_Do({
body : body, body : body,
condition : condition condition : condition,
}); });
case "while": case "while":
next(); next();
return new AST_While({ return new AST_While({
condition : parenthesised(), condition : parenthesized(),
body : in_loop(statement) body : in_loop(statement),
}); });
case "for": case "for":
@@ -959,15 +959,13 @@ function parse($TEXT, options) {
value = expression(); value = expression();
semicolon(); semicolon();
} }
return new AST_Return({ return new AST_Return({ value: value });
value: value
});
case "switch": case "switch":
next(); next();
return new AST_Switch({ return new AST_Switch({
expression : parenthesised(), expression : parenthesized(),
body : in_loop(switch_body_) body : in_loop(switch_body_),
}); });
case "throw": case "throw":
@@ -976,9 +974,7 @@ function parse($TEXT, options) {
croak("Illegal newline after 'throw'"); croak("Illegal newline after 'throw'");
var value = expression(); var value = expression();
semicolon(); semicolon();
return new AST_Throw({ return new AST_Throw({ value: value });
value: value
});
case "try": case "try":
next(); next();
@@ -996,8 +992,8 @@ function parse($TEXT, options) {
} }
next(); next();
return new AST_With({ return new AST_With({
expression : parenthesised(), expression : parenthesized(),
body : statement() body : statement(),
}); });
} }
} }
@@ -1421,15 +1417,15 @@ function parse($TEXT, options) {
}; };
function if_() { function if_() {
var cond = parenthesised(), body = statement(), belse = null; var cond = parenthesized(), body = statement(), alt = null;
if (is("keyword", "else")) { if (is("keyword", "else")) {
next(); next();
belse = statement(); alt = statement();
} }
return new AST_If({ return new AST_If({
condition : cond, condition : cond,
body : body, body : body,
alternative : belse alternative : alt,
}); });
} }
@@ -2171,9 +2167,9 @@ function parse($TEXT, options) {
token_error(sym.start, "Unexpected " + sym.name + " in strict mode"); token_error(sym.start, "Unexpected " + sym.name + " in strict mode");
} }
function as_symbol(type, noerror) { function as_symbol(type, no_error) {
if (!is("name")) { if (!is("name")) {
if (!noerror) croak("Name expected"); if (!no_error) croak("Name expected");
return null; return null;
} }
var sym = _make_symbol(type, S.token); var sym = _make_symbol(type, S.token);
@@ -2409,20 +2405,20 @@ function parse($TEXT, options) {
return new ctor({ operator: op, expression: expr }); return new ctor({ operator: op, expression: expr });
} }
var expr_op = function(left, min_prec, no_in) { var expr_op = function(left, min_precision, no_in) {
var op = is("operator") ? S.token.value : null; var op = is("operator") ? S.token.value : null;
if (op == "in" && no_in) op = null; if (op == "in" && no_in) op = null;
var prec = op != null ? PRECEDENCE[op] : null; var precision = op != null ? PRECEDENCE[op] : null;
if (prec != null && prec > min_prec) { if (precision != null && precision > min_precision) {
next(); next();
var right = expr_op(maybe_unary(no_in), op == "**" ? prec - 1 : prec, no_in); var right = expr_op(maybe_unary(no_in), op == "**" ? precision - 1 : precision, no_in);
return expr_op(new AST_Binary({ return expr_op(new AST_Binary({
start : left.start, start : left.start,
left : left, left : left,
operator : op, operator : op,
right : right, right : right,
end : right.end end : right.end,
}), min_prec, no_in); }), min_precision, no_in);
} }
return left; return left;
}; };

View File

@@ -124,12 +124,16 @@ function get_builtins() {
function reserve_quoted_keys(ast, reserved) { function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) { ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ClassProperty) { if (node instanceof AST_ClassProperty
if (node.start && node.start.quote) add(node.key); || node instanceof AST_DestructuredKeyVal
|| node instanceof AST_ObjectProperty) {
if (node.key instanceof AST_Node) {
addStrings(node.key, add);
} else if (node.start && node.start.quote) {
add(node.key);
}
} else if (node instanceof AST_Dot) { } else if (node instanceof AST_Dot) {
if (node.quoted) add(node.property); if (node.quoted) add(node.property);
} else if (node instanceof AST_ObjectProperty) {
if (node.start && node.start.quote) add(node.key);
} else if (node instanceof AST_Sub) { } else if (node instanceof AST_Sub) {
addStrings(node.property, add); addStrings(node.property, add);
} }
@@ -156,12 +160,16 @@ function mangle_properties(ast, options) {
builtins: false, builtins: false,
cache: null, cache: null,
debug: false, debug: false,
domprops: false,
keep_quoted: false, keep_quoted: false,
regex: null, regex: null,
reserved: null, reserved: null,
}, true); }, true);
var reserved = options.builtins ? new Dictionary() : get_builtins(); var reserved = options.builtins ? new Dictionary() : get_builtins();
if (!options.domprops && typeof domprops !== "undefined") domprops.forEach(function(name) {
reserved.set(name, true);
});
if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) { if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) {
reserved.set(name, true); reserved.set(name, true);
}); });
@@ -180,7 +188,7 @@ function mangle_properties(ast, options) {
var regex = options.regex; var regex = options.regex;
// note debug is either false (disabled), or a string of the debug suffix to use (enabled). // note debug is either false (disabled), or a string of the debug suffix to use (enabled).
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true' // note debug may be enabled as an empty string, which is falsy. Also treat passing 'true'
// the same as passing an empty string. // the same as passing an empty string.
var debug = options.debug !== false; var debug = options.debug !== false;
var debug_suffix; var debug_suffix;
@@ -191,9 +199,7 @@ function mangle_properties(ast, options) {
// step 1: find candidates to mangle // step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node) { ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_Binary) { if (node.TYPE == "Call") {
if (node.operator == "in") addStrings(node.left, add);
} else if (node.TYPE == "Call") {
var exp = node.expression; var exp = node.expression;
if (exp instanceof AST_Dot) switch (exp.property) { if (exp instanceof AST_Dot) switch (exp.property) {
case "defineProperty": case "defineProperty":
@@ -210,14 +216,18 @@ function mangle_properties(ast, options) {
addStrings(node.args[0], add); addStrings(node.args[0], add);
break; break;
} }
} else if (node instanceof AST_ClassProperty) { } else if (node instanceof AST_ClassProperty
if (typeof node.key == "string") add(node.key); || node instanceof AST_DestructuredKeyVal
|| node instanceof AST_ObjectProperty) {
if (node.key instanceof AST_Node) {
addStrings(node.key, add);
} else {
add(node.key);
}
} else if (node instanceof AST_Dot) { } else if (node instanceof AST_Dot) {
add(node.property); if (is_lhs(node, this.parent())) add(node.property);
} else if (node instanceof AST_ObjectProperty) {
if (typeof node.key == "string") add(node.key);
} else if (node instanceof AST_Sub) { } else if (node instanceof AST_Sub) {
addStrings(node.property, add); if (is_lhs(node, this.parent())) addStrings(node.property, add);
} }
})); }));
@@ -242,12 +252,16 @@ function mangle_properties(ast, options) {
mangleStrings(node.args[0]); mangleStrings(node.args[0]);
break; break;
} }
} else if (node instanceof AST_ClassProperty) { } else if (node instanceof AST_ClassProperty
if (typeof node.key == "string") node.key = mangle(node.key); || node instanceof AST_DestructuredKeyVal
|| node instanceof AST_ObjectProperty) {
if (node.key instanceof AST_Node) {
mangleStrings(node.key);
} else {
node.key = mangle(node.key);
}
} else if (node instanceof AST_Dot) { } else if (node instanceof AST_Dot) {
node.property = mangle(node.property); node.property = mangle(node.property);
} else if (node instanceof AST_ObjectProperty) {
if (typeof node.key == "string") node.key = mangle(node.key);
} else if (node instanceof AST_Sub) { } else if (node instanceof AST_Sub) {
if (!options.keep_quoted) mangleStrings(node.property); if (!options.keep_quoted) mangleStrings(node.property);
} }
@@ -303,7 +317,7 @@ function mangle_properties(ast, options) {
function mangleStrings(node) { function mangleStrings(node) {
if (node instanceof AST_Sequence) { if (node instanceof AST_Sequence) {
mangleStrings(node.expressions.tail_node()); mangleStrings(node.tail_node());
} else if (node instanceof AST_String) { } else if (node instanceof AST_String) {
node.value = mangle(node.value); node.value = mangle(node.value);
} else if (node instanceof AST_Conditional) { } else if (node instanceof AST_Conditional) {

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.17.2", "version": "3.17.3",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -126,6 +126,17 @@ function parse_test(file) {
croak(node); croak(node);
} }
var name = node.left.name; var name = node.left.name;
assert.ok([
"beautify",
"expression",
"mangle",
"options",
"rename",
].indexOf(name) >= 0, tmpl("Unsupported setting {name} [{line},{col}]", {
name: name,
line: node.start.line,
col: node.start.col,
}));
test[name] = evaluate(node.right); test[name] = evaluate(node.right);
return true; return true;
} }
@@ -271,7 +282,9 @@ function test_case(test) {
expect = test.expect_exact; expect = test.expect_exact;
} }
var input = to_toplevel(test.input, test.mangle, test.expression); var input = to_toplevel(test.input, test.mangle, test.expression);
var input_code = make_code(input, {}, test.expression); var input_code = make_code(input, {
keep_quoted_props: true,
}, test.expression);
var input_formatted = make_code(test.input, { var input_formatted = make_code(test.input, {
annotations: true, annotations: true,
beautify: true, beautify: true,

View File

@@ -290,6 +290,45 @@ increment_decrement_2: {
expect_stdout: "42" expect_stdout: "42"
} }
lazily_chained_assignments: {
options = {
assignments: true,
collapse_vars: true,
conditionals: true,
unused: true,
}
input: {
function f(a) {
if (a = console.log("foo"))
a = console.log("bar");
return a;
}
function g(b) {
if (b = console.log("baz"))
;
else
b = console.log("moo");
return b;
}
console.log(f(), g());
}
expect: {
function f(a) {
return console.log("foo") && console.log("bar");
}
function g(b) {
return console.log("baz") || console.log("moo");
}
console.log(f(), g());
}
expect_stdout: [
"foo",
"baz",
"moo",
"undefined undefined",
]
}
issue_3375_1: { issue_3375_1: {
options = { options = {
assignments: true, assignments: true,

View File

@@ -3540,3 +3540,61 @@ issue_5634_3_side_effects: {
] ]
node_version: ">=8" node_version: ">=8"
} }
issue_5692_1: {
options = {
awaits: true,
inline: true,
}
input: {
(async function() {
(async function() {
for await (var k of []);
})();
console.log("foo");
})();
console.log("bar");
}
expect: {
(async function() {
(async function() {
for await (var k of []);
})();
console.log("foo");
})();
console.log("bar");
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=10"
}
issue_5692_2: {
options = {
awaits: true,
inline: true,
}
input: {
(async function() {
(async function() {
for (var k of []);
})();
console.log("foo");
})();
console.log("bar");
}
expect: {
(async function() {
for (var k of []);
console.log("foo");
})();
console.log("bar");
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=8"
}

View File

@@ -80,6 +80,25 @@ de_morgan_1c: {
expect_stdout: "true" expect_stdout: "true"
} }
de_morgan_1d: {
options = {
booleans: true,
}
input: {
function f(a) {
return (a = false) || a;
}
console.log(f(null), f(42));
}
expect: {
function f(a) {
return a = !1;
}
console.log(f(null), f(42));
}
expect_stdout: "false false"
}
de_morgan_2a: { de_morgan_2a: {
options = { options = {
booleans: true, booleans: true,
@@ -181,6 +200,31 @@ de_morgan_2d: {
] ]
} }
de_morgan_2e: {
options = {
booleans: true,
conditionals: true,
}
input: {
function f(a, b) {
return (a && b) && b;
}
console.log(f(null), f(null, {}));
console.log(f(42), f(42, {}));
}
expect: {
function f(a, b) {
return a && b;
}
console.log(f(null), f(null, {}));
console.log(f(42), f(42, {}));
}
expect_stdout: [
"null null",
"undefined {}",
]
}
de_morgan_3a: { de_morgan_3a: {
options = { options = {
booleans: true, booleans: true,
@@ -786,3 +830,37 @@ issue_5469: {
} }
expect_stdout: "undefined" expect_stdout: "undefined"
} }
issue_5694_1: {
options = {
booleans: true,
conditionals: true,
}
input: {
var Infinity;
// Node.js v0.12~6 (vm): 42
console.log((Infinity = 42) && Infinity);
}
expect: {
var Infinity;
console.log((Infinity = 42) && Infinity);
}
expect_stdout: true
}
issue_5694_2: {
options = {
booleans: true,
conditionals: true,
}
input: {
var undefined;
// Node.js v0.12~6 (vm): NaN
console.log(("foo", ++undefined) || undefined);
}
expect: {
var undefined;
console.log(("foo", ++undefined) || undefined);
}
expect_stdout: true
}

View File

@@ -599,6 +599,7 @@ drop_extends: {
inline: true, inline: true,
passes: 2, passes: 2,
pure_getters: "strict", pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
sequences: true, sequences: true,
side_effects: true, side_effects: true,
@@ -921,6 +922,7 @@ single_use_3: {
single_use_4: { single_use_4: {
options = { options = {
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
toplevel: true, toplevel: true,
unused: true, unused: true,
@@ -1503,6 +1505,218 @@ keep_instanceof_3: {
node_version: ">=4" node_version: ">=4"
} }
keep_field_reference_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {}
class A {
p = f;
}
console.log(new A().p === new A().p ? "PASS" : "FAIL");
}
expect: {
"use strict";
function f() {}
class A {
p = f;
}
console.log(new A().p === new A().p ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=12"
}
keep_field_reference_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {}
var A = class {
p = f;
};
console.log(new A().p === new A().p ? "PASS" : "FAIL");
}
expect: {
"use strict";
function f() {}
var A = class {
p = f;
};
console.log(new A().p === new A().p ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=12"
}
keep_field_reference_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
class A {}
class B {
p = A;
}
console.log(new B().p === new B().p ? "PASS" : "FAIL");
}
expect: {
"use strict";
class A {}
class B {
p = A;
}
console.log(new B().p === new B().p ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=12"
}
keep_field_reference_4: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var A = class {};
var B = class {
p = A;
};
console.log(new B().p === new B().p ? "PASS" : "FAIL");
}
expect: {
"use strict";
var A = class {};
var B = class {
p = A;
};
console.log(new B().p === new B().p ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=12"
}
keep_static_field_reference_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {}
class A {
static P = f;
}
console.log(A.P === A.P ? "PASS" : "FAIL");
}
expect: {
"use strict";
class A {
static P = function() {};
}
console.log(A.P === A.P ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=12"
}
keep_static_field_reference_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {}
var A = class {
static P = f;
};
console.log(A.P === A.P ? "PASS" : "FAIL");
}
expect: {
"use strict";
var A = class {
static P = function() {};
};
console.log(A.P === A.P ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=12"
}
keep_static_field_reference_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
class A {}
class B {
static P = A;
}
console.log(B.P === B.P ? "PASS" : "FAIL");
}
expect: {
"use strict";
class B {
static P = class {};
}
console.log(B.P === B.P ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=12"
}
keep_static_field_reference_4: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var A = class {};
var B = class {
static P = A;
};
console.log(B.P === B.P ? "PASS" : "FAIL");
}
expect: {
"use strict";
var B = class {
static P = class {};
};
console.log(B.P === B.P ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=12"
}
issue_805_1: { issue_805_1: {
options = { options = {
inline: true, inline: true,
@@ -2164,6 +2378,7 @@ issue_4829_2: {
mangle_properties: { mangle_properties: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
keep_quoted: true, keep_quoted: true,
}, },
} }
@@ -2202,11 +2417,11 @@ mangle_properties: {
expect_stdout: "PASS 42" expect_stdout: "PASS 42"
expect_warnings: [ expect_warnings: [
"INFO: Preserving reserved property q", "INFO: Preserving reserved property q",
"INFO: Preserving reserved property log",
"INFO: Mapping property #P to #t", "INFO: Mapping property #P to #t",
"INFO: Mapping property Q to s", "INFO: Mapping property Q to s",
"INFO: Mapping property #p to #i", "INFO: Mapping property #p to #i",
"INFO: Mapping property r to e", "INFO: Mapping property r to e",
"INFO: Preserving reserved property log",
] ]
node_version: ">=14.6" node_version: ">=14.6"
} }
@@ -2312,6 +2527,7 @@ issue_4962_1: {
options = { options = {
ie: true, ie: true,
inline: true, inline: true,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2343,6 +2559,7 @@ issue_4962_1_strict: {
options = { options = {
ie: true, ie: true,
inline: true, inline: true,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2370,6 +2587,7 @@ issue_4962_1_strict_direct: {
options = { options = {
ie: true, ie: true,
inline: true, inline: true,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2401,6 +2619,7 @@ issue_4962_2: {
options = { options = {
ie: true, ie: true,
inline: true, inline: true,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2432,6 +2651,7 @@ issue_4962_2_strict: {
options = { options = {
ie: true, ie: true,
inline: true, inline: true,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2460,6 +2680,7 @@ issue_4962_2_strict_direct: {
options = { options = {
ie: true, ie: true,
inline: true, inline: true,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2494,6 +2715,7 @@ issue_4962_2_strict_direct_inline: {
ie: true, ie: true,
inline: true, inline: true,
passes: 2, passes: 2,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2877,6 +3099,7 @@ issue_5053_4: {
issue_5082_1: { issue_5082_1: {
options = { options = {
inline: true, inline: true,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2909,6 +3132,7 @@ issue_5082_1: {
issue_5082_1_strict: { issue_5082_1_strict: {
options = { options = {
inline: true, inline: true,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2942,6 +3166,7 @@ issue_5082_2: {
options = { options = {
inline: true, inline: true,
passes: 2, passes: 2,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2975,6 +3200,7 @@ issue_5082_2_static: {
options = { options = {
inline: true, inline: true,
passes: 2, passes: 2,
reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -3619,3 +3845,59 @@ issue_5662: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=6" node_version: ">=6"
} }
issue_5682_class_key: {
mangle = {
properties: true,
}
input: {
"use strict";
function f(a) {
return "foo" in a;
}
class A {
foo() {}
}
console.log(f(new A()) ? "PASS" : "FAIL");
}
expect: {
"use strict";
function f(o) {
return "o" in o;
}
class A {
o() {}
}
console.log(f(new A()) ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_5682_class_key_computed: {
mangle = {
properties: true,
}
input: {
"use strict";
function f(a) {
return "foo" in a;
}
class A {
["foo"]() {}
}
console.log(f(new A()) ? "PASS" : "FAIL");
}
expect: {
"use strict";
function f(o) {
return "o" in o;
}
class A {
["o"]() {}
}
console.log(f(new A()) ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -275,6 +275,7 @@ issue_2857_3: {
issue_2857_4: { issue_2857_4: {
options = { options = {
comparisons: true, comparisons: true,
conditionals: true,
} }
input: { input: {
function f(a, p) { function f(a, p) {
@@ -305,6 +306,7 @@ issue_2857_4: {
issue_2857_5: { issue_2857_5: {
options = { options = {
comparisons: true, comparisons: true,
conditionals: true,
} }
input: { input: {
function f(a, p) { function f(a, p) {
@@ -528,6 +530,7 @@ nullish_assign: {
nullish_chain: { nullish_chain: {
options = { options = {
comparisons: true, comparisons: true,
conditionals: true,
} }
input: { input: {
var a; var a;

View File

@@ -12,7 +12,7 @@ concat_1: {
var e = 1 + x() + 2 + "X" + 3 + "boo"; var e = 1 + x() + 2 + "X" + 3 + "boo";
// be careful with concatentation with "\0" with octal-looking strings. // be careful with concatenation with "\0" with octal-looking strings.
var f = "\0" + 360 + "\0" + 8 + "\0"; var f = "\0" + 360 + "\0" + 8 + "\0";
} }
expect: { expect: {

View File

@@ -3028,8 +3028,26 @@ issue_5673_2: {
expect: { expect: {
var a = "PASS"; var a = "PASS";
console.log(function(b) { console.log(function(b) {
return ((b = a) || (b = a)) && b; return a || (b = a) && b;
}()); }());
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_5694: {
options = {
conditionals: true,
}
input: {
FORCE_EXEC = "async()=>{}";
var a = "foo";
// Node.js v0.12~6 (vm): foo
console.log((NaN = a) ? NaN : 42);
}
expect: {
FORCE_EXEC = "async()=>{}";
var a = "foo";
console.log((NaN = a) ? NaN : 42);
}
expect_stdout: "NaN"
}

View File

@@ -1737,6 +1737,23 @@ singleton_side_effects: {
node_version: ">=6" node_version: ">=6"
} }
mangle_properties: {
mangle = {
properties: {
domprops: true,
},
}
input: {
function f({ p: a }) {
return a;
}
console.log(f({ p: "PASS" }));
}
expect_exact: 'function f({n}){return n}console.log(f({n:"PASS"}));'
expect_stdout: "PASS"
node_version: ">=6"
}
issue_4280: { issue_4280: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -8719,3 +8719,24 @@ single_use_inline_collision: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_5692: {
options = {
inline: true,
}
input: {
(function() {
while (console.log("PASS"))
if (console)
return;
})();
}
expect: {
(function() {
while (console.log("PASS"))
if (console)
return;
})();
}
expect_stdout: "PASS"
}

View File

@@ -525,7 +525,7 @@ if_var_return_2: {
} }
} }
if_var_retrn_3: { if_var_return_3: {
options = { options = {
conditionals: true, conditionals: true,
if_return: true, if_return: true,
@@ -2442,3 +2442,41 @@ issue_5649: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_5688: {
options = {
conditionals: true,
if_return: true,
}
input: {
L: do {
switch (console) {
default:
if (console)
break;
if (FAIL_1)
;
else
break L;
break;
case 42:
FAIL_2;
}
} while (console.log("PASS"));
}
expect: {
L: do {
switch (console) {
default:
if (console)
break;
if (FAIL_1)
break;
break L;
case 42:
FAIL_2;
}
} while (console.log("PASS"));
}
expect_stdout: "PASS"
}

View File

@@ -140,12 +140,12 @@ mangle: {
} }
input: { input: {
import foo, { bar } from "baz"; import foo, { bar } from "baz";
consoe.log(moo); console.log(moo);
import * as moo from "moz"; import * as moo from "moz";
} }
expect: { expect: {
import o, { bar as m } from "baz"; import o, { bar as m } from "baz";
consoe.log(r); console.log(r);
import * as r from "moz"; import * as r from "moz";
} }
} }
@@ -157,12 +157,12 @@ rename_mangle: {
} }
input: { input: {
import foo, { bar } from "baz"; import foo, { bar } from "baz";
consoe.log(moo); console.log(moo);
import * as moo from "moz"; import * as moo from "moz";
} }
expect: { expect: {
import o, { bar as m } from "baz"; import o, { bar as m } from "baz";
consoe.log(r); console.log(r);
import * as r from "moz"; import * as r from "moz";
} }
} }

View File

@@ -1,6 +1,7 @@
issue_1321_no_debug: { issue_1321_no_debug: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
keep_quoted: true, keep_quoted: true,
}, },
} }
@@ -23,6 +24,7 @@ issue_1321_debug: {
mangle = { mangle = {
properties: { properties: {
debug: "", debug: "",
domprops: true,
keep_quoted: true, keep_quoted: true,
}, },
} }
@@ -44,6 +46,7 @@ issue_1321_debug: {
issue_1321_with_quoted: { issue_1321_with_quoted: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
keep_quoted: false, keep_quoted: false,
}, },
} }

View File

@@ -1,5 +1,5 @@
/** /**
* There was an incorrect sort behaviour documented in issue #143: * There was an incorrect sort behavior documented in issue #143:
* (x = f(…)) <= x → x >= (x = f(…)) * (x = f(…)) <= x → x >= (x = f(…))
* *
* For example, let the equation be: * For example, let the equation be:
@@ -12,37 +12,54 @@
* a >= (a = parseInt('100')) → 99 >= 100 → false * a >= (a = parseInt('100')) → 99 >= 100 → false
*/ */
tranformation_sort_order_equal: { transformation_sort_order_equal: {
options = { options = {
comparisons: true, comparisons: true,
} }
input: {
input: { (a = parseInt('100')) == a } console.log((a = parseInt("100")) == a);
expect: { (a = parseInt('100')) == a } }
expect: {
console.log((a = parseInt("100")) == a);
}
expect_stdout: "true"
} }
tranformation_sort_order_unequal: { transformation_sort_order_unequal: {
options = { options = {
comparisons: true, comparisons: true,
} }
input: {
input: { (a = parseInt('100')) != a } console.log((a = parseInt("100")) != a);
expect: { (a = parseInt('100')) != a } }
expect: {
console.log((a = parseInt("100")) != a);
}
expect_stdout: "false"
} }
tranformation_sort_order_lesser_or_equal: { transformation_sort_order_lesser_or_equal: {
options = { options = {
comparisons: true, comparisons: true,
} }
input: {
input: { (a = parseInt('100')) <= a } console.log((a = parseInt("100")) <= a);
expect: { (a = parseInt('100')) <= a } }
expect: {
console.log((a = parseInt("100")) <= a);
}
expect_stdout: "true"
} }
tranformation_sort_order_greater_or_equal: {
transformation_sort_order_greater_or_equal: {
options = { options = {
comparisons: true, comparisons: true,
} }
input: {
input: { (a = parseInt('100')) >= a } console.log((a = parseInt("100")) >= a);
expect: { (a = parseInt('100')) >= a } }
expect: {
console.log((a = parseInt("100")) >= a);
}
expect_stdout: "true"
} }

View File

@@ -65,7 +65,9 @@ mangle_props: {
numeric_literal: { numeric_literal: {
mangle = { mangle = {
properties: true, properties: {
domprops: true,
},
} }
beautify = { beautify = {
beautify: true, beautify: true,
@@ -115,9 +117,9 @@ numeric_literal: {
"8 7 8", "8 7 8",
] ]
expect_warnings: [ expect_warnings: [
"INFO: Preserving reserved property log",
"INFO: Mapping property 0x25 to o", "INFO: Mapping property 0x25 to o",
"INFO: Mapping property 1E42 to b", "INFO: Mapping property 1E42 to b",
"INFO: Preserving reserved property log",
] ]
} }
@@ -125,6 +127,7 @@ identifier: {
mangle = { mangle = {
properties: { properties: {
builtins: true, builtins: true,
domprops: true,
}, },
} }
input: { input: {

View File

@@ -15,7 +15,7 @@ collapse: {
var a; var a;
b = c(); b = c();
a = typeof b === 'function' ? b() : b; a = typeof b === 'function' ? b() : b;
return 'stirng' == typeof a && d(); return 'string' == typeof a && d();
} }
function f3(c) { function f3(c) {
var a; var a;
@@ -41,7 +41,7 @@ collapse: {
return void 0 !== ('function' === typeof b ? b() : b) && c(); return void 0 !== ('function' === typeof b ? b() : b) && c();
} }
function f2(b) { function f2(b) {
return 'stirng' == typeof ('function' === typeof (b = c()) ? b() : b) && d(); return 'string' == typeof ('function' === typeof (b = c()) ? b() : b) && d();
} }
function f3(c) { function f3(c) {
var a; var a;

View File

@@ -1,6 +1,7 @@
dont_reuse_prop: { dont_reuse_prop: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
regex: /asd/, regex: /asd/,
}, },
} }
@@ -21,14 +22,15 @@ dont_reuse_prop: {
expect_stdout: "123" expect_stdout: "123"
expect_warnings: [ expect_warnings: [
"INFO: Preserving excluded property a", "INFO: Preserving excluded property a",
"INFO: Preserving reserved property log",
"INFO: Mapping property asd to b", "INFO: Mapping property asd to b",
"INFO: Preserving reserved property log",
] ]
} }
unmangleable_props_should_always_be_reserved: { unmangleable_props_should_always_be_reserved: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
regex: /asd/, regex: /asd/,
}, },
} }
@@ -49,7 +51,7 @@ unmangleable_props_should_always_be_reserved: {
expect_stdout: "123" expect_stdout: "123"
expect_warnings: [ expect_warnings: [
"INFO: Preserving excluded property a", "INFO: Preserving excluded property a",
"INFO: Preserving reserved property log",
"INFO: Mapping property asd to b", "INFO: Mapping property asd to b",
"INFO: Preserving reserved property log",
] ]
} }

View File

@@ -570,7 +570,7 @@ inlined_assignments: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
inilne_for: { inline_for: {
options = { options = {
inline: true, inline: true,
join_vars: true, join_vars: true,
@@ -590,7 +590,7 @@ inilne_for: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
inilne_var: { inline_var: {
options = { options = {
inline: true, inline: true,
join_vars: true, join_vars: true,

View File

@@ -173,7 +173,9 @@ numeric_literal: {
side_effects: true, side_effects: true,
} }
mangle = { mangle = {
properties: true, properties: {
domprops: true,
},
} }
beautify = { beautify = {
beautify: true, beautify: true,

View File

@@ -133,6 +133,7 @@ evaluate_string_length: {
mangle_properties_1: { mangle_properties_1: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
keep_quoted: false, keep_quoted: false,
}, },
} }
@@ -147,17 +148,18 @@ mangle_properties_1: {
a["a"] = "bar"; a["a"] = "bar";
a.b = "red"; a.b = "red";
x = {o: 10}; x = {o: 10};
a.r(x.o, a.a); a.run(x.o, a.a);
a['r']({b: "blue", a: "baz"}); a['run']({b: "blue", a: "baz"});
} }
} }
mangle_properties_2: { mangle_properties_2: {
mangle = { mangle = {
properties: { properties: {
domprops: true,
reserved: [ reserved: [
"value", "value",
] ],
}, },
} }
input: { input: {
@@ -199,6 +201,24 @@ mangle_properties_2: {
] ]
} }
mangle_properties_3: {
mangle = {
properties: true,
}
input: {
console.log({
[(console, "foo")]: "PASS",
}.foo);
}
expect: {
console.log({
[(console, "o")]: "PASS",
}.o);
}
expect_stdout: "PASS"
node_version: ">=4"
}
mangle_unquoted_properties: { mangle_unquoted_properties: {
options = { options = {
evaluate: true, evaluate: true,
@@ -207,6 +227,7 @@ mangle_unquoted_properties: {
mangle = { mangle = {
properties: { properties: {
builtins: true, builtins: true,
domprops: true,
keep_quoted: true, keep_quoted: true,
}, },
} }
@@ -308,6 +329,7 @@ mangle_debug_suffix_keep_quoted: {
properties: { properties: {
builtins: true, builtins: true,
debug: "XYZ", debug: "XYZ",
domprops: true,
keep_quoted: true, keep_quoted: true,
reserved: [], reserved: [],
}, },
@@ -962,14 +984,14 @@ issue_2256: {
} }
input: { input: {
({ "keep": 42 }); ({ "keep": 42 });
global.keep = global.change; global.keep = global.change = "PASS";
console.log(keep); console.log(keep);
} }
expect: { expect: {
global.keep = global.l; global.keep = global.l = "PASS";
console.log(keep); console.log(keep);
} }
expect_stdout: "undefined" expect_stdout: "PASS"
} }
lhs_prop_1: { lhs_prop_1: {
@@ -1645,3 +1667,163 @@ issue_5177: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=4" node_version: ">=4"
} }
issue_5682_in_1: {
mangle = {
properties: true,
}
input: {
function f(a) {
return "foo" in a;
}
var o = {};
var p = "foo";
o[p] = 42;
console.log(f(o) ? "PASS" : "FAIL");
}
expect: {
function f(o) {
return "foo" in o;
}
var o = {};
var p = "foo";
o[p] = 42;
console.log(f(o) ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
issue_5682_in_2: {
mangle = {
properties: true,
}
input: {
function f(a) {
return "foo" in a;
}
var o = { foo: 42 };
console.log(f(o) ? "PASS" : "FAIL");
}
expect: {
function f(o) {
return "o" in o;
}
var o = { o: 42 };
console.log(f(o) ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
issue_5682_dot_1: {
mangle = {
properties: true,
}
input: {
function f(a) {
return a.foo;
}
var o = {};
var p = "foo";
o[p] = "PASS";
console.log(f(o));
}
expect: {
function f(o) {
return o.foo;
}
var o = {};
var p = "foo";
o[p] = "PASS";
console.log(f(o));
}
expect_stdout: "PASS"
}
issue_5682_dot_2: {
mangle = {
properties: true,
}
input: {
function f(a) {
return a.foo;
}
var o = { foo: "PASS" };
console.log(f(o));
}
expect: {
function f(o) {
return o.o;
}
var o = { o: "PASS" };
console.log(f(o));
}
expect_stdout: "PASS"
}
issue_5682_dot_2_computed: {
mangle = {
properties: true,
}
input: {
function f(a) {
return a.foo;
}
var o = { ["foo"]: "PASS" };
console.log(f(o));
}
expect: {
function f(o) {
return o.o;
}
var o = { ["o"]: "PASS" };
console.log(f(o));
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_5682_sub_1: {
mangle = {
properties: true,
}
input: {
function f(a) {
return a["foo"];
}
var o = {};
var p = "foo";
o[p] = "PASS";
console.log(f(o));
}
expect: {
function f(o) {
return o["foo"];
}
var o = {};
var p = "foo";
o[p] = "PASS";
console.log(f(o));
}
expect_stdout: "PASS"
}
issue_5682_sub_2: {
mangle = {
properties: true,
}
input: {
function f(a) {
return a["foo"];
}
var o = { foo: "PASS" };
console.log(f(o));
}
expect: {
function f(o) {
return o["o"];
}
var o = { o: "PASS" };
console.log(f(o));
}
expect_stdout: "PASS"
}

View File

@@ -682,3 +682,165 @@ issue_5516: {
expect_stdout: "function" expect_stdout: "function"
node_version: ">=4" node_version: ">=4"
} }
issue_5697_1: {
options = {
if_return: true,
inline: true,
reduce_vars: true,
unused: true,
varify: true,
}
input: {
console.log(function() {
f();
return typeof a;
function f() {
(function() {
for (var k in { foo: 42 }) {
const a = k;
console.log(a);
}
})();
}
}());
}
expect: {
console.log(function() {
(function() {
for (var k in { foo: 42 }) {
var a = k;
console.log(a);
}
})();
return typeof a;
}());
}
expect_stdout: [
"foo",
"undefined",
]
}
issue_5697_2: {
options = {
if_return: true,
inline: true,
reduce_vars: true,
unused: true,
varify: true,
}
input: {
"use strict";
console.log(function() {
f();
return typeof a;
function f() {
(function() {
for (var k in { foo: 42 }) {
let a = k;
console.log(a);
}
})();
}
}());
}
expect: {
"use strict";
console.log(function() {
(function() {
for (var k in { foo: 42 }) {
var a = k;
console.log(a);
}
})();
return typeof a;
}());
}
expect_stdout: [
"foo",
"undefined",
]
node_version: ">=4"
}
issue_5697_3: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
varify: true,
}
input: {
console.log(function() {
f();
return typeof a;
function f() {
(function() {
for (var k in { foo: 42 }) {
const a = k;
console.log(a);
}
})();
}
}());
}
expect: {
console.log(function() {
(function() {
for (var k in { foo: 42 }) {
var a = k;
console.log(a);
}
})();
return typeof a;
}());
}
expect_stdout: [
"foo",
"undefined",
]
}
issue_5697_4: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
varify: true,
}
input: {
"use strict";
console.log(function() {
f();
return typeof a;
function f() {
(function() {
for (var k in { foo: 42 }) {
let a = k;
console.log(a);
}
})();
}
}());
}
expect: {
"use strict";
console.log(function() {
(function() {
for (var k in { foo: 42 }) {
var a = k;
console.log(a);
}
})();
return typeof a;
}());
}
expect_stdout: [
"foo",
"undefined",
]
node_version: ">=4"
}

View File

@@ -1769,3 +1769,252 @@ issue_5663: {
] ]
node_version: ">=6" node_version: ">=6"
} }
issue_5679_1: {
options = {
conditionals: true,
}
input: {
var a = "FAIL";
async function* f(b) {
try {
if (b)
return;
else
return;
} finally {
a = "PASS";
}
}
f().next();
console.log(a);
}
expect: {
var a = "FAIL";
async function* f(b) {
try {
b;
return;
} finally {
a = "PASS";
}
}
f().next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=10"
}
issue_5679_2: {
options = {
conditionals: true,
}
input: {
var a = "FAIL";
async function* f(b) {
try {
if (b)
return undefined;
else
return;
} finally {
a = "PASS";
}
}
f().next();
console.log(a);
}
expect: {
var a = "FAIL";
async function* f(b) {
try {
if (b)
return void 0;
return;
} finally {
a = "PASS";
}
}
f().next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=10"
}
issue_5679_3: {
options = {
conditionals: true,
}
input: {
var a = "FAIL";
async function* f(b) {
try {
if (b)
return;
else
return undefined;
} finally {
a = "PASS";
}
}
f(42).next();
console.log(a);
}
expect: {
var a = "FAIL";
async function* f(b) {
try {
if (b)
return;
return void 0;
} finally {
a = "PASS";
}
}
f(42).next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=10"
}
issue_5679_4: {
options = {
conditionals: true,
}
input: {
var a = "PASS";
async function* f(b) {
try {
if (b)
return undefined;
else
return undefined;
} finally {
a = "FAIL";
}
}
f(null).next();
console.log(a);
}
expect: {
var a = "PASS";
async function* f(b) {
try {
return b, void 0;
} finally {
a = "FAIL";
}
}
f(null).next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=10"
}
issue_5679_5: {
options = {
conditionals: true,
if_return: true,
}
input: {
var a = "FAIL";
async function* f(b) {
try {
if (b)
return console;
else
return;
} finally {
a = "PASS";
}
}
f().next();
console.log(a);
}
expect: {
var a = "FAIL";
async function* f(b) {
try {
if (b)
return console;
return;
} finally {
a = "PASS";
}
}
f().next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=10"
}
issue_5679_6: {
options = {
conditionals: true,
if_return: true,
}
input: {
var a = "PASS";
async function* f(b) {
try {
if (b)
return;
else
return console;
} finally {
a = "FAIL";
}
}
f().next();
console.log(a);
}
expect: {
var a = "PASS";
async function* f(b) {
try {
if (!b)
return console;
} finally {
a = "FAIL";
}
}
f().next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=10"
}
issue_5684: {
options = {
conditionals: true,
if_return: true,
}
input: {
(async function*() {
switch (42) {
default:
if (console.log("PASS"))
return;
return null;
case false:
}
})().next();
}
expect: {
(async function*() {
switch (42) {
default:
return console.log("PASS") ? void 0 : null;
case false:
}
})().next();
}
expect_stdout: "PASS"
node_version: ">=10"
}

View File

@@ -400,7 +400,7 @@ describe("comments", function() {
assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8"); assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8");
}); });
it("Should be able to filter commments with the 'some' option", function() { it("Should be able to filter comments with the 'some' option", function() {
var ast = UglifyJS.parse("// foo\n/*@preserve*/\n// bar\n/*@license*/\n//@license with the wrong comment type\n/*@cc_on something*/"); var ast = UglifyJS.parse("// foo\n/*@preserve*/\n// bar\n/*@license*/\n//@license with the wrong comment type\n/*@cc_on something*/");
assert.strictEqual(ast.print_to_string({comments: "some"}), "/*@preserve*/\n/*@license*/\n/*@cc_on something*/"); assert.strictEqual(ast.print_to_string({comments: "some"}), "/*@preserve*/\n/*@license*/\n/*@cc_on something*/");
}); });

View File

@@ -348,7 +348,7 @@ describe("Directives", function() {
'"use strict";doSomething("foo");' '"use strict";doSomething("foo");'
], ],
[ [
// Nothing gets optimised in the compressor because "use asm" is the first statement // Nothing gets optimized in the compressor because "use asm" is the first statement
'"use asm";"use\\x20strict";1+1;', '"use asm";"use\\x20strict";1+1;',
'"use asm";"use\\x20strict";1+1;' '"use asm";"use\\x20strict";1+1;'
], ],

View File

@@ -12,17 +12,16 @@ describe("let", function() {
s += '}'; s += '}';
var result = UglifyJS.minify(s, { var result = UglifyJS.minify(s, {
compress: false, compress: false,
}).code; });
if (result.error) throw result.error;
// Verify that select keywords and reserved keywords not produced // Verify that select keywords and reserved keywords not produced
[ [
"do", "do",
"let", "let",
"var", "var",
].forEach(function(name) { ].forEach(function(name) {
assert.strictEqual(result.indexOf("var " + name + "="), -1); assert.strictEqual(result.code.indexOf("var " + name + "="), -1);
}); });
// Verify that the variable names that appeared immediately before // Verify that the variable names that appeared immediately before
// and after the erroneously generated variable name still exist // and after the erroneously generated variable name still exist
// to show the test generated enough symbols. // to show the test generated enough symbols.
@@ -31,27 +30,29 @@ describe("let", function() {
"eet", "fet", "eet", "fet",
"rar", "oar", "rar", "oar",
].forEach(function(name) { ].forEach(function(name) {
assert.notStrictEqual(result.indexOf("var " + name + "="), -1); assert.notStrictEqual(result.code.indexOf("var " + name + "="), -1);
}); });
}); });
it("Should quote mangled properties that are reserved keywords", function() { it("Should quote mangled properties that are reserved keywords", function() {
var s = '"rrrrrnnnnniiiiiaaaaa";'; var s = '"rrrrrnnnnniiiiiaaaaa";';
for (var i = 0; i < 18000; i++) { for (var i = 0; i < 18000; i++) {
s += "v.b" + i + ";"; s += "v.b" + i + "=v;";
} }
var result = UglifyJS.minify(s, { var result = UglifyJS.minify(s, {
compress: false, compress: false,
ie: true, ie: true,
mangle: { mangle: {
properties: true, properties: {
} domprops: true,
}).code; },
},
});
if (result.error) throw result.error;
[ [
"in", "in",
"var", "var",
].forEach(function(name) { ].forEach(function(name) {
assert.notStrictEqual(result.indexOf(name), -1); assert.notStrictEqual(result.code.indexOf('v["' + name + '"]'), -1);
assert.notStrictEqual(result.indexOf('v["' + name + '"]'), -1);
}); });
}); });
it("Should parse `let` as name correctly", function() { it("Should parse `let` as name correctly", function() {

View File

@@ -110,10 +110,12 @@ describe("minify", function() {
var result = UglifyJS.minify(code, { var result = UglifyJS.minify(code, {
compress: false, compress: false,
mangle: { mangle: {
properties: true, properties: {
toplevel: true domprops: true,
}, },
nameCache: cache toplevel: true,
},
nameCache: cache,
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
original += code; original += code;
@@ -188,32 +190,30 @@ describe("minify", function() {
it("Shouldn't mangle quoted properties", function() { it("Shouldn't mangle quoted properties", function() {
var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};'; var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};';
var result = UglifyJS.minify(js, { var result = UglifyJS.minify(js, {
compress: { compress: true,
properties: false
},
mangle: { mangle: {
properties: { properties: {
keep_quoted: true domprops: true,
} keep_quoted: true,
},
}, },
output: { output: {
keep_quoted_props: true, keep_quoted_props: true,
quote_style: 3 quote_style: 3,
} },
}); });
assert.strictEqual(result.code, assert.strictEqual(result.code, 'a["foo"]="bar",a.a="red",x={"bar":10};');
'a["foo"]="bar",a.a="red",x={"bar":10};');
}); });
it("Should not mangle quoted property within dead code", function() { it("Should not mangle quoted property within dead code", function() {
var result = UglifyJS.minify('({ "keep": 1 }); g.keep = g.change;', { var result = UglifyJS.minify('({ "keep": 1 }); g.keep = g.change = 42;', {
mangle: { mangle: {
properties: { properties: {
keep_quoted: true keep_quoted: true,
} },
} },
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, "g.keep=g.g;"); assert.strictEqual(result.code, "g.keep=g.g=42;");
}); });
}); });

View File

@@ -245,7 +245,7 @@ describe("sourcemaps", function() {
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code + "\n", read("test/input/issue-3294/output.js")); assert.strictEqual(result.code + "\n", read("test/input/issue-3294/output.js"));
}); });
it("Should work in presence of unrecognised annotations", function() { it("Should work in presence of unrecognized annotations", function() {
var result = UglifyJS.minify(read("test/input/issue-3441/input.js"), { var result = UglifyJS.minify(read("test/input/issue-3441/input.js"), {
compress: false, compress: false,
mangle: false, mangle: false,

View File

@@ -1,6 +1,6 @@
var fs = require("fs"); var fs = require("fs");
new Function("exports", require("../tools/node").FILES.map(function(file) { new Function("domprops", "exports", require("../tools/node").FILES.map(function(file) {
if (/exports\.js$/.test(file)) file = require.resolve("./exports"); if (/exports\.js$/.test(file)) file = require.resolve("./exports");
return fs.readFileSync(file, "utf8"); return fs.readFileSync(file, "utf8");
}).join("\n\n"))(exports); }).join("\n\n"))(require("../tools/domprops.json"), exports);

View File

@@ -25,7 +25,7 @@ exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, top
} while (prev !== stdout); } while (prev !== stdout);
return stdout; return stdout;
} : semver.satisfies(process.version, "<0.12") ? run_code_vm : function(code, toplevel, timeout) { } : semver.satisfies(process.version, "<0.12") ? run_code_vm : function(code, toplevel, timeout) {
if ([ var stdout = ([
/\b(async[ \t]+function|Promise|setImmediate|setInterval|setTimeout)\b/, /\b(async[ \t]+function|Promise|setImmediate|setInterval|setTimeout)\b/,
/\basync([ \t]+|[ \t]*#)[^\s()[\]{}#:;,.&|!~=*%/+-]+(\s*\(|[ \t]*=>)/, /\basync([ \t]+|[ \t]*#)[^\s()[\]{}#:;,.&|!~=*%/+-]+(\s*\(|[ \t]*=>)/,
/\basync[ \t]*\*[ \t]*[^\s()[\]{}#:;,.&|!~=*%/+-]+\s*\(/, /\basync[ \t]*\*[ \t]*[^\s()[\]{}#:;,.&|!~=*%/+-]+\s*\(/,
@@ -33,11 +33,8 @@ exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, top
/\basync[ \t]*\([\s\S]*?\)[ \t]*=>/, /\basync[ \t]*\([\s\S]*?\)[ \t]*=>/,
].some(function(pattern) { ].some(function(pattern) {
return pattern.test(code); return pattern.test(code);
})) { }) ? run_code_exec : run_code_vm)(code, toplevel, timeout);
return run_code_exec(code, toplevel, timeout); return stdout.length > 1000 ? stdout.slice(0, 1000) + "…《" + stdout.length + "》" : stdout;
} else {
return run_code_vm(code, toplevel, timeout);
}
}; };
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) { exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false; if (typeof expected != typeof actual) return false;
@@ -286,6 +283,7 @@ function run_code_exec(code, toplevel, timeout) {
var result = spawnSync(process.argv[0], [ '--max-old-space-size=2048' ], { var result = spawnSync(process.argv[0], [ '--max-old-space-size=2048' ], {
encoding: "utf8", encoding: "utf8",
input: code, input: code,
maxBuffer: 1073741824,
stdio: "pipe", stdio: "pipe",
timeout: timeout || 5000, timeout: timeout || 5000,
}); });

View File

@@ -1142,7 +1142,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
} }
case STMT_FUNC_EXPR: case STMT_FUNC_EXPR:
// "In non-strict mode code, functions can only be declared at top level, inside a block, or ..." // "In non-strict mode code, functions can only be declared at top level, inside a block, or ..."
// (dont both with func decls in `if`; it's only a parser thing because you cant call them without a block) // (don't make func decls in `if`; it's only a parser thing because you can't call them without a block)
return "{" + createFunction(recurmax, NO_DEFUN, canThrow, stmtDepth) + "}"; return "{" + createFunction(recurmax, NO_DEFUN, canThrow, stmtDepth) + "}";
case STMT_TRY: case STMT_TRY:
// catch var could cause some problems // catch var could cause some problems

View File

@@ -15,13 +15,13 @@ exports.FILES = [
require.resolve("./exports.js"), require.resolve("./exports.js"),
]; ];
new Function("exports", function() { new Function("domprops", "exports", function() {
var code = exports.FILES.map(function(file) { var code = exports.FILES.map(function(file) {
return fs.readFileSync(file, "utf8"); return fs.readFileSync(file, "utf8");
}); });
code.push("exports.describe_ast = " + describe_ast.toString()); code.push("exports.describe_ast = " + describe_ast.toString());
return code.join("\n\n"); return code.join("\n\n");
}())(exports); }())(require("./domprops.json"), exports);
function to_comment(value) { function to_comment(value) {
if (typeof value != "string") value = JSON.stringify(value, function(key, value) { if (typeof value != "string") value = JSON.stringify(value, function(key, value) {