Compare commits

...

24 Commits

Author SHA1 Message Date
Alex Lam S.L
860aa9531b v3.14.4 2021-12-01 02:34:20 +08:00
Alex Lam S.L
2547542873 workaround sporadic slowdown in GitHub Actions (#5201) 2021-11-28 08:45:29 +08:00
Alex Lam S.L
3f8f0e246e fix corner case in collapse_vars (#5200)
fixes #5199
2021-11-28 01:43:43 +08:00
Alex Lam S.L
12227ebbb0 workaround toString() quirks on global context (#5198)
closes #5197
2021-11-27 23:17:51 +08:00
Alex Lam S.L
1b4bd7082b fix corner case in hoist_vars (#5196)
fixes #5195
2021-11-25 04:49:38 +08:00
Alex Lam S.L
0b6c185818 enhance merge_vars (#5194)
closes #5182
2021-11-24 19:08:40 +08:00
Alex Lam S.L
bfd0ac7f4b fix corner case in ie (#5193)
fixes #5192
2021-11-24 17:29:26 +08:00
Alex Lam S.L
1a8f2ecc65 enhance collapse_vars (#5186)
closes #5182
2021-11-24 17:28:36 +08:00
Alex Lam S.L
dc9e1ff0b1 fix corner case in unused (#5190)
fixes #5189
2021-11-23 20:10:05 +08:00
Alex Lam S.L
ea10498902 fix corner case in hoist_vars (#5188)
fixes #5187
2021-11-23 18:00:47 +08:00
Alex Lam S.L
69636dad69 improve sandbox async detection (#5185)
closes #5184
2021-11-23 02:29:54 +08:00
Alex Lam S.L
3ee1b0d00d enhance hoist_props & reduce_vars (#5183)
closes #5182
2021-11-23 00:31:10 +08:00
Alex Lam S.L
1e3ca4c6f7 enhance side_effects & unused (#5181) 2021-11-22 11:14:28 +08:00
Alex Lam S.L
839f4361f4 minor clean-up (#5180) 2021-11-19 16:34:11 +08:00
Alex Lam S.L
ae5c3ee8a1 fix corner cases in properties (#5178)
fixes #5177
2021-11-17 04:21:44 +08:00
Alex Lam S.L
77f7ae5ba2 fix corner case in join_vars (#5176)
fixes #5175
2021-11-15 21:35:54 +08:00
Alex Lam S.L
2d0f8bcff5 fix corner case in inline (#5174)
fixes #5173
2021-11-15 07:14:08 +08:00
Alex Lam S.L
f97e107c09 enhance reduce_vars (#5171) 2021-11-13 22:18:56 +08:00
Alex Lam S.L
e9932e1314 fix corner case in collapse_vars (#5169)
fixes #5168
2021-11-13 22:18:18 +08:00
Alex Lam S.L
eaa84d32df include Node.js v16 in CI tests (#5170)
- fix compatibility issues on v0.10 & v0.12
2021-11-13 18:34:18 +08:00
Alex Lam S.L
6e4aa0326f enhance reduce_vars (#5164)
- fix corner case in `join_vars`
2021-11-04 18:36:39 +08:00
Alex Lam S.L
f9a4b36dd1 fix corner cases in reduce_vars & rests (#5166)
fixes #5165
2021-11-03 21:29:01 +08:00
Alex Lam S.L
3acb5a329e enhance join_vars (#5162) 2021-11-02 11:33:24 +08:00
Alex Lam S.L
6d94242318 enhance reduce_vars (#5163) 2021-11-02 11:32:26 +08:00
28 changed files with 1273 additions and 184 deletions

View File

@@ -7,7 +7,7 @@ jobs:
test:
strategy:
matrix:
node: [ '0.10', '0.12', '4', '6', '8', '10', '12', '14', latest ]
node: [ '0.10', '0.12', '4', '6', '8', '10', '12', '14', '16', latest ]
os: [ ubuntu-latest, windows-latest ]
script: [ compress, mocha, release/benchmark, release/jetstream ]
name: ${{ matrix.node }} ${{ matrix.os }} ${{ matrix.script }}

View File

@@ -1999,7 +1999,7 @@ TreeWalker.prototype = {
},
find_parent: function(type) {
var stack = this.stack;
for (var i = stack.length; --i >= 0;) {
for (var i = stack.length - 1; --i >= 0;) {
var x = stack[i];
if (x instanceof type) return x;
}

View File

@@ -203,6 +203,7 @@ merge(Compressor.prototype, {
if (this.option("expression")) {
node.process_expression(true);
}
var merge_vars = this.options.merge_vars;
var passes = +this.options.passes || 1;
var min_count = 1 / 0;
var stopping = false;
@@ -211,6 +212,7 @@ merge(Compressor.prototype, {
node.figure_out_scope(mangle);
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this);
this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
node = node.transform(this);
if (passes > 1) {
var count = 0;
@@ -259,8 +261,8 @@ merge(Compressor.prototype, {
descend(node, this);
var opt = node.optimize(this);
if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
opt.merge_variables(this);
opt.drop_unused(this);
if (opt.merge_variables(this)) opt.drop_unused(this);
descend(opt, this);
}
if (opt === node) opt._squeezed = true;
@@ -389,7 +391,18 @@ merge(Compressor.prototype, {
}
var lhs = is_lhs(node, parent);
if (lhs) return lhs;
if (level == 0 && value && value.is_constant()) return;
if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
if (parent instanceof AST_Assign) switch (parent.operator) {
case "=":
return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
case "&&=":
case "||=":
case "??=":
return is_modified(compressor, tw, parent, parent, level + 1);
default:
return;
}
if (parent instanceof AST_Binary) {
if (!lazy_op[parent.operator]) return;
return is_modified(compressor, tw, parent, parent, level + 1);
@@ -445,7 +458,7 @@ merge(Compressor.prototype, {
}
function can_drop_symbol(ref, compressor, keep_lambda) {
var def = ref.definition();
var def = ref.redef || ref.definition();
if (ref.in_arg && is_funarg(def)) return false;
return all(def.orig, function(sym) {
if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) {
@@ -602,12 +615,14 @@ merge(Compressor.prototype, {
if (def.single_use == "m") return false;
var safe = tw.safe_ids[def.id];
if (safe) {
if (!HOP(tw.safe_ids, def.id)) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids;
var in_order = HOP(tw.safe_ids, def.id);
if (!in_order) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids;
if (def.fixed == null) {
if (is_arguments(def)) return false;
if (def.global && def.name == "arguments") return false;
tw.loop_ids[def.id] = null;
def.fixed = make_node(AST_Undefined, def.orig[0]);
if (in_order) delete def.safe_ids;
return true;
}
return !safe.assign || safe.assign === tw.safe_ids;
@@ -625,7 +640,7 @@ merge(Compressor.prototype, {
if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolLet);
});
if (def.fixed === false) return false;
if (def.fixed === false || def.fixed === 0) return false;
var safe = tw.safe_ids[def.id];
if (def.safe_ids) {
def.safe_ids[def.id] = false;
@@ -690,6 +705,7 @@ merge(Compressor.prototype, {
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true;
if (d.fixed) d.fixed.escaped = d.escaped;
return;
} else if (value_in_use(node, parent)) {
mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
@@ -707,6 +723,7 @@ merge(Compressor.prototype, {
if (parent instanceof AST_SimpleStatement) return;
if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
d.direct_access = true;
if (d.fixed) d.fixed.direct_access = true;
}
function mark_assignment_to_arguments(node) {
@@ -759,11 +776,13 @@ merge(Compressor.prototype, {
node.walk(scanner);
});
if (node.rest) {
var fixed_node;
if (save) fixed = compressor.option("rests") && function() {
var value = save();
return value instanceof AST_Array ? make_node(AST_Array, node, {
elements: value.elements.slice(node.elements.length),
}) : node;
if (!(value instanceof AST_Array)) return node;
if (!fixed_node) fixed_node = make_node(AST_Array, node);
fixed_node.elements = value.elements.slice(node.elements.length);
return fixed_node;
};
node.rest.walk(scanner);
}
@@ -843,11 +862,12 @@ merge(Compressor.prototype, {
return arg || make_node(AST_Undefined, iife);
}, visit);
});
var rest = fn.rest;
var rest = fn.rest, fixed_node;
if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() {
return fn.rest === rest ? make_node(AST_Array, fn, {
elements: iife.args.slice(fn.argnames.length),
}) : rest;
if (fn.rest !== rest) return rest;
if (!fixed_node) fixed_node = make_node(AST_Array, fn);
fixed_node.elements = iife.args.slice(fn.argnames.length);
return fixed_node;
}, visit);
walk_lambda(fn, tw);
var safe_ids = tw.safe_ids;
@@ -899,30 +919,22 @@ merge(Compressor.prototype, {
case "&&=":
case "||=":
case "??=":
left.walk(tw);
push(tw);
if (scan) {
right.walk(tw);
walk_assign();
} else {
mark_assignment_to_arguments(left);
right.walk(tw);
}
pop(tw);
return true;
var lazy = true;
default:
if (!scan) {
mark_assignment_to_arguments(left);
return;
return walk_lazy();
}
ld.assignments++;
var fixed = ld.fixed;
if (is_modified(compressor, tw, node, node, 0)) {
ld.fixed = false;
return;
return walk_lazy();
}
var safe = safe_to_read(tw, ld);
if (lazy) push(tw);
right.walk(tw);
if (lazy) pop(tw);
if (safe && !left.in_arg && safe_to_assign(tw, ld)) {
push_ref(ld, left);
mark(tw, ld);
@@ -978,7 +990,13 @@ merge(Compressor.prototype, {
}
var d = sym.definition();
d.assignments++;
if (fixed && !modified && !sym.in_arg && safe_to_assign(tw, d)) {
if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) {
walk();
d.fixed = false;
} else if (modified) {
walk();
d.fixed = 0;
} else {
push_ref(d, sym);
mark(tw, d);
if (left instanceof AST_Destructured
@@ -986,15 +1004,21 @@ merge(Compressor.prototype, {
d.single_use = false;
}
tw.loop_ids[d.id] = tw.in_loop;
mark_escaped(tw, d, sym.scope, node, right, 0, 1);
sym.fixed = d.fixed = fixed;
sym.fixed.assigns = [ node ];
} else {
walk();
d.fixed = false;
mark_escaped(tw, d, sym.scope, node, right, 0, 1);
}
});
}
function walk_lazy() {
if (!lazy) return;
left.walk(tw);
push(tw);
right.walk(tw);
pop(tw);
return true;
}
});
def(AST_Binary, function(tw) {
if (!lazy_op[this.operator]) return;
@@ -1261,7 +1285,7 @@ merge(Compressor.prototype, {
if (!safe) return;
safe.assign = true;
});
if (d.fixed === false) {
if (d.fixed === false || d.fixed === 0) {
var redef = d.redefined();
if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false;
} else if (d.fixed === undefined || !safe_to_read(tw, d)) {
@@ -1286,7 +1310,7 @@ merge(Compressor.prototype, {
if (d.single_use) {
d.single_use = "m";
} else {
d.fixed = false;
d.fixed = 0;
}
}
if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true;
@@ -1471,14 +1495,19 @@ merge(Compressor.prototype, {
AST_Symbol.DEFMETHOD("fixed_value", function() {
var fixed = this.definition().fixed;
if (fixed) {
if (this.fixed) fixed = this.fixed;
return fixed instanceof AST_Node ? fixed : fixed();
}
fixed = fixed === 0 && this.fixed;
if (!fixed) return fixed;
if (this.fixed) fixed = this.fixed;
return fixed instanceof AST_Node ? fixed : fixed();
var value = fixed instanceof AST_Node ? fixed : fixed();
return value.is_constant() && value;
});
AST_SymbolRef.DEFMETHOD("is_immutable", function() {
var def = this.redef || this.definition();
return def.orig.length == 1 && def.orig[0] instanceof AST_SymbolLambda;
return (this.in_arg || def.orig.length == 1) && def.orig[0] instanceof AST_SymbolLambda;
});
AST_Node.DEFMETHOD("convert_symbol", noop);
@@ -2879,8 +2908,9 @@ merge(Compressor.prototype, {
if (!value) {
value = node;
var def = node.definition();
var escaped = node.fixed && node.fixed.escaped || def.escaped;
if (!def.undeclared
&& (def.assignments || !def.escaped || def.escaped.cross_scope)
&& (def.assignments || !escaped || escaped.cross_scope)
&& (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) {
well_defined = false;
}
@@ -2888,8 +2918,33 @@ merge(Compressor.prototype, {
} else if (node instanceof AST_ObjectIdentity) {
value = node;
}
if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
if (find_arguments && node instanceof AST_Sub) {
if (value) {
lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
} else if (node instanceof AST_Lambda) {
for (var level = 0, parent, child = node; parent = tw.parent(level++); child = parent) {
if (parent instanceof AST_Assign) {
if (parent.left === child) break;
if (parent.operator == "=") continue;
if (lazy_op[parent.operator.slice(0, -1)]) continue;
break;
}
if (parent instanceof AST_Binary) {
if (lazy_op[parent.operator]) continue;
break;
}
if (parent instanceof AST_Call) return;
if (parent instanceof AST_Scope) return;
if (parent instanceof AST_Sequence) {
if (parent.tail_node() === child) continue;
break;
}
if (parent instanceof AST_Template) {
if (parent.tag) return;
break;
}
}
return true;
} else if (find_arguments && node instanceof AST_Sub) {
scope.each_argname(function(argname) {
if (!compressor.option("reduce_vars") || argname.definition().assignments) {
if (!argname.definition().fixed) well_defined = false;
@@ -2996,7 +3051,10 @@ merge(Compressor.prototype, {
return false;
}
var def = lhs.definition();
return def.references.length - def.replaced == referenced;
if (def.references.length - def.replaced == referenced) return true;
return def.fixed && lhs.fixed && def.references.filter(function(ref) {
return ref.fixed === lhs.fixed;
}).length == referenced;
}
function symbol_in_lvalues(sym, parent) {
@@ -3346,10 +3404,10 @@ merge(Compressor.prototype, {
}
}
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
CHANGED = statements.length != len;
}
function sequencesize(statements, compressor) {
@@ -3475,15 +3533,26 @@ merge(Compressor.prototype, {
var exprs = extract_exprs(body);
if (!exprs) return;
var trimmed = false;
for (var i = exprs.length - 1; --i >= 0;) {
for (var i = exprs.length - (keep || 0); --i >= 0;) {
var expr = exprs[i];
if (!(expr instanceof AST_Assign)) continue;
if (expr.operator != "=") continue;
if (!(expr.left instanceof AST_SymbolRef)) continue;
var tail = exprs.slice(i + 1);
if (!can_trim(expr)) continue;
var tail;
if (expr.left instanceof AST_SymbolRef) {
tail = exprs.slice(i + 1);
} else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) {
tail = exprs.slice(i + 1);
var flattened = expr.clone();
expr = expr.left.expression;
flattened.left = flattened.left.clone();
flattened.left.expression = expr.left.clone();
tail.unshift(flattened);
} else {
continue;
}
if (tail.length == 0) continue;
if (!trim_assigns(expr.left, expr.right, tail)) continue;
trimmed = true;
exprs = exprs.slice(0, i + 1).concat(tail);
exprs = exprs.slice(0, i).concat(expr, tail);
}
if (defn instanceof AST_Definitions) {
keep = keep || 0;
@@ -3497,6 +3566,10 @@ merge(Compressor.prototype, {
if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
}
return trimmed && exprs;
function can_trim(node) {
return node instanceof AST_Assign && node.operator == "=";
}
}
function merge_assigns(prev, defn) {
@@ -3553,22 +3626,35 @@ merge(Compressor.prototype, {
}
function trim_assigns(name, value, exprs) {
var names = Object.create(null);
names[name.name] = true;
while (value instanceof AST_Assign && value.operator == "=") {
if (value.left instanceof AST_SymbolRef) names[value.left.name] = true;
value = value.right;
}
if (!(value instanceof AST_Object)) return;
var trimmed = false;
do {
var node = exprs[0];
if (!(node instanceof AST_Assign)) break;
if (node.operator != "=") break;
if (!(node.left instanceof AST_PropAccess)) break;
if (!try_join(exprs[0])) break;
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed;
function try_join(node) {
if (!(node instanceof AST_Assign)) return;
if (node.operator != "=") return;
if (!(node.left instanceof AST_PropAccess)) return;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break;
if (name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break;
if (!(sym instanceof AST_SymbolRef)) return;
if (!(sym.name in names)) return;
if (!node.right.is_constant_expression(scope)) return;
var prop = node.left.property;
if (prop instanceof AST_Node) {
if (try_join(prop)) prop = node.left.property = prop.right.clone();
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
if (prop instanceof AST_Node) return;
prop = "" + prop;
var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
var key = node.key;
@@ -3580,15 +3666,13 @@ merge(Compressor.prototype, {
}
return key !== "__proto__";
};
if (!all(value.properties, diff)) break;
if (!all(value.properties, diff)) return;
value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
value: node.right,
}));
exprs.shift();
trimmed = true;
} while (exprs.length);
return trimmed;
return true;
}
}
function join_consecutive_vars(statements) {
@@ -5934,6 +6018,7 @@ merge(Compressor.prototype, {
});
tw.directives = Object.create(compressor.directives);
self.walk(tw);
var changed = false;
var merged = Object.create(null);
while (first.length && last.length) {
var head = first.pop();
@@ -5977,10 +6062,12 @@ merge(Compressor.prototype, {
def.references = refs.concat(def.references);
def.fixed = tail.definition.fixed && def.fixed;
merged[id] = def;
changed = true;
break;
} while (last.length);
if (skipped.length) last = last.concat(skipped);
}
return changed;
function push() {
segment = Object.create(segment);
@@ -7157,7 +7244,10 @@ merge(Compressor.prototype, {
case 1:
if (!drop) break;
var sym = elements[0];
if (!(sym instanceof AST_Symbol)) break;
if (sym.has_side_effects(compressor)) break;
if (value.has_side_effects(compressor) && sym.match_symbol(function(node) {
return node instanceof AST_PropAccess;
})) break;
value = make_node(AST_Sub, node, {
expression: value,
property: make_node(AST_Number, node, { value: 0 }),
@@ -7283,7 +7373,10 @@ merge(Compressor.prototype, {
if (!drop) break;
var prop = properties[0];
if (prop.key instanceof AST_Node) break;
if (!(prop.value instanceof AST_Symbol)) break;
if (prop.value.has_side_effects(compressor)) break;
if (value.has_side_effects(compressor) && prop.value.match_symbol(function(node) {
return node instanceof AST_PropAccess;
})) break;
value = make_node(AST_Sub, node, {
expression: value,
property: make_node_from_constant(prop.key, prop),
@@ -7627,6 +7720,7 @@ merge(Compressor.prototype, {
right: prop.value,
}));
});
defs.value = node.right;
defs_by_id[node.left.definition().id] = defs;
self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
definitions: decls,
@@ -7639,12 +7733,16 @@ merge(Compressor.prototype, {
descend(node, this);
var defs = new Dictionary();
var var_defs = [];
var decl = node.clone();
decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null;
var_defs.push(decl);
node.value.properties.forEach(function(prop) {
var_defs.push(make_node(AST_VarDef, node, {
name: make_sym(node.name.CTOR, node.name, prop.key),
value: prop.value,
}));
});
defs.value = node.value;
defs_by_id[node.name.definition().id] = defs;
return List.splice(var_defs);
}
@@ -7660,6 +7758,7 @@ merge(Compressor.prototype, {
if (!(node.expression instanceof AST_SymbolRef)) return;
var defs = defs_by_id[node.expression.definition().id];
if (!defs) return;
if (node.expression.fixed_value() !== defs.value) return;
var def = defs.get(node.get_property());
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
@@ -7670,7 +7769,9 @@ merge(Compressor.prototype, {
return sym;
}
if (node instanceof AST_SymbolRef) {
if (!(node.definition().id in defs_by_id)) return;
var defs = defs_by_id[node.definition().id];
if (!defs) return;
if (node.fixed_value() !== defs.value) return;
return make_node(AST_Object, node, { properties: [] });
}
}));
@@ -7679,18 +7780,16 @@ merge(Compressor.prototype, {
if (!(sym instanceof AST_Symbol)) return;
var def = sym.definition();
if (def.assignments != count) return;
if (def.direct_access) return;
if (def.escaped.depth == 1) return;
if (def.references.length - def.replaced == count) return;
if (def.single_use) return;
if (top_retain(def)) return;
if (sym.fixed_value() !== right) return;
var fixed = sym.fixed || def.fixed;
if (fixed.direct_access) return;
if (fixed.escaped && fixed.escaped.depth == 1) return;
return right instanceof AST_Object
&& right.properties.length > 0
&& all(right.properties, can_hoist_property)
&& all(def.references, function(ref) {
return ref.fixed_value() === right;
})
&& can_drop_symbol(sym, compressor);
}
});
@@ -7758,14 +7857,10 @@ merge(Compressor.prototype, {
if (compressor.has_directive("use strict") && expr.is_constant()) return this;
}
if (left.has_side_effects(compressor)) return this;
var right = this.right;
if (!lazy_op[this.operator.slice(0, -1)]) {
this.write_only = true;
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return right.drop_side_effect_free(compressor);
}
}
return this;
if (lazy_op[this.operator.slice(0, -1)]) return this;
this.write_only = true;
if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this;
return this.right.drop_side_effect_free(compressor);
});
def(AST_Await, function(compressor) {
if (!compressor.option("awaits")) return this;
@@ -9016,16 +9111,20 @@ merge(Compressor.prototype, {
right: value,
});
a.push(assign);
name.fixed = function() {
var fixed = function() {
return assign.right;
};
name.fixed.assigns = [ assign ];
fixed.assigns = [ assign ];
fixed.direct_access = def.direct_access;
fixed.escaped = def.escaped;
name.fixed = fixed;
def.references.forEach(function(ref) {
var assigns = ref.fixed && ref.fixed.assigns;
if (assigns && assigns[0] === defn) assigns[0] = assign;
});
def.references.push(name);
}
def.assignments++;
def.eliminated++;
def.single_use = false;
return a;
@@ -9204,6 +9303,13 @@ merge(Compressor.prototype, {
}) : arg);
}
function avoid_await_yield(parent_scope) {
var avoid = [];
if (is_async(parent_scope)) avoid.push("await");
if (is_generator(parent_scope)) avoid.push("yield");
return avoid.length && makePredicate(avoid);
}
OPT(AST_Call, function(self, compressor) {
var exp = self.expression;
var terminated = trim_optional_chain(self, compressor);
@@ -9531,7 +9637,10 @@ merge(Compressor.prototype, {
var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
if (can_inline && stat instanceof AST_Return) {
var value = stat.value;
if (exp === fn && !fn.name && (!value || value.is_constant_expression()) && safe_from_await_yield(fn)) {
if (exp === fn
&& !fn.name
&& (!value || value.is_constant_expression())
&& safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
return make_sequence(self, convert_args(value)).optimize(compressor);
}
}
@@ -9550,7 +9659,7 @@ merge(Compressor.prototype, {
if (can_substitute_directly()) {
var args = self.args.slice();
var refs = [];
args.push(value.clone(true).transform(new TreeTransformer(function(node) {
var retValue = value.clone(true).transform(new TreeTransformer(function(node) {
if (node instanceof AST_SymbolRef) {
var def = node.definition();
if (fn.variables.get(node.name) !== def) {
@@ -9564,12 +9673,20 @@ merge(Compressor.prototype, {
var parent = this.parent();
return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
}
})));
}));
var save_inlined = fn.inlined;
if (exp !== fn) fn.inlined = true;
var node = make_sequence(self, args.filter(function(arg) {
return arg;
})).optimize(compressor);
var exprs = [];
args.forEach(function(arg) {
if (!arg) return;
arg = arg.clone(true);
arg.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) refs.push(node);
}));
exprs.push(arg);
}, []);
exprs.push(retValue);
var node = make_sequence(self, exprs).optimize(compressor);
fn.inlined = save_inlined;
node = maintain_this_binding(compressor, parent, current, node);
if (replacing || best_of_expression(node, self) === node) {
@@ -9603,7 +9720,7 @@ merge(Compressor.prototype, {
&& all(fn.body, is_empty)
&& (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
&& !(is_arrow(fn) && fn.value)
&& safe_from_await_yield(fn)) {
&& safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
return make_sequence(self, convert_args()).optimize(compressor);
}
}
@@ -9711,16 +9828,8 @@ merge(Compressor.prototype, {
return args;
}
function avoid_await_yield() {
var avoid = [];
var parent_scope = scope || compressor.find_parent(AST_Scope);
if (is_async(parent_scope)) avoid.push("await");
if (is_generator(parent_scope)) avoid.push("yield");
return avoid.length && makePredicate(avoid);
}
function safe_from_await_yield(node) {
var avoid = avoid_await_yield();
function safe_from_await_yield(node, scope) {
var avoid = avoid_await_yield(scope);
if (!avoid) return true;
var safe = true;
var tw = new TreeWalker(function(node) {
@@ -9796,8 +9905,9 @@ merge(Compressor.prototype, {
if (!fn.variables.all(function(def) {
return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
})) return;
var scope = compressor.find_parent(AST_Scope);
var abort = false;
var avoid = avoid_await_yield();
var avoid = avoid_await_yield(scope);
var begin;
var in_order = [];
var side_effects = false;
@@ -9841,8 +9951,7 @@ merge(Compressor.prototype, {
while (end-- > begin && fn.argnames[end] === in_order.pop());
end++;
}
var scope = side_effects && !in_order && compressor.find_parent(AST_Scope);
return end <= begin || all(self.args.slice(begin, end), scope ? function(funarg) {
return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) {
return funarg.is_constant_expression(scope);
} : function(funarg) {
return !funarg.has_side_effects(compressor);
@@ -9912,7 +10021,7 @@ merge(Compressor.prototype, {
} while (!(scope instanceof AST_Scope));
insert = scope.body.indexOf(child) + 1;
if (!insert) return false;
if (!safe_from_await_yield(fn)) return false;
if (!safe_from_await_yield(fn, scope)) return false;
var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
if (scope instanceof AST_Toplevel) {
if (compressor.toplevel.vars) {
@@ -12314,19 +12423,34 @@ merge(Compressor.prototype, {
for (var i = props.length; --i >= 0;) {
var prop = props[i];
if (prop.key !== key) continue;
if (!all(props, can_hoist_property)) break;
if (!safe_to_flatten(prop.value, compressor)) break;
props = props.map(function(prop) {
return prop.value;
});
if (prop instanceof AST_ObjectMethod
&& prop.value instanceof AST_Function
&& !(compressor.parent() instanceof AST_Call)) {
if (prop.value.uses_arguments) break;
props[i] = make_node(AST_Arrow, prop.value, prop.value);
if (!all(props, can_hoist_property)) return;
if (!safe_to_flatten(prop.value, compressor)) return;
var scope, values = [];
for (var j = 0; j < props.length; j++) {
var value = props[j].value;
if (props[j] instanceof AST_ObjectMethod) {
var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
if (arrow) {
if (!scope) scope = compressor.find_parent(AST_Scope);
var avoid = avoid_await_yield(scope);
value.each_argname(function(argname) {
if (avoid[argname.name]) arrow = false;
});
}
var ctor;
if (arrow) {
ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow;
} else if (i === j && !(compressor.parent() instanceof AST_Call)) {
return;
} else {
ctor = value.CTOR;
}
value = make_node(ctor, value, value);
}
values.push(value);
}
return make_node(AST_Sub, this, {
expression: make_node(AST_Array, expr, { elements: props }),
expression: make_node(AST_Array, expr, { elements: values }),
property: make_node(AST_Number, this, { value: i }),
});
}

View File

@@ -1152,8 +1152,9 @@ function OutputStream(options) {
});
}
function print_arrow(self, output) {
if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg && !self.rest) {
self.argnames[0].print(output);
var argname = self.argnames.length == 1 && !self.rest && self.argnames[0];
if (argname instanceof AST_SymbolFunarg && argname.name != "yield") {
argname.print(output);
} else {
print_funargs(self, output);
}

View File

@@ -1316,6 +1316,11 @@ function parse($TEXT, options) {
}
if (node instanceof AST_SymbolFunarg) return node;
if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
if (node instanceof AST_Yield) return new AST_SymbolFunarg({
start: node.start,
name: "yield",
end: node.end,
});
token_error(node.start, "Invalid arrow parameter");
}

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

View File

@@ -550,6 +550,24 @@ logical_side_effects: {
node_version: ">=15"
}
evaluate_lazy_assignment: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 42;
console.log(a &&= "PASS");
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4815_1: {
options = {
evaluate: true,

View File

@@ -453,7 +453,7 @@ object_function: {
}).f();
}
expect: {
(async function() {
(async () => {
console.log("PASS");
})();
}
@@ -2212,3 +2212,29 @@ issue_5159_2: {
]
node_version: ">=8"
}
issue_5177: {
options = {
properties: true,
}
input: {
(async function() {
return {
p(await) {},
}.p;
})().then(function(a) {
console.log(typeof a);
});
}
expect: {
(async function() {
return {
p(await) {},
}.p;
})().then(function(a) {
console.log(typeof a);
});
}
expect_stdout: "function"
node_version: ">=8"
}

View File

@@ -9253,13 +9253,13 @@ issue_4920_2: {
console.log(b);
}
expect: {
var o = {
var o;
var a = "PASS", b;
({
get PASS() {
a = "FAIL";
},
};
var a = "PASS", b;
o[b = a];
})[b = a];
console.log(b);
}
expect_stdout: "PASS"
@@ -9283,16 +9283,47 @@ issue_4920_3: {
}
expect: {
var log = console.log;
var o = {
var o;
var a = "PASS", b;
({
get PASS() {
a = "FAIL";
},
})[b = a];
log(b);
}
expect_stdout: "PASS"
}
issue_4920_4: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var log = console.log;
var o = {
get [(a = "FAIL 1", "PASS")]() {
a = "FAIL 2";
},
};
var a = "PASS", b;
o[b = a];
log(b);
}
expect: {
var log = console.log;
var o = {
get [(a = "FAIL 1", "PASS")]() {
a = "FAIL 2";
},
};
var a = "PASS", b;
o[b = a];
log(b);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4935: {
@@ -9482,3 +9513,56 @@ issue_5112_2: {
}
expect_stdout: "PASS"
}
issue_5182: {
options = {
arrows: true,
collapse_vars: true,
evaluate: true,
hoist_props: true,
inline: true,
merge_vars: true,
passes: 4,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var con = console;
global.log = con.log;
var jump = function(x) {
console.log("JUMP:", x * 10);
return x + x;
};
var jump2 = jump;
var run = function(x) {
console.log("RUN:", x * -10);
return x * x;
};
var run2 = run;
var bar = (x, y) => {
console.log("BAR:", x + y);
return x - y;
};
var bar2 = bar;
var obj = {
foo: bar2,
go: run2,
not_used: jump2,
};
console.log(obj.foo(1, 2), global.log("PASS"));
}
expect: {
var con = console;
global.log = con.log,
console.log((console.log("BAR:", 3), -1), global.log("PASS"));
}
expect_stdout: [
"BAR: 3",
"PASS",
"-1 undefined",
]
node_version: ">=4"
}

View File

@@ -1938,3 +1938,22 @@ issue_5138_2: {
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5192: {
options = {
dead_code: true,
ie: true,
}
input: {
(function a(a, [] = a = "PASS") {
console.log(a);
})("FAIL");
}
expect: {
(function a(a, [] = a = "PASS") {
console.log(a);
})("FAIL");
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -1581,6 +1581,75 @@ hoist_vars: {
node_version: ">=6"
}
singleton_1: {
options = {
pure_getters: true,
side_effects: true,
unused: true,
}
input: {
var [ a ] = "P", b, o = {};
[ { 1: o.p } ] = [ "FAIL" ];
({ foo: [ o.q ] } = { foo: "S" });
[ b = "S" ] = [];
console.log(a + o.p + o.q + b);
}
expect: {
var b, a = "P"[0], o = {};
o.p = [ "FAIL"["1"] ][0];
o.q = { foo: "S"[0] }["foo"];
[ b = "S" ] = [];
console.log(a + o.p + o.q + b);
}
expect_stdout: "PASS"
node_version: ">=6"
}
singleton_2: {
options = {
evaluate: true,
passes: 2,
pure_getters: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
var [ a ] = "P", b, o = {};
[ { 1: o.p } ] = [ "FAIL" ];
({ foo: [ o.q ] } = { foo: "S" });
[ b = "S" ] = [];
console.log(a + o.p + o.q + b);
}
expect: {
var b, a = "P", o = {};
o.p = "A";
o.q = "S";
[ b = "S" ] = [];
console.log(a + o.p + o.q + b);
}
expect_stdout: "PASS"
node_version: ">=6"
}
singleton_side_effects: {
options = {
side_effects: true,
unused: true,
}
input: {
[ 42[console.log("foo")] ] = [ console.log("bar") ];
}
expect: {
[ 42[console.log("foo")] ] = [ console.log("bar") ];
}
expect_stdout: [
"bar",
"foo",
]
node_version: ">=6"
}
issue_4280: {
options = {
evaluate: true,
@@ -3225,3 +3294,75 @@ issue_5153_object_var: {
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5168: {
options = {
collapse_vars: true,
}
input: {
(function a({
[console.log(typeof function() {
++a;
return a;
}())]: b,
}) {
var a;
})({});
}
expect: {
(function a({
[console.log(typeof function() {
++a;
return a;
}())]: b,
}) {
var a;
})({});
}
expect_stdout: "function"
node_version: ">=6"
}
issue_5189_1: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 42;
[ a.p ] = a = "PASS";
console.log(a);
}
expect: {
var a;
[ a.p ] = a = "PASS";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5189_2: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 42;
({ p: a.q } = a = "PASS");
console.log(a);
}
expect: {
var a;
({ p: a.q } = a = "PASS");
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -2916,7 +2916,7 @@ issue_4133: {
console.log(a);
}
expect: {
var b = 1;
var a = 1;
console.log(0);
}
expect_stdout: "0"
@@ -3062,7 +3062,7 @@ issue_4184: {
expect_stdout: "42"
}
issue_4235: {
issue_4235_1: {
options = {
inline: true,
reduce_vars: true,
@@ -3081,13 +3081,37 @@ issue_4235: {
}
expect: {
void function() {
var f;
console.log(f);
var f = console.log(f);
}();
}
expect_stdout: "undefined"
}
issue_4235_2: {
options = {
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
varify: true,
}
input: {
(function() {
{
const f = 0;
}
(function f() {
var f = console.log(f);
})();
})();
}
expect: {
console.log(void 0);
}
expect_stdout: "undefined"
}
issue_4404: {
options = {
pure_getters: "strict",

View File

@@ -4857,8 +4857,9 @@ issue_4155: {
}
expect: {
(function() {
void console.log(b);
var b = function() {};
var a;
void console.log(a);
function b() {}
b && console.log(typeof b);
})();
}
@@ -6655,3 +6656,49 @@ issue_5140: {
}
expect_stdout: "42"
}
issue_5173_1: {
options = {
conditionals: true,
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
function f(a, b) {
console.log(b);
}
f([ A = 42, [] + "" || (A = f) ]);
}
expect: {
function f(a, b) {
console.log(b);
}
f([ A = 42, [] + "" || (A = f) ]);
}
expect_stdout: "undefined"
}
issue_5173_2: {
options = {
conditionals: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a, b) {
console.log(b);
}
f([ A = 42, [] + "" || (A = f) ]);
}
expect: {
function f(a, b) {
console.log(b);
}
f(A = [] + "" ? 42 : f);
}
expect_stdout: "undefined"
}

View File

@@ -217,7 +217,8 @@ name_collision_1: {
var obj_foo = 1;
var obj_bar = 2;
function f() {
var obj_foo$0 = 3,
var obj,
obj_foo$0 = 3,
obj_bar = 4,
obj_b_r = 5,
obj_b_r$0 = 6,
@@ -249,7 +250,8 @@ name_collision_2: {
console.log(o.p === o.p, o["+"](4), o["-"](5), o__$0, o__$1);
}
expect: {
var o_p = 1,
var o,
o_p = 1,
o__ = function(x) {
return x;
},
@@ -283,7 +285,8 @@ name_collision_3: {
console.log(o.p === o.p, o["+"](4), o["-"](5));
}
expect: {
var o_p = 1,
var o,
o_p = 1,
o__ = function(x) {
return x;
},
@@ -315,7 +318,7 @@ name_collision_4: {
}
expect: {
console.log(function() {
var o_p$0 = 0, o_q = "PASS";
var o, o_p$0 = 0, o_q = "PASS";
return function(o_p) {
if (!o_p$0) return o_p;
}(o_q);
@@ -768,7 +771,7 @@ issue_3046: {
expect: {
console.log(function(a) {
do {
var b_c = a++;
var b, b_c = a++;
} while (b_c && a);
return a;
}(0));
@@ -931,7 +934,7 @@ issue_3411: {
expect: {
var c = 1;
!function f() {
var o_p = --c && f();
var o, o_p = --c && f();
+{} || console.log("PASS");
}();
}
@@ -1042,9 +1045,7 @@ issue_3945_1: {
expect: {
function f() {
o.p;
var o = {
q: 0,
};
var o, o_q = 0;
}
}
}
@@ -1063,9 +1064,7 @@ issue_3945_2: {
}
expect: {
console.log(typeof o);
var o = {
p: 0,
};
var o, o_p = 0;
}
expect_stdout: "undefined"
}
@@ -1134,10 +1133,46 @@ issue_4985: {
}());
}
expect: {
var a_p = 42;
var a, a_p = 42;
console.log(function() {
({});
}());
}
expect_stdout: "undefined"
}
issue_5182: {
options = {
hoist_props: true,
merge_vars: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var o = console;
log = o.log;
o = {
p: function(a) {
console.log(a ? "PASS" : "FAIL");
return a;
},
};
log(o.p(42));
}
expect: {
var o_p = console;
log = o_p.log;
o_p = function(a) {
console.log(a ? "PASS" : "FAIL");
return a;
};
log(o_p(42));
}
expect_stdout: [
"PASS",
"42",
]
}

View File

@@ -135,7 +135,7 @@ issue_2295: {
}
}
issue_4487: {
issue_4487_1: {
options = {
functions: true,
hoist_vars: true,
@@ -150,16 +150,64 @@ issue_4487: {
};
var b = a();
}
expect: {
var a = function f() {
var f = console.log(typeof f);
};
a();
}
expect_stdout: "undefined"
}
issue_4487_2: {
options = {
functions: true,
hoist_vars: true,
keep_fnames: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function f() {
var f = console.log(typeof f);
};
var b = a();
}
expect: {
function a() {
var f;
console.log(typeof f);
var f = console.log(typeof f);
}
a();
}
expect_stdout: "undefined"
}
issue_4487_3: {
options = {
functions: true,
hoist_vars: true,
keep_fnames: true,
passes: 3,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function f() {
var f = console.log(typeof f);
};
var b = a();
}
expect: {
(function a() {
console.log(typeof void 0);
})();
}
expect_stdout: "undefined"
}
issue_4489: {
options = {
collapse_vars: true,
@@ -251,18 +299,18 @@ issue_4839: {
unused: true,
}
input: {
var o = function(a, b) {
var log = console.log, o = function(a, b) {
return b && b;
}("foo");
for (var k in o)
throw "FAIL";
console.log("PASS");
log("PASS");
}
expect: {
var k, o = void 0;
for (k in o)
var k, log = console.log;
for (k in void 0)
throw "FAIL";
console.log("PASS");
log("PASS");
}
expect_stdout: "PASS"
}
@@ -288,8 +336,7 @@ issue_4859: {
}
expect: {
(function f(a) {
var d = 1 / 0, d = Infinity;
console.log(d);
console.log(Infinity);
return f;
})();
}
@@ -395,3 +442,61 @@ issue_4898: {
}
expect_stdout: "PASS"
}
issue_5187: {
options = {
hoist_props: true,
hoist_vars: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f() {
var a = 42;
do {
var b = { 0: a++ };
} while (console.log(b[b ^= 0]));
}
f();
}
expect: {
(function() {
var b, a = 42;
do {
b = { 0: a++ };
} while (console.log(b[b ^= 0]));
})();
}
expect_stdout: "42"
}
issue_5195: {
options = {
hoist_props: true,
hoist_vars: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f() {
var a;
do {
var b = { p: a };
} while (console.log(b += ""));
}
f();
}
expect: {
(function() {
var a, b;
do {
b = { p: a };
} while (console.log(b += ""));
})();
}
expect_stdout: "[object Object]"
}

View File

@@ -489,6 +489,116 @@ join_object_assignments_regex: {
expect_stdout: "1"
}
chained_assignments: {
options = {
join_vars: true,
}
input: {
var a, b = a = {};
b.p = "PASS";
console.log(a.p);
}
expect: {
var a, b = a = {
p: "PASS",
};
console.log(a.p);
}
expect_stdout: "PASS"
}
folded_assignments_1: {
options = {
evaluate: true,
join_vars: true,
}
input: {
var a = {};
a[a.PASS = 42] = "PASS";
console.log(a[42], a.PASS);
}
expect: {
var a = {
PASS: 42,
42: "PASS",
};
console.log(a[42], a.PASS);
}
expect_stdout: "PASS 42"
}
folded_assignments_2: {
options = {
evaluate: true,
join_vars: true,
}
input: {
"use strict";
var a = {};
a[42] = "FAIL";
a[a.PASS = 42] = "PASS";
console.log(a[42], a.PASS);
}
expect: {
"use strict";
var a = {
42: "FAIL",
PASS: 42,
};
a[42] = "PASS";
console.log(a[42], a.PASS);
}
expect_stdout: "PASS 42"
}
inlined_assignments: {
options = {
join_vars: true,
unused: true,
}
input: {
var a;
(a = {}).p = "PASS";
console.log(a.p);
}
expect: {
var a = {
p: "PASS",
};
console.log(a.p);
}
expect_stdout: "PASS"
}
typescript_enum: {
rename = true
options = {
assignments: true,
collapse_vars: true,
evaluate: true,
hoist_props: true,
inline: true,
join_vars: true,
passes: 4,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var Enum;
(function (Enum) {
Enum[Enum.PASS = 42] = "PASS";
})(Enum || (Enum = {}));
console.log(Enum[42], Enum.PASS);
}
expect: {
console.log("PASS", 42);
}
expect_stdout: "PASS 42"
}
issue_2816: {
options = {
join_vars: true,
@@ -1183,3 +1293,26 @@ assign_sequence_var: {
"1 2 3",
]
}
issue_5175: {
options = {
join_vars: true,
}
input: {
function log(f) {
console.log(f(), A.p);
}
log(function() {
return (A = {}).p = "PASS";
});
}
expect: {
function log(f) {
console.log(f(), A.p);
}
log(function() {
return (A = {}).p = "PASS";
});
}
expect_stdout: "PASS PASS"
}

View File

@@ -476,9 +476,9 @@ issue_4112: {
try {
throw 42;
} catch (e) {
var a = e;
for (e in a);
a = function() {};
var o = e;
for (e in o);
function a() {}
console.log(typeof a);
return a;
}
@@ -3377,3 +3377,60 @@ issue_4956_2: {
}
expect_stdout: "42"
}
issue_5182: {
options = {
arrows: true,
collapse_vars: true,
evaluate: true,
hoist_props: true,
inline: true,
merge_vars: true,
passes: 4,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
try {
var con = console;
} catch (x) {}
global.log = con.log;
var jump = function(x) {
console.log("JUMP:", x * 10);
return x + x;
};
var jump2 = jump;
var run = function(x) {
console.log("RUN:", x * -10);
return x * x;
};
var run2 = run;
var bar = (x, y) => {
console.log("BAR:", x + y);
return x - y;
};
var bar2 = bar;
var obj = {
foo: bar2,
go: run2,
not_used: jump2,
};
console.log(obj.foo(1, 2), global.log("PASS"));
}
expect: {
try {
var con = console;
} catch (x) {}
global.log = con.log,
console.log((console.log("BAR:", 3), -1), global.log("PASS"));
}
expect_stdout: [
"BAR: 3",
"PASS",
"-1 undefined",
]
node_version: ">=4"
}

View File

@@ -1510,3 +1510,71 @@ issue_5093_quote_style: {
expect_exact: 'console.log({a:true,\'42\':"PASS","null":[]}[6*7]);'
expect_stdout: "PASS"
}
object_methods: {
options = {
properties: true,
}
input: {
({
p() {
console.log("FAIL 1");
},
*q() {
console.log("FAIL 2");
},
async r() {
console.log("FAIL 3");
},
async *s() {
console.log("PASS");
},
}).s().next();
}
expect: {
[
() => {
console.log("FAIL 1");
},
function*() {
console.log("FAIL 2");
},
async () => {
console.log("FAIL 3");
},
async function*() {
console.log("PASS");
},
][3]().next();
}
expect_stdout: "PASS"
node_version: ">=10"
}
issue_5177: {
options = {
properties: true,
}
input: {
var a = "FAIL";
var o = { a: "PASS" };
o.p = {
q() {
return this.a;
},
}.q;
console.log(o.p());
}
expect: {
var a = "FAIL";
var o = { a: "PASS" };
o.p = {
q() {
return this.a;
},
}.q;
console.log(o.p());
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -1640,6 +1640,26 @@ nested_property_assignments_3: {
expect_stdout: "PASS"
}
nested_property_assignments_4: {
options = {
pure_getters: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var n, o = { p: { q: { r: "PASS" } } };
(n = o.p).r = n.q.r;
console.log(o.p.r);
}
expect: {
var n, o = { p: { q: { r: "PASS" } } };
(n = o.p).r = n.q.r;
console.log(o.p.r);
}
expect_stdout: "PASS"
}
issue_4939: {
options = {
pure_getters: "strict",

View File

@@ -7320,6 +7320,64 @@ local_assignment_loop: {
expect_stdout: "PASS"
}
local_assignment_modified: {
options = {
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a;
(a = a || {}).p = 42;
console.log(a.p);
}
expect: {
var a;
(a = {}).p = 42;
console.log(a.p);
}
expect_stdout: "42"
}
local_declaration: {
options = {
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a;
a || console.log(a = "PASS");
}
expect: {
var a;
console.log("PASS");
}
expect_stdout: "PASS"
}
local_definition_modified: {
options = {
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = a || {};
a.p = 42;
console.log(a.p);
}
expect: {
var a = {};
a.p = 42;
console.log(a.p);
}
expect_stdout: "42"
}
issue_3957_1: {
options = {
evaluate: true,
@@ -7435,6 +7493,7 @@ issue_4030: {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}

View File

@@ -1044,13 +1044,13 @@ issue_5100_1: {
}
expect: {
var a;
[ {
({
p: {},
...a
} ] = [ {
} = [ {
p: [ a = 42["q"] ],
r: "PASS",
} ];
} ][0]);
console.log(a.r);
}
expect_stdout: "PASS"
@@ -1077,12 +1077,12 @@ issue_5100_2: {
}
expect: {
var a;
[ {
({
p: {},
...a
} ] = [ {
} = [ {
p: [ console.log("PASS"), a = 42["q"] ],
} ];
} ][0]);
}
expect_stdout: "PASS"
node_version: ">=10"
@@ -1151,3 +1151,57 @@ issue_5128_2: {
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5165_1: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
reduce_vars: true,
rests: true,
side_effects: true,
switches: true,
unsafe: true,
}
input: {
console.log(function([ ...a ]) {
switch (a) {
case a:
return "PASS";
}
}([]));
}
expect: {
console.log(function([ ...a ]) {
return "PASS";
}([]));
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5165_2: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
reduce_vars: true,
rests: true,
side_effects: true,
switches: true,
unsafe: true,
}
input: {
console.log(function(...a) {
switch (a) {
case a:
return "PASS";
}
}());
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -201,3 +201,20 @@ issue_4811_2: {
expect_stdout: "PASS [object global] true"
node_version: ">=8"
}
issue_5197: {
rename = true
input: {
function f(async) {
async(")=>{}");
}
console.log("" + this.__proto__);
}
expect: {
function f(a) {
a(")=>{}");
}
console.log("" + this.__proto__);
}
expect_stdout: "[object global]"
}

View File

@@ -743,3 +743,29 @@ issue_5145_2: {
]
node_version: ">=4"
}
issue_5199: {
options = {
collapse_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function() {
console.log(typeof b);
}``;
{
const b = a;
}
}
expect: {
var a = function() {
console.log(typeof b);
}``;
{
const b = a;
}
}
expect_stdout: "undefined"
node_version: ">=4"
}

View File

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

View File

@@ -96,7 +96,7 @@ pause_resume: {
node_version: ">=4"
}
arrow_yield: {
arrow_yield_1: {
input: {
yield = "PASS";
console.log(function*() {
@@ -108,6 +108,18 @@ arrow_yield: {
node_version: ">=4"
}
arrow_yield_2: {
input: {
console.log(typeof function *() {
// Syntax error on Node.js v6+
return (yield) => {};
}().next().value);
}
expect_exact: "console.log(typeof function*(){return(yield)=>{}}().next().value);"
expect_stdout: "function"
node_version: "4"
}
for_of: {
input: {
function* f() {
@@ -1275,3 +1287,25 @@ issue_5076: {
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5177: {
options = {
properties: true,
}
input: {
console.log(typeof function*() {
return {
p(yield) {},
}.p;
}().next().value);
}
expect: {
console.log(typeof function*() {
return {
p(yield) {},
}.p;
}().next().value);
}
expect_stdout: "function"
node_version: ">=4"
}

View File

@@ -69,6 +69,7 @@ if (typeof phantom == "undefined") {
npm([
"install",
"graceful-fs@4.2.6",
"is-my-json-valid@2.20.5",
"phantomjs-prebuilt@2.1.14",
"--no-audit",
"--no-optional",

View File

@@ -4,34 +4,27 @@ var UglifyJS = require("../..");
describe("spidermonkey export/import sanity test", function() {
it("Should produce a functional build when using --self with spidermonkey", function(done) {
this.timeout(60000);
this.timeout(120000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +
uglifyjs + " -p spidermonkey -cm";
exec(command, {
maxBuffer: 1048576
}, function(err, stdout) {
var command = [
uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey",
uglifyjs + " -p spidermonkey -cm",
].join(" | ");
exec(command, { maxBuffer: 1048576 }, function(err, stdout) {
if (err) throw err;
eval(stdout);
assert.strictEqual(typeof SpiderUglify, "object");
var result = SpiderUglify.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done();
});
});
it("Should not add unnecessary escape slashes to regexps", function() {
it("Should not add unnecessary escape slashes to RegExp", function() {
var input = "/[\\\\/]/;";
var ast = UglifyJS.parse(input).to_mozilla_ast();
assert.equal(
UglifyJS.AST_Node.from_mozilla_ast(ast).print_to_string(),
input
);
assert.strictEqual(UglifyJS.AST_Node.from_mozilla_ast(ast).print_to_string(), input);
});
it("Should judge between directives and strings correctly on import", function() {

View File

@@ -28,7 +28,7 @@ exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, top
if ([
/\basync[ \t]*\([\s\S]*?\)[ \t]*=>/,
/\b(async[ \t]+function|Promise|setImmediate|setInterval|setTimeout)\b/,
/\basync([ \t]+|[ \t]*#|[ \t]*\*[ \t]*)[^\s()[\]{}#,.&|!~=*%/+-]+(\s*\(|[ \t]*=>)/,
/\basync([ \t]+|[ \t]*#|[ \t]*\*[ \t]*)[^\s()[\]{}#:;,.&|!~=*%/+-]+(\s*\(|[ \t]*=>)/,
].some(function(pattern) {
return pattern.test(code);
})) {
@@ -202,13 +202,11 @@ function setup(global, builtins, setup_log, setup_tty) {
});
Object.defineProperties(global, props);
// for Node.js v8+
if (global.toString !== Object.prototype.toString) {
global.__proto__ = Object.defineProperty(Object.create(global.__proto__), "toString", {
value: function() {
return "[object global]";
},
});
}
global.__proto__ = Object.defineProperty(Object.create(global.__proto__), "toString", {
value: function() {
return "[object global]";
},
});
function self() {
return this;