improve handling of non-trivial assignment values (#5227)

This commit is contained in:
Alex Lam S.L
2021-12-22 23:55:06 +00:00
committed by GitHub
parent 343bf6d7a5
commit 7b2eb4b5ff
13 changed files with 423 additions and 217 deletions

View File

@@ -1397,9 +1397,7 @@ Compressor.prototype.compress = function(node) {
operator: "+", operator: "+",
expression: make_ref(exp, fixed) expression: make_ref(exp, fixed)
}), }),
right: make_node(AST_Number, node, { right: make_node(AST_Number, node, { value: 1 }),
value: 1
})
}); });
}; };
d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : []; d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
@@ -1410,7 +1408,7 @@ Compressor.prototype.compress = function(node) {
exp.fixed = function() { exp.fixed = function() {
return make_node(AST_UnaryPrefix, node, { return make_node(AST_UnaryPrefix, node, {
operator: "+", operator: "+",
expression: make_ref(exp, fixed) expression: make_ref(exp, fixed),
}); });
}; };
exp.fixed.assigns = fixed && fixed.assigns; exp.fixed.assigns = fixed && fixed.assigns;
@@ -1501,11 +1499,11 @@ Compressor.prototype.compress = function(node) {
var fixed = this.definition().fixed; var fixed = this.definition().fixed;
if (fixed) { if (fixed) {
if (this.fixed) fixed = this.fixed; if (this.fixed) fixed = this.fixed;
return fixed instanceof AST_Node ? fixed : fixed(); return (fixed instanceof AST_Node ? fixed : fixed()).tail_node();
} }
fixed = fixed === 0 && this.fixed; fixed = fixed === 0 && this.fixed;
if (!fixed) return fixed; if (!fixed) return fixed;
var value = fixed instanceof AST_Node ? fixed : fixed(); var value = (fixed instanceof AST_Node ? fixed : fixed()).tail_node();
return value.is_constant() && value; return value.is_constant() && value;
}); });
@@ -1699,7 +1697,7 @@ Compressor.prototype.compress = function(node) {
function merge_sequence(array, node) { function merge_sequence(array, node) {
if (node instanceof AST_Sequence) { if (node instanceof AST_Sequence) {
array.push.apply(array, node.expressions); [].push.apply(array, node.expressions);
} else { } else {
array.push(node); array.push(node);
} }
@@ -1813,6 +1811,31 @@ Compressor.prototype.compress = function(node) {
return true; return true;
} }
// Certain combination of unused name + side effect leads to invalid AST:
// https://github.com/mishoo/UglifyJS/issues/44
// https://github.com/mishoo/UglifyJS/issues/1838
// https://github.com/mishoo/UglifyJS/issues/3371
// We fix it at this stage by moving the `var` outside the `for`.
function patch_for_init(node, in_list) {
var block;
if (node.init instanceof AST_BlockStatement) {
block = node.init;
node.init = block.body.pop();
block.body.push(node);
}
if (node.init instanceof AST_Defun) {
if (!block) block = make_node(AST_BlockStatement, node, { body: [ node ] });
block.body.splice(-1, 0, node.init);
node.init = null;
} else if (node.init instanceof AST_SimpleStatement) {
node.init = node.init.body;
} else if (is_empty(node.init)) {
node.init = null;
}
if (!block) return;
return in_list ? List.splice(block.body) : block;
}
function tighten_body(statements, compressor) { function tighten_body(statements, compressor) {
var in_lambda = last_of(compressor, function(node) { var in_lambda = last_of(compressor, function(node) {
return node instanceof AST_Lambda; return node instanceof AST_Lambda;
@@ -1972,12 +1995,12 @@ Compressor.prototype.compress = function(node) {
var def = candidate.name.definition(); var def = candidate.name.definition();
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++; def.replaced++;
return maintain_this_binding(compressor, parent, node, candidate.value); return maintain_this_binding(compressor, parent, node, rvalue);
} }
return make_node(AST_Assign, candidate, { return make_node(AST_Assign, candidate, {
operator: "=", operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name), left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: candidate.value, right: rvalue,
}); });
} }
var assign = candidate; var assign = candidate;
@@ -1986,7 +2009,9 @@ Compressor.prototype.compress = function(node) {
if (!(assign instanceof AST_Assign)) break; if (!(assign instanceof AST_Assign)) break;
assign = assign.right; assign = assign.right;
} }
return candidate; assign = candidate.clone();
assign.right = rvalue;
return assign;
} }
// These node types have child nodes that execute sequentially, // These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them. // but are otherwise not safe to scan into or beyond them.
@@ -2083,7 +2108,9 @@ Compressor.prototype.compress = function(node) {
} }
// Skip (non-executed) functions and (leading) default case in switch statements // Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node; if (node instanceof AST_Default || node instanceof AST_Scope) return node;
}, patch_sequence); }, function(node) {
return patch_sequence(node, multi_replacer);
});
while (--stat_index >= 0) { while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e. // Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x()); // function(a, b){ ... }(x());
@@ -2122,7 +2149,14 @@ Compressor.prototype.compress = function(node) {
var well_defined = true; var well_defined = true;
var lvalues = get_lvalues(candidate); var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs); var lhs_local = is_lhs_local(lhs);
var rvalue = get_rvalue(candidate); var rhs_value = get_rvalue(candidate);
var rvalue;
if (rhs_value instanceof AST_Sequence
&& !(candidate instanceof AST_Assign && candidate.operator != "=")) {
rvalue = rhs_value.tail_node();
} else {
rvalue = rhs_value;
}
if (!side_effects) side_effects = value_has_side_effects(); if (!side_effects) side_effects = value_has_side_effects();
var check_destructured = in_try || !lhs_local ? function(node) { var check_destructured = in_try || !lhs_local ? function(node) {
return node instanceof AST_Destructured; return node instanceof AST_Destructured;
@@ -2163,7 +2197,7 @@ Compressor.prototype.compress = function(node) {
value_def.single_use = false; value_def.single_use = false;
changed = true; changed = true;
} }
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1); if (replaced) remove_candidate(candidate);
} }
} }
return changed; return changed;
@@ -2768,6 +2802,7 @@ Compressor.prototype.compress = function(node) {
return; return;
} }
if (remaining < 1) return; if (remaining < 1) return;
rhs = rhs.tail_node();
var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs; var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
if (!(value instanceof AST_SymbolRef)) return; if (!(value instanceof AST_SymbolRef)) return;
var def = value.definition(); var def = value.definition();
@@ -3002,24 +3037,21 @@ Compressor.prototype.compress = function(node) {
} }
function remove_candidate(expr) { function remove_candidate(expr) {
var value = rvalue === rhs_value ? null : make_sequence(rhs_value, rhs_value.expressions.slice(0, -1));
var index = expr.name_index; var index = expr.name_index;
if (index >= 0) { if (index >= 0) {
var argname = scope.argnames[index]; var argname = scope.argnames[index];
if (argname instanceof AST_DefaultValue) { if (argname instanceof AST_DefaultValue) {
argname.value = make_node(AST_Number, argname, { argname.value = value || make_node(AST_Number, argname, { value: 0 });
value: 0
});
argname.name.definition().fixed = false; argname.name.definition().fixed = false;
} else { } else {
var args = compressor.parent().args; var args = compressor.parent().args;
if (args[index]) { if (args[index]) {
args[index] = make_node(AST_Number, args[index], { args[index] = value || make_node(AST_Number, args[index], { value: 0 });
value: 0
});
argname.definition().fixed = false; argname.definition().fixed = false;
} }
} }
return true; return;
} }
var end = hit_stack.length - 1; var end = hit_stack.length - 1;
if (hit_stack[end - 1].body === hit_stack[end]) end--; if (hit_stack[end - 1].body === hit_stack[end]) end--;
@@ -3034,20 +3066,46 @@ Compressor.prototype.compress = function(node) {
if (value_def) value_def.replaced++; if (value_def) value_def.replaced++;
node = node.clone(); node = node.clone();
node.value = null; node.value = null;
return node; return value ? List.splice([ value, node ]) : node;
} }
return in_list ? List.skip : null; if (!value) return in_list ? List.skip : null;
}, patch_sequence); return is_statement(node) ? make_node(AST_SimpleStatement, value, { body: value }) : value;
}, function(node, in_list) {
if (node instanceof AST_Definitions) {
var body = [], defns = node.definitions;
for (var index = 0, pos = 0; index < defns.length; index++) {
var defn = defns[index];
if (defn instanceof AST_VarDef) continue;
flush();
pos = index + 1;
body.push(make_node(AST_SimpleStatement, defn, { body: defn }));
}
if (pos == 0) return;
flush();
if (body.length == 1) return body[0];
return in_list ? List.splice(body) : make_node(AST_BlockStatement, node, { body: body });
}
if (node instanceof AST_For) return patch_for_init(node, in_list);
return patch_sequence(node, this);
function flush() {
if (pos < index) {
var cropped = node.clone();
cropped.definitions = defns.slice(pos, index);
body.push(cropped);
}
}
});
abort = false; abort = false;
hit = false; hit = false;
hit_index = 0; hit_index = 0;
return statements[stat_index].transform(tt); if (!(statements[stat_index] = statements[stat_index].transform(tt))) statements.splice(stat_index, 1);
} }
function patch_sequence(node) { function patch_sequence(node, tt) {
if (node instanceof AST_Sequence) switch (node.expressions.length) { if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null; case 0: return null;
case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]); case 1: return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
} }
} }
@@ -3779,11 +3837,18 @@ Compressor.prototype.compress = function(node) {
} else if (stat instanceof AST_If) { } else if (stat instanceof AST_If) {
stat.condition = join_assigns_expr(stat.condition); stat.condition = join_assigns_expr(stat.condition);
} else if (stat instanceof AST_SimpleStatement) { } else if (stat instanceof AST_SimpleStatement) {
var exprs = join_assigns(prev, stat.body); var exprs = join_assigns(prev, stat.body), next;
if (exprs) { if (exprs) {
changed = true; changed = true;
if (!exprs.length) continue; if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs); stat.body = make_sequence(stat.body, exprs);
} else if (prev instanceof AST_Definitions
&& (next = statements[i + 1])
&& prev.TYPE == next.TYPE
&& (next = next.definitions[0]).value) {
changed = true;
next.value = make_sequence(stat, [ stat.body, next.value ]);
continue;
} }
} else if (stat instanceof AST_Switch) { } else if (stat instanceof AST_Switch) {
stat.expression = join_assigns_expr(stat.expression); stat.expression = join_assigns_expr(stat.expression);
@@ -6690,7 +6755,7 @@ Compressor.prototype.compress = function(node) {
value = null; value = null;
trim_defns.push(def); trim_defns.push(def);
} }
var old_def; var old_def, fn;
if (!value && !(node instanceof AST_Let)) { if (!value && !(node instanceof AST_Let)) {
if (parent instanceof AST_ExportDeclaration) { if (parent instanceof AST_ExportDeclaration) {
flush(); flush();
@@ -6704,17 +6769,18 @@ Compressor.prototype.compress = function(node) {
} else if (compressor.option("functions") } else if (compressor.option("functions")
&& !compressor.option("ie") && !compressor.option("ie")
&& drop_sym && drop_sym
&& value
&& var_defs[sym.id] == 1 && var_defs[sym.id] == 1
&& sym.assignments == 0 && sym.assignments == 0
&& value instanceof AST_LambdaExpression && (fn = value.tail_node()) instanceof AST_LambdaExpression
&& !is_arguments(sym) && !is_arguments(sym)
&& !is_arrow(value) && !is_arrow(fn)
&& assigned_once(value, sym.references) && assigned_once(fn, sym.references)
&& can_declare_defun(value) && can_declare_defun(fn)
&& (old_def = rename_def(value, def.name.name)) !== false) { && (old_def = rename_def(fn, def.name.name)) !== false) {
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name)); AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
var ctor; var ctor;
switch (value.CTOR) { switch (fn.CTOR) {
case AST_AsyncFunction: case AST_AsyncFunction:
ctor = AST_AsyncDefun; ctor = AST_AsyncDefun;
break; break;
@@ -6728,7 +6794,7 @@ Compressor.prototype.compress = function(node) {
ctor = AST_GeneratorDefun; ctor = AST_GeneratorDefun;
break; break;
} }
var defun = make_node(ctor, def, value); var defun = make_node(ctor, def, fn);
defun.name = make_node(AST_SymbolDefun, def.name, def.name); defun.name = make_node(AST_SymbolDefun, def.name, def.name);
var name_def = def.name.scope.resolve().def_function(defun.name); var name_def = def.name.scope.resolve().def_function(defun.name);
if (old_def) old_def.forEach(function(node) { if (old_def) old_def.forEach(function(node) {
@@ -6737,6 +6803,7 @@ Compressor.prototype.compress = function(node) {
node.reference(); node.reference();
}); });
body.push(defun); body.push(defun);
if (value !== fn) [].push.apply(side_effects, value.expressions.slice(0, -1));
} else { } else {
if (drop_sym if (drop_sym
&& var_defs[sym.id] > 1 && var_defs[sym.id] > 1
@@ -6759,7 +6826,7 @@ Compressor.prototype.compress = function(node) {
head.push(def); head.push(def);
} }
} else { } else {
value = value && !value.single_use && value.drop_side_effect_free(compressor); value = value && value.drop_side_effect_free(compressor);
if (value) { if (value) {
AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name)); AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value); side_effects.push(value);
@@ -6867,13 +6934,18 @@ Compressor.prototype.compress = function(node) {
} }
} }
default: default:
var seq;
if (tail.length > 0 && (seq = tail[0].value) instanceof AST_Sequence) {
tail[0].value = seq.tail_node();
body.push(make_node(AST_SimpleStatement, node, {
body: make_sequence(seq, seq.expressions.slice(0, -1)),
}));
}
node.definitions = head.concat(tail); node.definitions = head.concat(tail);
body.push(node); body.push(node);
} }
if (side_effects.length > 0) { if (side_effects.length > 0) {
body.push(make_node(AST_SimpleStatement, node, { body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) }));
body: make_sequence(node, side_effects)
}));
} }
return insert_statements(body, node, in_list); return insert_statements(body, node, in_list);
} }
@@ -6922,33 +6994,7 @@ Compressor.prototype.compress = function(node) {
} }
}, function(node, in_list) { }, function(node, in_list) {
if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list); if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
// Certain combination of unused name + side effect leads to invalid AST: if (node instanceof AST_For) return patch_for_init(node, in_list);
// https://github.com/mishoo/UglifyJS/issues/44
// https://github.com/mishoo/UglifyJS/issues/1838
// https://github.com/mishoo/UglifyJS/issues/3371
// We fix it at this stage by moving the `var` outside the `for`.
if (node instanceof AST_For) {
var block;
if (node.init instanceof AST_BlockStatement) {
block = node.init;
node.init = block.body.pop();
block.body.push(node);
}
if (node.init instanceof AST_Defun) {
if (!block) {
block = make_node(AST_BlockStatement, node, {
body: [ node ]
});
}
block.body.splice(-1, 0, node.init);
node.init = null;
} else if (node.init instanceof AST_SimpleStatement) {
node.init = node.init.body;
} else if (is_empty(node.init)) {
node.init = null;
}
return !block ? node : in_list ? List.splice(block.body) : block;
}
if (node instanceof AST_ForIn) { if (node instanceof AST_ForIn) {
if (!drop_vars || !compressor.option("loops")) return; if (!drop_vars || !compressor.option("loops")) return;
if (!is_empty(node.body)) return; if (!is_empty(node.body)) return;
@@ -8749,15 +8795,21 @@ Compressor.prototype.compress = function(node) {
var body = [], var_defs = [], refs = []; var body = [], var_defs = [], refs = [];
var body_exprs = sequencesize(self.body, body, var_defs, refs); var body_exprs = sequencesize(self.body, body, var_defs, refs);
var alt_exprs = sequencesize(self.alternative, body, var_defs, refs); var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
if (body_exprs && alt_exprs) { if (body_exprs instanceof AST_BlockStatement || alt_exprs instanceof AST_BlockStatement) {
if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
body.push(self);
if (body_exprs instanceof AST_BlockStatement) self.body = body_exprs;
if (alt_exprs instanceof AST_BlockStatement) self.alternative = alt_exprs;
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
} else if (body_exprs && alt_exprs) {
if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs })); if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
if (body_exprs.length == 0) { if (body_exprs.length == 0) {
body.push(make_node(AST_SimpleStatement, self.condition, { body.push(make_node(AST_SimpleStatement, self.condition, {
body: alt_exprs.length > 0 ? make_node(AST_Binary, self, { body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
operator : "||", operator: "||",
left : self.condition, left: self.condition,
right : make_sequence(self.alternative, alt_exprs) right: make_sequence(self.alternative, alt_exprs),
}).transform(compressor) : self.condition.clone() }).transform(compressor) : self.condition.clone(),
}).optimize(compressor)); }).optimize(compressor));
} else if (alt_exprs.length == 0) { } else if (alt_exprs.length == 0) {
if (self_condition_length === negated_length && !negated_is_best if (self_condition_length === negated_length && !negated_is_best
@@ -8769,79 +8821,64 @@ Compressor.prototype.compress = function(node) {
} }
body.push(make_node(AST_SimpleStatement, self, { body.push(make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, { body: make_node(AST_Binary, self, {
operator : negated_is_best ? "||" : "&&", operator: negated_is_best ? "||" : "&&",
left : negated_is_best ? negated : self.condition, left: negated_is_best ? negated : self.condition,
right : make_sequence(self.body, body_exprs) right: make_sequence(self.body, body_exprs),
}).transform(compressor) }).transform(compressor),
}).optimize(compressor)); }).optimize(compressor));
} else { } else {
body.push(make_node(AST_SimpleStatement, self, { body.push(make_node(AST_SimpleStatement, self, {
body: make_node(AST_Conditional, self, { body: make_node(AST_Conditional, self, {
condition : self.condition, condition: self.condition,
consequent : make_sequence(self.body, body_exprs), consequent: make_sequence(self.body, body_exprs),
alternative : make_sequence(self.alternative, alt_exprs) alternative: make_sequence(self.alternative, alt_exprs),
}) }),
}).optimize(compressor)); }).optimize(compressor));
} }
refs.forEach(function(ref) { refs.forEach(function(ref) {
ref.definition().references.push(ref); ref.definition().references.push(ref);
}); });
return make_node(AST_BlockStatement, self, { return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
body: body
}).optimize(compressor);
} }
if (is_empty(self.body)) { if (is_empty(self.body)) self = make_node(AST_If, self, {
self = make_node(AST_If, self, { condition: negated,
condition: negated, body: self.alternative,
body: self.alternative, alternative: null,
alternative: null });
}); if (self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) {
}
if (self.body instanceof AST_Exit
&& self.alternative instanceof AST_Exit
&& self.body.TYPE == self.alternative.TYPE) {
var exit = make_node(self.body.CTOR, self, { var exit = make_node(self.body.CTOR, self, {
value: make_node(AST_Conditional, self, { value: make_node(AST_Conditional, self, {
condition : self.condition, condition: self.condition,
consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor), consequent: self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor) alternative: self.alternative.value
}) || make_node(AST_Undefined, self.alternative).transform(compressor),
}),
}); });
if (exit instanceof AST_Return) { if (exit instanceof AST_Return) exit.in_bool = self.body.in_bool || self.alternative.in_bool;
exit.in_bool = self.body.in_bool || self.alternative.in_bool;
}
return exit; return exit;
} }
if (self.body instanceof AST_If if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) {
&& !self.body.alternative
&& !self.alternative) {
self = make_node(AST_If, self, { self = make_node(AST_If, self, {
condition: make_node(AST_Binary, self.condition, { condition: make_node(AST_Binary, self.condition, {
operator: "&&", operator: "&&",
left: self.condition, left: self.condition,
right: self.body.condition right: self.body.condition,
}), }),
body: self.body.body, body: self.body.body,
alternative: null alternative: null,
}); });
} }
if (aborts(self.body)) { if (aborts(self.body) && self.alternative) {
if (self.alternative) { var alt = self.alternative;
var alt = self.alternative; self.alternative = null;
self.alternative = null; return make_node(AST_BlockStatement, self, { body: [ self, alt ] }).optimize(compressor);
return make_node(AST_BlockStatement, self, {
body: [ self, alt ]
}).optimize(compressor);
}
} }
if (aborts(self.alternative)) { if (aborts(self.alternative)) {
var body = self.body; var body = self.body;
self.body = self.alternative; self.body = self.alternative;
self.condition = negated_is_best ? negated : self.condition.negate(compressor); self.condition = negated_is_best ? negated : self.condition.negate(compressor);
self.alternative = null; self.alternative = null;
return make_node(AST_BlockStatement, self, { return make_node(AST_BlockStatement, self, { body: [ self, body ] }).optimize(compressor);
body: [ self, body ]
}).optimize(compressor);
} }
if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative); if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
return self; return self;
@@ -8852,10 +8889,19 @@ Compressor.prototype.compress = function(node) {
var exprs = []; var exprs = [];
for (var i = 0; i < stat.body.length; i++) { for (var i = 0; i < stat.body.length; i++) {
var line = stat.body[i]; var line = stat.body[i];
if (line instanceof AST_EmptyStatement) continue;
if (line instanceof AST_Exit) {
if (exprs.length == 0) return;
line = line.clone();
exprs.push(line.value || make_node(AST_Undefined, line).transform(compressor));
line.value = make_sequence(stat, exprs);
var block = stat.clone();
block.body = block.body.slice(i + 1);
block.body.unshift(line);
return block;
}
if (line instanceof AST_LambdaDefinition) { if (line instanceof AST_LambdaDefinition) {
defuns.push(line); defuns.push(line);
} else if (line instanceof AST_EmptyStatement) {
continue;
} else if (line instanceof AST_SimpleStatement) { } else if (line instanceof AST_SimpleStatement) {
if (!compressor.option("sequences") && exprs.length > 0) return; if (!compressor.option("sequences") && exprs.length > 0) return;
exprs.push(line.body); exprs.push(line.body);
@@ -9320,9 +9366,7 @@ Compressor.prototype.compress = function(node) {
args[pos++] = make_sequence(call, side_effects); args[pos++] = make_sequence(call, side_effects);
side_effects = []; side_effects = [];
} else { } else {
args[pos++] = make_node(AST_Number, args[i], { args[pos++] = make_node(AST_Number, args[i], { value: 0 });
value: 0
});
continue; continue;
} }
} }
@@ -9924,10 +9968,14 @@ Compressor.prototype.compress = function(node) {
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
var line = fn.body[i]; var line = fn.body[i];
if (line instanceof AST_Var) { if (line instanceof AST_Var) {
var assigned = var_assigned || !declarations_only(line); if (var_assigned) {
if (assigned) { if (!stat) continue;
if (!(stat instanceof AST_SimpleStatement)) return false;
if (!declarations_only(line)) stat = null;
} else if (!declarations_only(line)) {
if (stat && !(stat instanceof AST_SimpleStatement)) return false;
stat = null;
var_assigned = true; var_assigned = true;
if (stat) return false;
} }
} else if (line instanceof AST_AsyncDefun } else if (line instanceof AST_AsyncDefun
|| line instanceof AST_Defun || line instanceof AST_Defun
@@ -10191,7 +10239,7 @@ Compressor.prototype.compress = function(node) {
function flatten_vars(decls, expressions) { function flatten_vars(decls, expressions) {
var args = [ insert, 0 ]; var args = [ insert, 0 ];
var decl_var = [], expr_var = [], expr_loop = []; var decl_var = [], expr_var = [], expr_loop = [], exprs = [];
for (var i = 0; i < fn.body.length; i++) { for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i]; var stat = fn.body[i];
if (stat instanceof AST_LambdaDefinition) { if (stat instanceof AST_LambdaDefinition) {
@@ -10209,11 +10257,20 @@ Compressor.prototype.compress = function(node) {
} }
continue; continue;
} }
if (!(stat instanceof AST_Var)) continue; if (!(stat instanceof AST_Var)) {
if (stat instanceof AST_SimpleStatement) exprs.push(stat.body);
continue;
}
for (var j = 0; j < stat.definitions.length; j++) { for (var j = 0; j < stat.definitions.length; j++) {
var var_def = stat.definitions[j]; var var_def = stat.definitions[j];
var name = flatten_var(var_def.name); var name = flatten_var(var_def.name);
append_var(decl_var, expr_var, name, var_def.value); var value = var_def.value;
if (value && exprs.length > 0) {
exprs.push(value);
value = make_sequence(var_def, exprs);
exprs = [];
}
append_var(decl_var, expr_var, name, value);
if (in_loop && !arg_used.has(name.name)) { if (in_loop && !arg_used.has(name.name)) {
var def = fn.variables.get(name.name); var def = fn.variables.get(name.name);
var sym = make_node(AST_SymbolRef, name, name); var sym = make_node(AST_SymbolRef, name, name);
@@ -12211,9 +12268,7 @@ Compressor.prototype.compress = function(node) {
} }
function pop_seq(node) { function pop_seq(node) {
if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { value: 0 });
value: 0
});
return make_sequence(node, node.expressions.slice(0, -1)); return make_sequence(node, node.expressions.slice(0, -1));
} }
}); });
@@ -12526,35 +12581,25 @@ Compressor.prototype.compress = function(node) {
var exp = self.expression.expression; var exp = self.expression.expression;
if (is_undeclared_ref(exp)) switch (exp.name) { if (is_undeclared_ref(exp)) switch (exp.name) {
case "Array": case "Array":
self.expression = make_node(AST_Array, self.expression, { self.expression = make_node(AST_Array, self.expression, { elements: [] });
elements: []
});
break; break;
case "Function": case "Function":
self.expression = make_node(AST_Function, self.expression, { self.expression = make_node(AST_Function, self.expression, {
argnames: [], argnames: [],
body: [] body: [],
}).init_vars(exp.scope); }).init_vars(exp.scope);
break; break;
case "Number": case "Number":
self.expression = make_node(AST_Number, self.expression, { self.expression = make_node(AST_Number, self.expression, { value: 0 });
value: 0
});
break; break;
case "Object": case "Object":
self.expression = make_node(AST_Object, self.expression, { self.expression = make_node(AST_Object, self.expression, { properties: [] });
properties: []
});
break; break;
case "RegExp": case "RegExp":
self.expression = make_node(AST_RegExp, self.expression, { self.expression = make_node(AST_RegExp, self.expression, { value: /t/ });
value: /t/
});
break; break;
case "String": case "String":
self.expression = make_node(AST_String, self.expression, { self.expression = make_node(AST_String, self.expression, { value: "" });
value: ""
});
break; break;
} }
} }

View File

@@ -489,7 +489,7 @@ logical_assignments: {
node_version: ">=15" node_version: ">=15"
} }
logical_collapse_vars: { logical_collapse_vars_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
} }
@@ -509,6 +509,27 @@ logical_collapse_vars: {
node_version: ">=15" node_version: ">=15"
} }
logical_collapse_vars_2: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS";
(function(b) {
b ||= (a = "FAIL", {});
return b;
})(console).log(a);
}
expect: {
var a = "PASS";
(function(b) {
return b ||= (a = "FAIL", {});
})(console).log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
logical_reduce_vars: { logical_reduce_vars: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -346,9 +346,7 @@ collapse_vars_if: {
return "x" != "Bar" + x / 4 ? g9 : g5; return "x" != "Bar" + x / 4 ? g9 : g5;
} }
function f3(x) { function f3(x) {
if (x) return x ? 1 : 2;
return 1;
return 2;
} }
} }
} }
@@ -1912,6 +1910,42 @@ collapse_vars_regexp: {
] ]
} }
collapse_arg_sequence: {
options = {
collapse_vars: true,
unused: true,
}
input: {
(function(a) {
a("foo");
})((console.log("bar"), console.log));
}
expect: {
(function(a) {
(0, console.log)("foo");
})(console.log("bar"));
}
expect_stdout: [
"bar",
"foo",
]
}
collapse_for_init: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
for (var a = (Math, console), b = a.log("PASS"); b;);
}
expect: {
Math;
for (var a, b = console.log("PASS"); b;);
}
expect_stdout: "PASS"
}
issue_1537: { issue_1537: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -2667,6 +2701,24 @@ chained_4: {
expect_stdout: "foo undefined" expect_stdout: "foo undefined"
} }
chained_5: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS";
var a = (console, console.log(a));
a && ++a;
}
expect: {
var a = "PASS";
console;
var a;
(a = console.log(a)) && ++a;
}
expect_stdout: "PASS"
}
boolean_binary_1: { boolean_binary_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -2906,6 +2958,43 @@ compound_assignment_2: {
expect_stdout: "4" expect_stdout: "4"
} }
compound_assignment_3: {
options = {
collapse_vars: true,
}
input: {
var a = 1;
a += (console.log("PASS"), 2);
a.p;
}
expect: {
var a = 1;
(a += (console.log("PASS"), 2)).p;
}
expect_stdout: "PASS"
}
compound_assignment_4: {
options = {
collapse_vars: true,
evaluate: true,
}
input: {
A = "PASS";
var a = "";
a += (a = "FAIL", A);
a.p;
console.log(a);
}
expect: {
A = "PASS";
var a = "";
(a += (a = "FAIL", A)).p;
console.log(a);
}
expect_stdout: "PASS"
}
issue_2187_1: { issue_2187_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -3030,10 +3119,10 @@ issue_2203_2: {
a: "FAIL", a: "FAIL",
b: function() { b: function() {
return function(c) { return function(c) {
return (String, (Object, function() { return (Object, function() {
return this; return this;
}())).a; }()).a;
}(); }(String);
} }
}.b()); }.b());
} }
@@ -3764,17 +3853,17 @@ issue_2437_1: {
console.log(foo()); console.log(foo());
} }
expect: { expect: {
console.log(function() { var req, detectFunc, result;
if (xhrDesc) { console.log((
var result = !!(req = new XMLHttpRequest()).onreadystatechange; xhrDesc ? (result = !!(req = new XMLHttpRequest).onreadystatechange,
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}), Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc||{})
result; ) : (
} (req = new XMLHttpRequest).onreadystatechange = detectFunc = function(){},
var req = new XMLHttpRequest(), detectFunc = function(){}; result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
return req.onreadystatechange = detectFunc, req.onreadystatechange = null
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc, ),
req.onreadystatechange = null, result; result
}()); ));
} }
} }
@@ -3815,15 +3904,15 @@ issue_2437_2: {
foo(); foo();
} }
expect: { expect: {
!function() { var req;
if (xhrDesc) xhrDesc ? (
return (req = new XMLHttpRequest()).onreadystatechange, (req = new XMLHttpRequest).onreadystatechange,
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}); Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {})
var req = new XMLHttpRequest(); ) : (
req.onreadystatechange = function(){}, (req = new XMLHttpRequest).onreadystatechange = function(){},
req[SYMBOL_FAKE_ONREADYSTATECHANGE_1], req[SYMBOL_FAKE_ONREADYSTATECHANGE_1],
req.onreadystatechange = null; req.onreadystatechange = null
}(); );
} }
} }
@@ -6445,7 +6534,8 @@ assign_undeclared: {
console.log(typeof B); console.log(typeof B);
} }
expect: { expect: {
B = new (console.log(42), function() {})(); console.log(42);
B = new function() {}();
console.log(typeof B); console.log(typeof B);
} }
expect_stdout: [ expect_stdout: [
@@ -9141,9 +9231,7 @@ issue_4908: {
console.log(d[1]); console.log(d[1]);
} }
expect: { expect: {
var a = 0, b; var a = 0, b, c = (console || a++, a), d = [ (d = a) && d, d += 42 ];
console || a++;
var c = a, d = [ (d = a) && d, d += 42 ];
console.log(d[1]); console.log(d[1]);
} }
expect_stdout: "42" expect_stdout: "42"

View File

@@ -158,6 +158,28 @@ process_boolean_returns: {
node_version: ">=6" node_version: ">=6"
} }
collapse_arg_sequence: {
options = {
collapse_vars: true,
unused: true,
}
input: {
(function(a = (console.log("bar"), console.log)) {
a("foo");
})();
}
expect: {
(function(a = console.log("bar")) {
(0, console.log)("foo");
})();
}
expect_stdout: [
"bar",
"foo",
]
node_version: ">=6"
}
collapse_value_1: { collapse_value_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,

View File

@@ -395,7 +395,8 @@ funarg_unused_6_keep_fargs: {
} }
expect: { expect: {
(function() { (function() {
var {} = (console, 42); console;
var {} = 42;
})(); })();
console.log(typeof a); console.log(typeof a);
} }

View File

@@ -1728,7 +1728,8 @@ issue_2768: {
} }
expect: { expect: {
var a = "FAIL"; var a = "FAIL";
var c = (d = a, void (d && (a = "PASS"))); d = a;
var c = void (d && (a = "PASS"));
var d; var d;
console.log(a, typeof c); console.log(a, typeof c);
} }
@@ -2382,7 +2383,8 @@ issue_3664: {
} }
expect: { expect: {
console.log(function() { console.log(function() {
var a, b = (a = (a = [ b && console.log("FAIL") ]).p = 0, 0); a = (a = [ b && console.log("FAIL") ]).p = 0;
var a, b = 0;
return "PASS"; return "PASS";
}()); }());
} }
@@ -2551,10 +2553,9 @@ issue_3899: {
console.log(typeof a); console.log(typeof a);
} }
expect: { expect: {
function a() { console.log(typeof function() {
return 2; return 2;
} });
console.log(typeof a);
} }
expect_stdout: "function" expect_stdout: "function"
} }

View File

@@ -99,8 +99,8 @@ issue_4664: {
expect: { expect: {
(function f() { (function f() {
new function(a) { new function(a) {
console.log(typeof f, a, typeof this); console.log(typeof f, 2 ** 30, typeof this);
}((A = 0, 2 ** 30)); }(A = 0);
})(); })();
} }
expect_stdout: "function 1073741824 object" expect_stdout: "function 1073741824 object"

View File

@@ -3039,18 +3039,17 @@ issue_2437: {
console.log(foo()); console.log(foo());
} }
expect: { expect: {
console.log(function() { var req, detectFunc, result;
if (xhrDesc) { console.log((
var result = !!(req = new XMLHttpRequest()).onreadystatechange; xhrDesc ? (
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}), result = !!(req = new XMLHttpRequest).onreadystatechange,
result; Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {})
} ) : (
function detectFunc() {} (req = new XMLHttpRequest).onreadystatechange = detectFunc = function(){},
var req = new XMLHttpRequest(); result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,req.onreadystatechange = null
return req.onreadystatechange = detectFunc, ),
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc, result
req.onreadystatechange = null, result; ));
}());
} }
} }
@@ -3826,16 +3825,14 @@ inlined_single_use: {
} }
expect: { expect: {
console.log(function(f) { console.log(function(f) {
var a = function() { a = function() {
A; A;
}; },
var b = function() { b = function() {
a(B); a(B);
}; },
(function() { void 0;
b; var a, b;
});
return;
}()); }());
} }
expect_stdout: "undefined" expect_stdout: "undefined"
@@ -5290,6 +5287,42 @@ direct_inline_catch_redefined: {
expect_stdout: true expect_stdout: true
} }
statement_var_inline: {
options = {
inline: true,
join_vars: true,
unused: true,
}
input: {
function f() {
(function() {
var a = {};
function g() {
a.p;
}
g(console.log("PASS"));
var b = function h(c) {
c && c.q;
}();
})();
}
f();
}
expect: {
function f() {
var c, a = {};
function g() {
a.p;
}
g(console.log("PASS"));
c && c.q;
return;
}
f();
}
expect_stdout: "PASS"
}
issue_4171_1: { issue_4171_1: {
options = { options = {
functions: true, functions: true,

View File

@@ -282,7 +282,7 @@ issue_4736: {
(function() { (function() {
(function() { (function() {
0, 0,
console.log(1073741824); console.log(1 << 30);
})(); })();
})(); })();
} }
@@ -336,7 +336,7 @@ issue_4859: {
} }
expect: { expect: {
(function f(a) { (function f(a) {
console.log(Infinity); console.log(2 + 1 / 0);
return f; return f;
})(); })();
} }

View File

@@ -1282,10 +1282,7 @@ assign_sequence_var: {
console.log(a, b, c); console.log(a, b, c);
} }
expect: { expect: {
var a = 0, b = 1; var a = 0, b = 1, c = (console.log(a), a++, b = 2, 3);
console.log(a),
a++;
var b = 2, c = 3;
console.log(a, b, c); console.log(a, b, c);
} }
expect_stdout: [ expect_stdout: [

View File

@@ -181,10 +181,10 @@ issue_2203_2: {
a: "FAIL", a: "FAIL",
b: function() { b: function() {
return function() { return function() {
return (String, (Object, function() { return (Object, function() {
return this; return this;
}())).a; }()).a;
}(); }(String);
} }
}.b()); }.b());
} }

View File

@@ -910,9 +910,7 @@ hoist_decl: {
var d; var d;
} }
expect: { expect: {
var a; var a, b = (w(), x()), c, d;
w();
var b = x(), c, d;
for (y(); 0;) z(); for (y(); 0;) z();
} }
} }

View File

@@ -205,8 +205,8 @@ describe("sourcemaps", function() {
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, [ assert.strictEqual(result.code, [
"var Foo=function(){console.log(3)};new Foo;var bar=function(o){return o};", "var Foo=function(){console.log(3)},bar=(new Foo,function(o){return o});",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElBQVMsSUFBSUYsSUNBbkQsSUFBSUcsSUFDQSxTQUFjQSxHQUNWLE9BQU9BIn0=", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElDQWxDQyxLREEyQyxJQUFJSCxJQ0MvQyxTQUFjRyxHQUNWLE9BQU9BIn0=",
].join("\n")); ].join("\n"));
assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]); assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]);
}); });