improve handling of non-trivial assignment values (#5227)
This commit is contained in:
335
lib/compress.js
335
lib/compress.js
@@ -1397,9 +1397,7 @@ Compressor.prototype.compress = function(node) {
|
||||
operator: "+",
|
||||
expression: make_ref(exp, fixed)
|
||||
}),
|
||||
right: make_node(AST_Number, node, {
|
||||
value: 1
|
||||
})
|
||||
right: make_node(AST_Number, node, { value: 1 }),
|
||||
});
|
||||
};
|
||||
d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
|
||||
@@ -1410,7 +1408,7 @@ Compressor.prototype.compress = function(node) {
|
||||
exp.fixed = function() {
|
||||
return make_node(AST_UnaryPrefix, node, {
|
||||
operator: "+",
|
||||
expression: make_ref(exp, fixed)
|
||||
expression: make_ref(exp, fixed),
|
||||
});
|
||||
};
|
||||
exp.fixed.assigns = fixed && fixed.assigns;
|
||||
@@ -1501,11 +1499,11 @@ Compressor.prototype.compress = function(node) {
|
||||
var fixed = this.definition().fixed;
|
||||
if (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;
|
||||
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;
|
||||
});
|
||||
|
||||
@@ -1699,7 +1697,7 @@ Compressor.prototype.compress = function(node) {
|
||||
|
||||
function merge_sequence(array, node) {
|
||||
if (node instanceof AST_Sequence) {
|
||||
array.push.apply(array, node.expressions);
|
||||
[].push.apply(array, node.expressions);
|
||||
} else {
|
||||
array.push(node);
|
||||
}
|
||||
@@ -1813,6 +1811,31 @@ Compressor.prototype.compress = function(node) {
|
||||
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) {
|
||||
var in_lambda = last_of(compressor, function(node) {
|
||||
return node instanceof AST_Lambda;
|
||||
@@ -1972,12 +1995,12 @@ Compressor.prototype.compress = function(node) {
|
||||
var def = candidate.name.definition();
|
||||
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
|
||||
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, {
|
||||
operator: "=",
|
||||
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
|
||||
right: candidate.value,
|
||||
right: rvalue,
|
||||
});
|
||||
}
|
||||
var assign = candidate;
|
||||
@@ -1986,7 +2009,9 @@ Compressor.prototype.compress = function(node) {
|
||||
if (!(assign instanceof AST_Assign)) break;
|
||||
assign = assign.right;
|
||||
}
|
||||
return candidate;
|
||||
assign = candidate.clone();
|
||||
assign.right = rvalue;
|
||||
return assign;
|
||||
}
|
||||
// These node types have child nodes that execute sequentially,
|
||||
// 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
|
||||
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) {
|
||||
// Treat parameters as collapsible in IIFE, i.e.
|
||||
// function(a, b){ ... }(x());
|
||||
@@ -2122,7 +2149,14 @@ Compressor.prototype.compress = function(node) {
|
||||
var well_defined = true;
|
||||
var lvalues = get_lvalues(candidate);
|
||||
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();
|
||||
var check_destructured = in_try || !lhs_local ? function(node) {
|
||||
return node instanceof AST_Destructured;
|
||||
@@ -2163,7 +2197,7 @@ Compressor.prototype.compress = function(node) {
|
||||
value_def.single_use = false;
|
||||
changed = true;
|
||||
}
|
||||
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
|
||||
if (replaced) remove_candidate(candidate);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
@@ -2768,6 +2802,7 @@ Compressor.prototype.compress = function(node) {
|
||||
return;
|
||||
}
|
||||
if (remaining < 1) return;
|
||||
rhs = rhs.tail_node();
|
||||
var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
|
||||
if (!(value instanceof AST_SymbolRef)) return;
|
||||
var def = value.definition();
|
||||
@@ -3002,24 +3037,21 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
|
||||
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;
|
||||
if (index >= 0) {
|
||||
var argname = scope.argnames[index];
|
||||
if (argname instanceof AST_DefaultValue) {
|
||||
argname.value = make_node(AST_Number, argname, {
|
||||
value: 0
|
||||
});
|
||||
argname.value = value || make_node(AST_Number, argname, { value: 0 });
|
||||
argname.name.definition().fixed = false;
|
||||
} else {
|
||||
var args = compressor.parent().args;
|
||||
if (args[index]) {
|
||||
args[index] = make_node(AST_Number, args[index], {
|
||||
value: 0
|
||||
});
|
||||
args[index] = value || make_node(AST_Number, args[index], { value: 0 });
|
||||
argname.definition().fixed = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
var end = hit_stack.length - 1;
|
||||
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++;
|
||||
node = node.clone();
|
||||
node.value = null;
|
||||
return node;
|
||||
return value ? List.splice([ value, node ]) : node;
|
||||
}
|
||||
return in_list ? List.skip : null;
|
||||
}, patch_sequence);
|
||||
if (!value) return in_list ? List.skip : null;
|
||||
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;
|
||||
hit = false;
|
||||
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) {
|
||||
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) {
|
||||
stat.condition = join_assigns_expr(stat.condition);
|
||||
} else if (stat instanceof AST_SimpleStatement) {
|
||||
var exprs = join_assigns(prev, stat.body);
|
||||
var exprs = join_assigns(prev, stat.body), next;
|
||||
if (exprs) {
|
||||
changed = true;
|
||||
if (!exprs.length) continue;
|
||||
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) {
|
||||
stat.expression = join_assigns_expr(stat.expression);
|
||||
@@ -6690,7 +6755,7 @@ Compressor.prototype.compress = function(node) {
|
||||
value = null;
|
||||
trim_defns.push(def);
|
||||
}
|
||||
var old_def;
|
||||
var old_def, fn;
|
||||
if (!value && !(node instanceof AST_Let)) {
|
||||
if (parent instanceof AST_ExportDeclaration) {
|
||||
flush();
|
||||
@@ -6704,17 +6769,18 @@ Compressor.prototype.compress = function(node) {
|
||||
} else if (compressor.option("functions")
|
||||
&& !compressor.option("ie")
|
||||
&& drop_sym
|
||||
&& value
|
||||
&& var_defs[sym.id] == 1
|
||||
&& sym.assignments == 0
|
||||
&& value instanceof AST_LambdaExpression
|
||||
&& (fn = value.tail_node()) instanceof AST_LambdaExpression
|
||||
&& !is_arguments(sym)
|
||||
&& !is_arrow(value)
|
||||
&& assigned_once(value, sym.references)
|
||||
&& can_declare_defun(value)
|
||||
&& (old_def = rename_def(value, def.name.name)) !== false) {
|
||||
&& !is_arrow(fn)
|
||||
&& assigned_once(fn, sym.references)
|
||||
&& can_declare_defun(fn)
|
||||
&& (old_def = rename_def(fn, def.name.name)) !== false) {
|
||||
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
|
||||
var ctor;
|
||||
switch (value.CTOR) {
|
||||
switch (fn.CTOR) {
|
||||
case AST_AsyncFunction:
|
||||
ctor = AST_AsyncDefun;
|
||||
break;
|
||||
@@ -6728,7 +6794,7 @@ Compressor.prototype.compress = function(node) {
|
||||
ctor = AST_GeneratorDefun;
|
||||
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);
|
||||
var name_def = def.name.scope.resolve().def_function(defun.name);
|
||||
if (old_def) old_def.forEach(function(node) {
|
||||
@@ -6737,6 +6803,7 @@ Compressor.prototype.compress = function(node) {
|
||||
node.reference();
|
||||
});
|
||||
body.push(defun);
|
||||
if (value !== fn) [].push.apply(side_effects, value.expressions.slice(0, -1));
|
||||
} else {
|
||||
if (drop_sym
|
||||
&& var_defs[sym.id] > 1
|
||||
@@ -6759,7 +6826,7 @@ Compressor.prototype.compress = function(node) {
|
||||
head.push(def);
|
||||
}
|
||||
} else {
|
||||
value = value && !value.single_use && value.drop_side_effect_free(compressor);
|
||||
value = value && value.drop_side_effect_free(compressor);
|
||||
if (value) {
|
||||
AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
side_effects.push(value);
|
||||
@@ -6867,13 +6934,18 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
}
|
||||
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);
|
||||
body.push(node);
|
||||
}
|
||||
if (side_effects.length > 0) {
|
||||
body.push(make_node(AST_SimpleStatement, node, {
|
||||
body: make_sequence(node, side_effects)
|
||||
}));
|
||||
body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) }));
|
||||
}
|
||||
return insert_statements(body, node, in_list);
|
||||
}
|
||||
@@ -6922,33 +6994,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
}, function(node, 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:
|
||||
// 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_For) return patch_for_init(node, in_list);
|
||||
if (node instanceof AST_ForIn) {
|
||||
if (!drop_vars || !compressor.option("loops")) return;
|
||||
if (!is_empty(node.body)) return;
|
||||
@@ -8749,15 +8795,21 @@ Compressor.prototype.compress = function(node) {
|
||||
var body = [], var_defs = [], refs = [];
|
||||
var body_exprs = sequencesize(self.body, 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 (body_exprs.length == 0) {
|
||||
body.push(make_node(AST_SimpleStatement, self.condition, {
|
||||
body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
|
||||
operator : "||",
|
||||
left : self.condition,
|
||||
right : make_sequence(self.alternative, alt_exprs)
|
||||
}).transform(compressor) : self.condition.clone()
|
||||
operator: "||",
|
||||
left: self.condition,
|
||||
right: make_sequence(self.alternative, alt_exprs),
|
||||
}).transform(compressor) : self.condition.clone(),
|
||||
}).optimize(compressor));
|
||||
} else if (alt_exprs.length == 0) {
|
||||
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: make_node(AST_Binary, self, {
|
||||
operator : negated_is_best ? "||" : "&&",
|
||||
left : negated_is_best ? negated : self.condition,
|
||||
right : make_sequence(self.body, body_exprs)
|
||||
}).transform(compressor)
|
||||
operator: negated_is_best ? "||" : "&&",
|
||||
left: negated_is_best ? negated : self.condition,
|
||||
right: make_sequence(self.body, body_exprs),
|
||||
}).transform(compressor),
|
||||
}).optimize(compressor));
|
||||
} else {
|
||||
body.push(make_node(AST_SimpleStatement, self, {
|
||||
body: make_node(AST_Conditional, self, {
|
||||
condition : self.condition,
|
||||
consequent : make_sequence(self.body, body_exprs),
|
||||
alternative : make_sequence(self.alternative, alt_exprs)
|
||||
})
|
||||
condition: self.condition,
|
||||
consequent: make_sequence(self.body, body_exprs),
|
||||
alternative: make_sequence(self.alternative, alt_exprs),
|
||||
}),
|
||||
}).optimize(compressor));
|
||||
}
|
||||
refs.forEach(function(ref) {
|
||||
ref.definition().references.push(ref);
|
||||
});
|
||||
return make_node(AST_BlockStatement, self, {
|
||||
body: body
|
||||
}).optimize(compressor);
|
||||
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
|
||||
}
|
||||
if (is_empty(self.body)) {
|
||||
self = make_node(AST_If, self, {
|
||||
condition: negated,
|
||||
body: self.alternative,
|
||||
alternative: null
|
||||
});
|
||||
}
|
||||
if (self.body instanceof AST_Exit
|
||||
&& self.alternative instanceof AST_Exit
|
||||
&& self.body.TYPE == self.alternative.TYPE) {
|
||||
if (is_empty(self.body)) self = make_node(AST_If, self, {
|
||||
condition: negated,
|
||||
body: self.alternative,
|
||||
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)
|
||||
})
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) {
|
||||
self = make_node(AST_If, self, {
|
||||
condition: make_node(AST_Binary, self.condition, {
|
||||
operator: "&&",
|
||||
left: self.condition,
|
||||
right: self.body.condition
|
||||
right: self.body.condition,
|
||||
}),
|
||||
body: self.body.body,
|
||||
alternative: null
|
||||
alternative: null,
|
||||
});
|
||||
}
|
||||
if (aborts(self.body)) {
|
||||
if (self.alternative) {
|
||||
var alt = self.alternative;
|
||||
self.alternative = null;
|
||||
return make_node(AST_BlockStatement, self, {
|
||||
body: [ self, alt ]
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (aborts(self.body) && self.alternative) {
|
||||
var alt = self.alternative;
|
||||
self.alternative = null;
|
||||
return make_node(AST_BlockStatement, self, { body: [ self, alt ] }).optimize(compressor);
|
||||
}
|
||||
if (aborts(self.alternative)) {
|
||||
var body = self.body;
|
||||
self.body = self.alternative;
|
||||
self.condition = negated_is_best ? negated : self.condition.negate(compressor);
|
||||
self.alternative = null;
|
||||
return make_node(AST_BlockStatement, self, {
|
||||
body: [ self, body ]
|
||||
}).optimize(compressor);
|
||||
return make_node(AST_BlockStatement, self, { body: [ self, body ] }).optimize(compressor);
|
||||
}
|
||||
if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
|
||||
return self;
|
||||
@@ -8852,10 +8889,19 @@ Compressor.prototype.compress = function(node) {
|
||||
var exprs = [];
|
||||
for (var i = 0; i < stat.body.length; 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) {
|
||||
defuns.push(line);
|
||||
} else if (line instanceof AST_EmptyStatement) {
|
||||
continue;
|
||||
} else if (line instanceof AST_SimpleStatement) {
|
||||
if (!compressor.option("sequences") && exprs.length > 0) return;
|
||||
exprs.push(line.body);
|
||||
@@ -9320,9 +9366,7 @@ Compressor.prototype.compress = function(node) {
|
||||
args[pos++] = make_sequence(call, side_effects);
|
||||
side_effects = [];
|
||||
} else {
|
||||
args[pos++] = make_node(AST_Number, args[i], {
|
||||
value: 0
|
||||
});
|
||||
args[pos++] = make_node(AST_Number, args[i], { value: 0 });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -9924,10 +9968,14 @@ Compressor.prototype.compress = function(node) {
|
||||
for (var i = 0; i < len; i++) {
|
||||
var line = fn.body[i];
|
||||
if (line instanceof AST_Var) {
|
||||
var assigned = var_assigned || !declarations_only(line);
|
||||
if (assigned) {
|
||||
if (var_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;
|
||||
if (stat) return false;
|
||||
}
|
||||
} else if (line instanceof AST_AsyncDefun
|
||||
|| line instanceof AST_Defun
|
||||
@@ -10191,7 +10239,7 @@ Compressor.prototype.compress = function(node) {
|
||||
|
||||
function flatten_vars(decls, expressions) {
|
||||
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++) {
|
||||
var stat = fn.body[i];
|
||||
if (stat instanceof AST_LambdaDefinition) {
|
||||
@@ -10209,11 +10257,20 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
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++) {
|
||||
var var_def = stat.definitions[j];
|
||||
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)) {
|
||||
var def = fn.variables.get(name.name);
|
||||
var sym = make_node(AST_SymbolRef, name, name);
|
||||
@@ -12211,9 +12268,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
|
||||
function pop_seq(node) {
|
||||
if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
|
||||
value: 0
|
||||
});
|
||||
if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { value: 0 });
|
||||
return make_sequence(node, node.expressions.slice(0, -1));
|
||||
}
|
||||
});
|
||||
@@ -12526,35 +12581,25 @@ Compressor.prototype.compress = function(node) {
|
||||
var exp = self.expression.expression;
|
||||
if (is_undeclared_ref(exp)) switch (exp.name) {
|
||||
case "Array":
|
||||
self.expression = make_node(AST_Array, self.expression, {
|
||||
elements: []
|
||||
});
|
||||
self.expression = make_node(AST_Array, self.expression, { elements: [] });
|
||||
break;
|
||||
case "Function":
|
||||
self.expression = make_node(AST_Function, self.expression, {
|
||||
argnames: [],
|
||||
body: []
|
||||
body: [],
|
||||
}).init_vars(exp.scope);
|
||||
break;
|
||||
case "Number":
|
||||
self.expression = make_node(AST_Number, self.expression, {
|
||||
value: 0
|
||||
});
|
||||
self.expression = make_node(AST_Number, self.expression, { value: 0 });
|
||||
break;
|
||||
case "Object":
|
||||
self.expression = make_node(AST_Object, self.expression, {
|
||||
properties: []
|
||||
});
|
||||
self.expression = make_node(AST_Object, self.expression, { properties: [] });
|
||||
break;
|
||||
case "RegExp":
|
||||
self.expression = make_node(AST_RegExp, self.expression, {
|
||||
value: /t/
|
||||
});
|
||||
self.expression = make_node(AST_RegExp, self.expression, { value: /t/ });
|
||||
break;
|
||||
case "String":
|
||||
self.expression = make_node(AST_String, self.expression, {
|
||||
value: ""
|
||||
});
|
||||
self.expression = make_node(AST_String, self.expression, { value: "" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +489,7 @@ logical_assignments: {
|
||||
node_version: ">=15"
|
||||
}
|
||||
|
||||
logical_collapse_vars: {
|
||||
logical_collapse_vars_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
@@ -509,6 +509,27 @@ logical_collapse_vars: {
|
||||
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: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
|
||||
@@ -346,9 +346,7 @@ collapse_vars_if: {
|
||||
return "x" != "Bar" + x / 4 ? g9 : g5;
|
||||
}
|
||||
function f3(x) {
|
||||
if (x)
|
||||
return 1;
|
||||
return 2;
|
||||
return x ? 1 : 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: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
@@ -2667,6 +2701,24 @@ chained_4: {
|
||||
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: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
@@ -2906,6 +2958,43 @@ compound_assignment_2: {
|
||||
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: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
@@ -3030,10 +3119,10 @@ issue_2203_2: {
|
||||
a: "FAIL",
|
||||
b: function() {
|
||||
return function(c) {
|
||||
return (String, (Object, function() {
|
||||
return (Object, function() {
|
||||
return this;
|
||||
}())).a;
|
||||
}();
|
||||
}()).a;
|
||||
}(String);
|
||||
}
|
||||
}.b());
|
||||
}
|
||||
@@ -3764,17 +3853,17 @@ issue_2437_1: {
|
||||
console.log(foo());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
if (xhrDesc) {
|
||||
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
|
||||
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
|
||||
result;
|
||||
}
|
||||
var req = new XMLHttpRequest(), detectFunc = function(){};
|
||||
return req.onreadystatechange = detectFunc,
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
|
||||
req.onreadystatechange = null, result;
|
||||
}());
|
||||
var req, detectFunc, result;
|
||||
console.log((
|
||||
xhrDesc ? (result = !!(req = new XMLHttpRequest).onreadystatechange,
|
||||
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc||{})
|
||||
) : (
|
||||
(req = new XMLHttpRequest).onreadystatechange = detectFunc = function(){},
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
|
||||
req.onreadystatechange = null
|
||||
),
|
||||
result
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3815,15 +3904,15 @@ issue_2437_2: {
|
||||
foo();
|
||||
}
|
||||
expect: {
|
||||
!function() {
|
||||
if (xhrDesc)
|
||||
return (req = new XMLHttpRequest()).onreadystatechange,
|
||||
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {});
|
||||
var req = new XMLHttpRequest();
|
||||
req.onreadystatechange = function(){},
|
||||
var req;
|
||||
xhrDesc ? (
|
||||
(req = new XMLHttpRequest).onreadystatechange,
|
||||
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {})
|
||||
) : (
|
||||
(req = new XMLHttpRequest).onreadystatechange = function(){},
|
||||
req[SYMBOL_FAKE_ONREADYSTATECHANGE_1],
|
||||
req.onreadystatechange = null;
|
||||
}();
|
||||
req.onreadystatechange = null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6445,7 +6534,8 @@ assign_undeclared: {
|
||||
console.log(typeof B);
|
||||
}
|
||||
expect: {
|
||||
B = new (console.log(42), function() {})();
|
||||
console.log(42);
|
||||
B = new function() {}();
|
||||
console.log(typeof B);
|
||||
}
|
||||
expect_stdout: [
|
||||
@@ -9141,9 +9231,7 @@ issue_4908: {
|
||||
console.log(d[1]);
|
||||
}
|
||||
expect: {
|
||||
var a = 0, b;
|
||||
console || a++;
|
||||
var c = a, d = [ (d = a) && d, d += 42 ];
|
||||
var a = 0, b, c = (console || a++, a), d = [ (d = a) && d, d += 42 ];
|
||||
console.log(d[1]);
|
||||
}
|
||||
expect_stdout: "42"
|
||||
|
||||
@@ -158,6 +158,28 @@ process_boolean_returns: {
|
||||
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: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
|
||||
@@ -395,7 +395,8 @@ funarg_unused_6_keep_fargs: {
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var {} = (console, 42);
|
||||
console;
|
||||
var {} = 42;
|
||||
})();
|
||||
console.log(typeof a);
|
||||
}
|
||||
|
||||
@@ -1728,7 +1728,8 @@ issue_2768: {
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL";
|
||||
var c = (d = a, void (d && (a = "PASS")));
|
||||
d = a;
|
||||
var c = void (d && (a = "PASS"));
|
||||
var d;
|
||||
console.log(a, typeof c);
|
||||
}
|
||||
@@ -2382,7 +2383,8 @@ issue_3664: {
|
||||
}
|
||||
expect: {
|
||||
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";
|
||||
}());
|
||||
}
|
||||
@@ -2551,10 +2553,9 @@ issue_3899: {
|
||||
console.log(typeof a);
|
||||
}
|
||||
expect: {
|
||||
function a() {
|
||||
console.log(typeof function() {
|
||||
return 2;
|
||||
}
|
||||
console.log(typeof a);
|
||||
});
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ issue_4664: {
|
||||
expect: {
|
||||
(function f() {
|
||||
new function(a) {
|
||||
console.log(typeof f, a, typeof this);
|
||||
}((A = 0, 2 ** 30));
|
||||
console.log(typeof f, 2 ** 30, typeof this);
|
||||
}(A = 0);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "function 1073741824 object"
|
||||
|
||||
@@ -3039,18 +3039,17 @@ issue_2437: {
|
||||
console.log(foo());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
if (xhrDesc) {
|
||||
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
|
||||
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
|
||||
result;
|
||||
}
|
||||
function detectFunc() {}
|
||||
var req = new XMLHttpRequest();
|
||||
return req.onreadystatechange = detectFunc,
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
|
||||
req.onreadystatechange = null, result;
|
||||
}());
|
||||
var req, detectFunc, result;
|
||||
console.log((
|
||||
xhrDesc ? (
|
||||
result = !!(req = new XMLHttpRequest).onreadystatechange,
|
||||
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {})
|
||||
) : (
|
||||
(req = new XMLHttpRequest).onreadystatechange = detectFunc = function(){},
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,req.onreadystatechange = null
|
||||
),
|
||||
result
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3826,16 +3825,14 @@ inlined_single_use: {
|
||||
}
|
||||
expect: {
|
||||
console.log(function(f) {
|
||||
var a = function() {
|
||||
a = function() {
|
||||
A;
|
||||
};
|
||||
var b = function() {
|
||||
},
|
||||
b = function() {
|
||||
a(B);
|
||||
};
|
||||
(function() {
|
||||
b;
|
||||
});
|
||||
return;
|
||||
},
|
||||
void 0;
|
||||
var a, b;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
@@ -5290,6 +5287,42 @@ direct_inline_catch_redefined: {
|
||||
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: {
|
||||
options = {
|
||||
functions: true,
|
||||
|
||||
@@ -282,7 +282,7 @@ issue_4736: {
|
||||
(function() {
|
||||
(function() {
|
||||
0,
|
||||
console.log(1073741824);
|
||||
console.log(1 << 30);
|
||||
})();
|
||||
})();
|
||||
}
|
||||
@@ -336,7 +336,7 @@ issue_4859: {
|
||||
}
|
||||
expect: {
|
||||
(function f(a) {
|
||||
console.log(Infinity);
|
||||
console.log(2 + 1 / 0);
|
||||
return f;
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -1282,10 +1282,7 @@ assign_sequence_var: {
|
||||
console.log(a, b, c);
|
||||
}
|
||||
expect: {
|
||||
var a = 0, b = 1;
|
||||
console.log(a),
|
||||
a++;
|
||||
var b = 2, c = 3;
|
||||
var a = 0, b = 1, c = (console.log(a), a++, b = 2, 3);
|
||||
console.log(a, b, c);
|
||||
}
|
||||
expect_stdout: [
|
||||
|
||||
@@ -181,10 +181,10 @@ issue_2203_2: {
|
||||
a: "FAIL",
|
||||
b: function() {
|
||||
return function() {
|
||||
return (String, (Object, function() {
|
||||
return (Object, function() {
|
||||
return this;
|
||||
}())).a;
|
||||
}();
|
||||
}()).a;
|
||||
}(String);
|
||||
}
|
||||
}.b());
|
||||
}
|
||||
|
||||
@@ -910,9 +910,7 @@ hoist_decl: {
|
||||
var d;
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
w();
|
||||
var b = x(), c, d;
|
||||
var a, b = (w(), x()), c, d;
|
||||
for (y(); 0;) z();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +205,8 @@ describe("sourcemaps", function() {
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
assert.strictEqual(result.code, [
|
||||
"var Foo=function(){console.log(3)};new Foo;var bar=function(o){return o};",
|
||||
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElBQVMsSUFBSUYsSUNBbkQsSUFBSUcsSUFDQSxTQUFjQSxHQUNWLE9BQU9BIn0=",
|
||||
"var Foo=function(){console.log(3)},bar=(new Foo,function(o){return o});",
|
||||
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElDQWxDQyxLREEyQyxJQUFJSCxJQ0MvQyxTQUFjRyxHQUNWLE9BQU9BIn0=",
|
||||
].join("\n"));
|
||||
assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user