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
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

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
- `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
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.
- `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.
- `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
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
- `yields` (default: `true`) — apply optimizations to `yield` expressions
@@ -891,12 +891,15 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code;
### Mangle properties options
- `builtins` (default: `false`) — Use `true` to allow the mangling of builtin
DOM properties. Not recommended to override this setting.
- `builtins` (default: `false`) — Use `true` to allow the mangling of built-in
properties of JavaScript API. Not recommended to override this setting.
- `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.
- `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
arguments.

View File

@@ -238,17 +238,6 @@ if (specified["beautify"] && specified["output-opts"]) fatal("--beautify cannot
[ "compress", "mangle" ].forEach(function(name) {
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 (typeof options.output != "object") options.output = {};
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", {
$documentation: "Base class for all statements introducing a lexical scope",
$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",
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",

View File

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

View File

@@ -76,7 +76,7 @@ function OutputStream(options) {
wrap_iife : false,
}, 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
if (options.comments) {
var comments = options.comments;
@@ -997,7 +997,7 @@ function OutputStream(options) {
if (self.init instanceof AST_Definitions) {
self.init.print(output);
} else {
parenthesize_for_noin(self.init, output, true);
parenthesize_for_no_in(self.init, output, true);
}
output.print(";");
output.space();
@@ -1413,7 +1413,7 @@ function OutputStream(options) {
print_braced(this, output);
});
function print_definitinos(type) {
function print_definitions(type) {
return function(output) {
var self = this;
output.print(type);
@@ -1426,15 +1426,15 @@ function OutputStream(options) {
if (!(p instanceof AST_IterationStatement && p.init === self)) output.semicolon();
};
}
DEFPRINT(AST_Const, print_definitinos("const"));
DEFPRINT(AST_Let, print_definitinos("let"));
DEFPRINT(AST_Var, print_definitinos("var"));
DEFPRINT(AST_Const, print_definitions("const"));
DEFPRINT(AST_Let, print_definitions("let"));
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;
// need to take some precautions here:
// 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 (node instanceof AST_Binary && node.operator == "in") return parens = true;
if (node instanceof AST_Scope && !(is_arrow(node) && node.value)) return true;
@@ -1450,8 +1450,8 @@ function OutputStream(options) {
output.print("=");
output.space();
var p = output.parent(1);
var noin = p instanceof AST_For || p instanceof AST_ForEnumeration;
parenthesize_for_noin(self.value, output, noin);
var no_in = p instanceof AST_For || p instanceof AST_ForEnumeration;
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(";");
}
function parenthesised() {
function parenthesized() {
expect("(");
var exp = expression();
expect(")");
@@ -920,18 +920,18 @@ function parse($TEXT, options) {
next();
var body = in_loop(statement);
expect_token("keyword", "while");
var condition = parenthesised();
var condition = parenthesized();
semicolon(true);
return new AST_Do({
body : body,
condition : condition
condition : condition,
});
case "while":
next();
return new AST_While({
condition : parenthesised(),
body : in_loop(statement)
condition : parenthesized(),
body : in_loop(statement),
});
case "for":
@@ -959,15 +959,13 @@ function parse($TEXT, options) {
value = expression();
semicolon();
}
return new AST_Return({
value: value
});
return new AST_Return({ value: value });
case "switch":
next();
return new AST_Switch({
expression : parenthesised(),
body : in_loop(switch_body_)
expression : parenthesized(),
body : in_loop(switch_body_),
});
case "throw":
@@ -976,9 +974,7 @@ function parse($TEXT, options) {
croak("Illegal newline after 'throw'");
var value = expression();
semicolon();
return new AST_Throw({
value: value
});
return new AST_Throw({ value: value });
case "try":
next();
@@ -996,8 +992,8 @@ function parse($TEXT, options) {
}
next();
return new AST_With({
expression : parenthesised(),
body : statement()
expression : parenthesized(),
body : statement(),
});
}
}
@@ -1421,15 +1417,15 @@ function parse($TEXT, options) {
};
function if_() {
var cond = parenthesised(), body = statement(), belse = null;
var cond = parenthesized(), body = statement(), alt = null;
if (is("keyword", "else")) {
next();
belse = statement();
alt = statement();
}
return new AST_If({
condition : cond,
body : body,
alternative : belse
alternative : alt,
});
}
@@ -2171,9 +2167,9 @@ function parse($TEXT, options) {
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 (!noerror) croak("Name expected");
if (!no_error) croak("Name expected");
return null;
}
var sym = _make_symbol(type, S.token);
@@ -2409,20 +2405,20 @@ function parse($TEXT, options) {
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;
if (op == "in" && no_in) op = null;
var prec = op != null ? PRECEDENCE[op] : null;
if (prec != null && prec > min_prec) {
var precision = op != null ? PRECEDENCE[op] : null;
if (precision != null && precision > min_precision) {
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({
start : left.start,
left : left,
operator : op,
right : right,
end : right.end
}), min_prec, no_in);
end : right.end,
}), min_precision, no_in);
}
return left;
};

View File

@@ -124,12 +124,16 @@ function get_builtins() {
function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ClassProperty) {
if (node.start && node.start.quote) add(node.key);
if (node instanceof AST_ClassProperty
|| 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) {
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) {
addStrings(node.property, add);
}
@@ -156,12 +160,16 @@ function mangle_properties(ast, options) {
builtins: false,
cache: null,
debug: false,
domprops: false,
keep_quoted: false,
regex: null,
reserved: null,
}, true);
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) {
reserved.set(name, true);
});
@@ -180,7 +188,7 @@ function mangle_properties(ast, options) {
var regex = options.regex;
// 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.
var debug = options.debug !== false;
var debug_suffix;
@@ -191,9 +199,7 @@ function mangle_properties(ast, options) {
// step 1: find candidates to mangle
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_Binary) {
if (node.operator == "in") addStrings(node.left, add);
} else if (node.TYPE == "Call") {
if (node.TYPE == "Call") {
var exp = node.expression;
if (exp instanceof AST_Dot) switch (exp.property) {
case "defineProperty":
@@ -210,14 +216,18 @@ function mangle_properties(ast, options) {
addStrings(node.args[0], add);
break;
}
} else if (node instanceof AST_ClassProperty) {
if (typeof node.key == "string") add(node.key);
} else if (node instanceof AST_ClassProperty
|| 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) {
add(node.property);
} else if (node instanceof AST_ObjectProperty) {
if (typeof node.key == "string") add(node.key);
if (is_lhs(node, this.parent())) add(node.property);
} 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]);
break;
}
} else if (node instanceof AST_ClassProperty) {
if (typeof node.key == "string") node.key = mangle(node.key);
} else if (node instanceof AST_ClassProperty
|| 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) {
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) {
if (!options.keep_quoted) mangleStrings(node.property);
}
@@ -303,7 +317,7 @@ function mangle_properties(ast, options) {
function mangleStrings(node) {
if (node instanceof AST_Sequence) {
mangleStrings(node.expressions.tail_node());
mangleStrings(node.tail_node());
} else if (node instanceof AST_String) {
node.value = mangle(node.value);
} else if (node instanceof AST_Conditional) {

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

View File

@@ -126,6 +126,17 @@ function parse_test(file) {
croak(node);
}
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);
return true;
}
@@ -271,7 +282,9 @@ function test_case(test) {
expect = test.expect_exact;
}
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, {
annotations: true,
beautify: true,

View File

@@ -290,6 +290,45 @@ increment_decrement_2: {
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: {
options = {
assignments: true,

View File

@@ -3540,3 +3540,61 @@ issue_5634_3_side_effects: {
]
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"
}
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: {
options = {
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: {
options = {
booleans: true,
@@ -786,3 +830,37 @@ issue_5469: {
}
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,
passes: 2,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
@@ -921,6 +922,7 @@ single_use_3: {
single_use_4: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -1503,6 +1505,218 @@ keep_instanceof_3: {
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: {
options = {
inline: true,
@@ -2164,6 +2378,7 @@ issue_4829_2: {
mangle_properties: {
mangle = {
properties: {
domprops: true,
keep_quoted: true,
},
}
@@ -2202,11 +2417,11 @@ mangle_properties: {
expect_stdout: "PASS 42"
expect_warnings: [
"INFO: Preserving reserved property q",
"INFO: Preserving reserved property log",
"INFO: Mapping property #P to #t",
"INFO: Mapping property Q to s",
"INFO: Mapping property #p to #i",
"INFO: Mapping property r to e",
"INFO: Preserving reserved property log",
]
node_version: ">=14.6"
}
@@ -2312,6 +2527,7 @@ issue_4962_1: {
options = {
ie: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2343,6 +2559,7 @@ issue_4962_1_strict: {
options = {
ie: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2370,6 +2587,7 @@ issue_4962_1_strict_direct: {
options = {
ie: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2401,6 +2619,7 @@ issue_4962_2: {
options = {
ie: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2432,6 +2651,7 @@ issue_4962_2_strict: {
options = {
ie: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2460,6 +2680,7 @@ issue_4962_2_strict_direct: {
options = {
ie: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2494,6 +2715,7 @@ issue_4962_2_strict_direct_inline: {
ie: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2877,6 +3099,7 @@ issue_5053_4: {
issue_5082_1: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2909,6 +3132,7 @@ issue_5082_1: {
issue_5082_1_strict: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2942,6 +3166,7 @@ issue_5082_2: {
options = {
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -2975,6 +3200,7 @@ issue_5082_2_static: {
options = {
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@@ -3619,3 +3845,59 @@ issue_5662: {
expect_stdout: "PASS"
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: {
options = {
comparisons: true,
conditionals: true,
}
input: {
function f(a, p) {
@@ -305,6 +306,7 @@ issue_2857_4: {
issue_2857_5: {
options = {
comparisons: true,
conditionals: true,
}
input: {
function f(a, p) {
@@ -528,6 +530,7 @@ nullish_assign: {
nullish_chain: {
options = {
comparisons: true,
conditionals: true,
}
input: {
var a;

View File

@@ -12,7 +12,7 @@ concat_1: {
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";
}
expect: {

View File

@@ -3028,8 +3028,26 @@ issue_5673_2: {
expect: {
var a = "PASS";
console.log(function(b) {
return ((b = a) || (b = a)) && b;
return a || (b = a) && b;
}());
}
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"
}
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: {
options = {
evaluate: true,

View File

@@ -8719,3 +8719,24 @@ single_use_inline_collision: {
}
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 = {
conditionals: true,
if_return: true,
@@ -2442,3 +2442,41 @@ issue_5649: {
}
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: {
import foo, { bar } from "baz";
consoe.log(moo);
console.log(moo);
import * as moo from "moz";
}
expect: {
import o, { bar as m } from "baz";
consoe.log(r);
console.log(r);
import * as r from "moz";
}
}
@@ -157,12 +157,12 @@ rename_mangle: {
}
input: {
import foo, { bar } from "baz";
consoe.log(moo);
console.log(moo);
import * as moo from "moz";
}
expect: {
import o, { bar as m } from "baz";
consoe.log(r);
console.log(r);
import * as r from "moz";
}
}

View File

@@ -1,6 +1,7 @@
issue_1321_no_debug: {
mangle = {
properties: {
domprops: true,
keep_quoted: true,
},
}
@@ -23,6 +24,7 @@ issue_1321_debug: {
mangle = {
properties: {
debug: "",
domprops: true,
keep_quoted: true,
},
}
@@ -44,6 +46,7 @@ issue_1321_debug: {
issue_1321_with_quoted: {
mangle = {
properties: {
domprops: true,
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(…))
*
* For example, let the equation be:
@@ -12,37 +12,54 @@
* a >= (a = parseInt('100')) → 99 >= 100 → false
*/
tranformation_sort_order_equal: {
transformation_sort_order_equal: {
options = {
comparisons: true,
}
input: { (a = parseInt('100')) == a }
expect: { (a = parseInt('100')) == a }
input: {
console.log((a = parseInt("100")) == a);
}
expect: {
console.log((a = parseInt("100")) == a);
}
expect_stdout: "true"
}
tranformation_sort_order_unequal: {
transformation_sort_order_unequal: {
options = {
comparisons: true,
}
input: { (a = parseInt('100')) != a }
expect: { (a = parseInt('100')) != a }
input: {
console.log((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 = {
comparisons: true,
}
input: { (a = parseInt('100')) <= a }
expect: { (a = parseInt('100')) <= a }
input: {
console.log((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 = {
comparisons: true,
}
input: { (a = parseInt('100')) >= a }
expect: { (a = parseInt('100')) >= a }
}
input: {
console.log((a = parseInt("100")) >= a);
}
expect: {
console.log((a = parseInt("100")) >= a);
}
expect_stdout: "true"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -133,6 +133,7 @@ evaluate_string_length: {
mangle_properties_1: {
mangle = {
properties: {
domprops: true,
keep_quoted: false,
},
}
@@ -147,17 +148,18 @@ mangle_properties_1: {
a["a"] = "bar";
a.b = "red";
x = {o: 10};
a.r(x.o, a.a);
a['r']({b: "blue", a: "baz"});
a.run(x.o, a.a);
a['run']({b: "blue", a: "baz"});
}
}
mangle_properties_2: {
mangle = {
properties: {
domprops: true,
reserved: [
"value",
]
],
},
}
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: {
options = {
evaluate: true,
@@ -207,6 +227,7 @@ mangle_unquoted_properties: {
mangle = {
properties: {
builtins: true,
domprops: true,
keep_quoted: true,
},
}
@@ -308,6 +329,7 @@ mangle_debug_suffix_keep_quoted: {
properties: {
builtins: true,
debug: "XYZ",
domprops: true,
keep_quoted: true,
reserved: [],
},
@@ -962,14 +984,14 @@ issue_2256: {
}
input: {
({ "keep": 42 });
global.keep = global.change;
global.keep = global.change = "PASS";
console.log(keep);
}
expect: {
global.keep = global.l;
global.keep = global.l = "PASS";
console.log(keep);
}
expect_stdout: "undefined"
expect_stdout: "PASS"
}
lhs_prop_1: {
@@ -1645,3 +1667,163 @@ issue_5177: {
expect_stdout: "PASS"
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"
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"
}
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");
});
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*/");
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");'
],
[
// 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;'
],

View File

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

View File

@@ -110,10 +110,12 @@ describe("minify", function() {
var result = UglifyJS.minify(code, {
compress: false,
mangle: {
properties: true,
toplevel: true
properties: {
domprops: true,
},
toplevel: true,
},
nameCache: cache
nameCache: cache,
});
if (result.error) throw result.error;
original += code;
@@ -188,32 +190,30 @@ describe("minify", function() {
it("Shouldn't mangle quoted properties", function() {
var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};';
var result = UglifyJS.minify(js, {
compress: {
properties: false
},
compress: true,
mangle: {
properties: {
keep_quoted: true
}
domprops: true,
keep_quoted: true,
},
},
output: {
keep_quoted_props: true,
quote_style: 3
}
quote_style: 3,
},
});
assert.strictEqual(result.code,
'a["foo"]="bar",a.a="red",x={"bar":10};');
assert.strictEqual(result.code, 'a["foo"]="bar",a.a="red",x={"bar":10};');
});
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: {
properties: {
keep_quoted: true
}
}
keep_quoted: true,
},
},
});
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;
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"), {
compress: false,
mangle: false,

View File

@@ -1,6 +1,6 @@
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");
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);
return stdout;
} : 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/,
/\basync([ \t]+|[ \t]*#)[^\s()[\]{}#:;,.&|!~=*%/+-]+(\s*\(|[ \t]*=>)/,
/\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]*=>/,
].some(function(pattern) {
return pattern.test(code);
})) {
return run_code_exec(code, toplevel, timeout);
} else {
return run_code_vm(code, toplevel, timeout);
}
}) ? run_code_exec : run_code_vm)(code, toplevel, timeout);
return stdout.length > 1000 ? stdout.slice(0, 1000) + "…《" + stdout.length + "》" : stdout;
};
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
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' ], {
encoding: "utf8",
input: code,
maxBuffer: 1073741824,
stdio: "pipe",
timeout: timeout || 5000,
});

View File

@@ -1142,7 +1142,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
}
case STMT_FUNC_EXPR:
// "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) + "}";
case STMT_TRY:
// catch var could cause some problems

View File

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