Compare commits

..

24 Commits

Author SHA1 Message Date
Alex Lam S.L
2441827408 v3.2.2 2017-12-10 13:46:17 +08:00
Alex Lam S.L
0aff037a35 improve unused on assign-only symbols (#2568) 2017-12-09 06:19:29 +08:00
Alex Lam S.L
74a2f53683 fix escape analysis for AST_Throw (#2564) 2017-12-08 02:54:37 +08:00
Alex Lam S.L
e20935c3f2 fix escape analysis for AST_Conditional & AST_Sequence (#2563)
fixes #2560
2017-12-08 01:50:38 +08:00
Alex Lam S.L
3e34f62a1c account for side-effects in conditional call inversion (#2562)
fixes #2560
2017-12-08 01:15:31 +08:00
Alex Lam S.L
d21cb84696 eliminate noop calls more aggressively (#2559) 2017-12-07 01:22:08 +08:00
Alex Lam S.L
3dd495ecdd improve if_return (#2558)
`return void x()` => `x()`
2017-12-07 01:01:52 +08:00
Alex Lam S.L
b9f3ddfb30 v3.2.1 2017-12-03 11:39:51 +08:00
Alex Lam S.L
77332a0315 fix dead_code on for (#2552) 2017-12-02 15:46:05 +08:00
Alex Lam S.L
85c56adbd1 more tests for #2535 (#2551) 2017-12-02 02:26:56 +08:00
Alex Lam S.L
8da3754e51 improve evaluate on typeof (#2550)
- gated through `typeofs`
2017-12-02 02:18:33 +08:00
Alex Lam S.L
9a6b11f8e6 improve boolean compression (#2548)
fixes #2535
2017-12-01 22:41:35 +08:00
Alex Lam S.L
7ac6fdcc99 improve switch case compression (#2547) 2017-12-01 14:32:00 +08:00
Alex Lam S.L
f6610baaa8 improve AST_For.init & AST_Switch.expression compression (#2546) 2017-12-01 12:53:59 +08:00
Alex Lam S.L
09b320e8a5 convert to number under boolean context (#2545) 2017-12-01 12:52:36 +08:00
Alex Lam S.L
5a1e99d713 improve compression of if conditions (#2544) 2017-12-01 06:18:31 +08:00
Alex Lam S.L
b762f2d6f4 improve compression of loop conditions (#2543) 2017-12-01 05:52:33 +08:00
Alex Lam S.L
172079a47f improve code reuse (#2542) 2017-12-01 03:40:46 +08:00
Alex Lam S.L
c58d3936a3 fix corner case in call binding (#2541) 2017-12-01 03:18:20 +08:00
Alex Lam S.L
18302bf8e9 backport test from #2526 (#2534) 2017-11-29 13:32:00 +08:00
Alex Lam S.L
bc5047c1e7 fix inline on nested substitutions (#2533)
fixes #2531
2017-11-29 13:31:41 +08:00
Alex Lam S.L
206a54a746 fix nested hoist_props substitution (#2523)
fixes #2519
2017-11-28 14:39:00 +08:00
Alex Lam S.L
32def5ebf5 improve synergy between collapse_vars & unused (#2521) 2017-11-28 14:02:39 +08:00
Alex Lam S.L
ecc9f6b770 drop assignment in AST_VarDef.value (#2522)
fixes #2516
2017-11-28 13:08:40 +08:00
27 changed files with 1492 additions and 376 deletions

View File

@@ -898,24 +898,6 @@ TreeWalker.prototype = {
}
}
},
in_boolean_context: function() {
var stack = this.stack;
var i = stack.length, self = stack[--i];
while (i > 0) {
var p = stack[--i];
if ((p instanceof AST_If && p.condition === self) ||
(p instanceof AST_Conditional && p.condition === self) ||
(p instanceof AST_DWLoop && p.condition === self) ||
(p instanceof AST_For && p.condition === self) ||
(p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self))
{
return true;
}
if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")))
return false;
self = p;
}
},
loopcontrol_target: function(node) {
var stack = this.stack;
if (node.label) for (var i = stack.length; --i >= 0;) {

View File

@@ -145,6 +145,27 @@ merge(Compressor.prototype, {
return true;
return false;
},
in_boolean_context: function() {
if (!this.option("booleans")) return false;
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
},
compress: function(node) {
if (this.option("expression")) {
node.process_expression(true);
@@ -631,11 +652,13 @@ merge(Compressor.prototype, {
}
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped = true;
return;
} else if (parent instanceof AST_Array) {
} else if (parent instanceof AST_Array
|| parent instanceof AST_Conditional && node !== parent.condition
|| parent instanceof AST_Sequence && node === parent.tail_node()) {
mark_escaped(d, scope, parent, parent, level + 1);
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1);
@@ -701,7 +724,7 @@ merge(Compressor.prototype, {
function make_sequence(orig, expressions) {
if (expressions.length == 1) return expressions[0];
return make_node(AST_Sequence, orig, {
expressions: expressions
expressions: expressions.reduce(merge_sequence, [])
});
}
@@ -758,6 +781,7 @@ merge(Compressor.prototype, {
} else {
array.push(node);
}
return array;
}
function as_statement_array(thing) {
@@ -800,14 +824,6 @@ merge(Compressor.prototype, {
|| compressor.option("unsafe") && global_names(this.name);
});
function drop_decl(def) {
def.eliminated++;
if (def.orig.length == def.eliminated) {
def.scope.functions.del(def.name);
def.scope.variables.del(def.name);
}
}
function is_identifier_atom(node) {
return node instanceof AST_Infinity
|| node instanceof AST_NaN
@@ -1088,6 +1104,7 @@ merge(Compressor.prototype, {
function get_lhs(expr) {
if (expr instanceof AST_VarDef) {
var def = expr.name.definition();
if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
@@ -1137,7 +1154,6 @@ merge(Compressor.prototype, {
if (node === expr || node.body === expr) {
found = true;
if (node instanceof AST_VarDef) {
drop_decl(node.name.definition());
node.value = null;
return node;
}
@@ -1197,11 +1213,20 @@ merge(Compressor.prototype, {
var stat = statements[i];
var next = statements[i + 1];
if (in_lambda && stat instanceof AST_Return && !stat.value && !next) {
if (in_lambda && !next && stat instanceof AST_Return) {
if (!stat.value) {
CHANGED = true;
statements.length--;
continue;
}
if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
CHANGED = true;
statements[i] = make_node(AST_SimpleStatement, stat, {
body: stat.value.expression
});
continue;
}
}
if (stat instanceof AST_If) {
var ab = aborts(stat.body);
@@ -1287,9 +1312,16 @@ merge(Compressor.prototype, {
&& prev instanceof AST_If && prev.body instanceof AST_Return
&& i + 2 == statements.length && next instanceof AST_SimpleStatement) {
CHANGED = true;
statements.push(make_node(AST_Return, next, {
stat = stat.clone();
stat.alternative = make_node(AST_BlockStatement, next, {
body: [
next,
make_node(AST_Return, next, {
value: null
}).transform(compressor));
})
]
});
statements.splice(i, 2, stat.transform(compressor));
continue;
}
}
@@ -1405,13 +1437,7 @@ merge(Compressor.prototype, {
function cons_seq(right) {
n--;
var left = prev.body;
if (!(left instanceof AST_Sequence)) {
left = make_node(AST_Sequence, left, {
expressions: [ left ]
});
}
merge_sequence(left.expressions, right);
return left.transform(compressor);
return make_sequence(left, [ left, right ]).transform(compressor);
};
var n = 0, prev;
for (var i = 0, len = statements.length; i < len; i++) {
@@ -1429,7 +1455,7 @@ merge(Compressor.prototype, {
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body.drop_side_effect_free(compressor);
stat.init = prev.body;
n--;
}
}
@@ -1560,7 +1586,7 @@ merge(Compressor.prototype, {
|| this.alternative._dot_throw(compressor);
})
def(AST_Sequence, function(compressor) {
return this.expressions[this.expressions.length - 1]._dot_throw(compressor);
return this.tail_node()._dot_throw(compressor);
});
def(AST_SymbolRef, function(compressor) {
if (this.is_undefined) return true;
@@ -1597,7 +1623,7 @@ merge(Compressor.prototype, {
return this.operator == "=" && this.right.is_boolean();
});
def(AST_Sequence, function(){
return this.expressions[this.expressions.length - 1].is_boolean();
return this.tail_node().is_boolean();
});
def(AST_True, return_true);
def(AST_False, return_true);
@@ -1624,7 +1650,7 @@ merge(Compressor.prototype, {
|| this.operator == "=" && this.right.is_number(compressor);
});
def(AST_Sequence, function(compressor){
return this.expressions[this.expressions.length - 1].is_number(compressor);
return this.tail_node().is_number(compressor);
});
def(AST_Conditional, function(compressor){
return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
@@ -1648,7 +1674,7 @@ merge(Compressor.prototype, {
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
});
def(AST_Sequence, function(compressor){
return this.expressions[this.expressions.length - 1].is_string(compressor);
return this.tail_node().is_string(compressor);
});
def(AST_Conditional, function(compressor){
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
@@ -1820,12 +1846,17 @@ merge(Compressor.prototype, {
return this;
});
def(AST_UnaryPrefix, function(compressor){
var e = this.expression;
// Function would be evaluated to an array and so typeof would
// incorrectly return 'object'. Hence making is a special case.
if (this.operator == "typeof" && this.expression instanceof AST_Function) {
if (compressor.option("typeofs")
&& this.operator == "typeof"
&& (e instanceof AST_Lambda
|| e instanceof AST_SymbolRef
&& e.fixed_value() instanceof AST_Lambda)) {
return typeof function(){};
}
var e = ev(this.expression, compressor);
e = ev(e, compressor);
if (e === this.expression) return this;
switch (this.operator) {
case "!": return !e;
@@ -1886,7 +1917,6 @@ merge(Compressor.prototype, {
return value === node ? this : value;
});
def(AST_SymbolRef, function(compressor){
if (!compressor.option("reduce_vars")) return this;
var fixed = this.fixed_value();
if (!fixed) return this;
this._eval = return_this;
@@ -2276,6 +2306,10 @@ merge(Compressor.prototype, {
self.walk(new TreeWalker(function(node) {
if (!result) return true;
if (node instanceof AST_SymbolRef) {
if (self.inlined) {
result = false;
return true;
}
var def = node.definition();
if (member(def, self.enclosed)
&& !self.variables.has(def.name)) {
@@ -2434,47 +2468,18 @@ merge(Compressor.prototype, {
});
return true;
}
var sym;
if (scope === self
&& (sym = assign_as_unused(node)) instanceof AST_SymbolRef
&& self.variables.get(sym.name) === sym.definition()) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
return scan_ref_scoped(node, descend);
});
self.walk(tw);
// pass 2: for every used symbol we need to walk its
// initialization code to figure out if it uses other
// symbols (that may not be in_use).
tw = new TreeWalker(scan_ref_scoped);
for (var i = 0; i < in_use.length; ++i) {
in_use[i].orig.forEach(function(decl){
// undeclared globals will be instanceof AST_SymbolRef
var init = initializations.get(decl.name);
if (init) init.forEach(function(init){
var tw = new TreeWalker(function(node){
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
});
init.walk(tw);
});
});
@@ -2482,6 +2487,20 @@ merge(Compressor.prototype, {
// pass 3: we should drop declarations not in_use
var tt = new TreeTransformer(
function before(node, descend, in_list) {
var parent = tt.parent();
if (drop_vars) {
var sym = assign_as_unused(node);
if (sym instanceof AST_SymbolRef
&& !(sym.definition().id in in_use_ids)) {
if (node instanceof AST_Assign) {
return maintain_this_binding(parent, node, node.right.transform(tt));
}
return make_node(AST_Number, node, {
value: 0
});
}
}
if (scope !== self) return;
if (node instanceof AST_Function
&& node.name
&& !compressor.option("keep_fnames")) {
@@ -2512,12 +2531,10 @@ merge(Compressor.prototype, {
var def = node.name.definition();
if (!(def.id in in_use_ids)) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
drop_decl(def);
def.eliminated++;
return make_node(AST_EmptyStatement, node);
}
return node;
}
var parent = tt.parent();
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
// place uninitialized names at the start
var body = [], head = [], tail = [];
@@ -2531,17 +2548,24 @@ merge(Compressor.prototype, {
if (!drop_vars || sym.id in in_use_ids) {
if (def.name instanceof AST_SymbolVar) {
var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && !def.value) {
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
if (def.value) {
side_effects.push(make_node(AST_Assign, def, {
operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name),
right: def.value
}));
}
remove(var_defs, def);
drop_decl(sym);
sym.eliminated++;
return;
}
}
if (def.value) {
if (side_effects.length > 0) {
if (tail.length > 0) {
merge_sequence(side_effects, def.value);
side_effects.push(def.value);
def.value = make_sequence(def.value, side_effects);
} else {
body.push(make_node(AST_SimpleStatement, node, {
@@ -2556,36 +2580,20 @@ merge(Compressor.prototype, {
}
} else if (sym.orig[0] instanceof AST_SymbolCatch) {
var value = def.value && def.value.drop_side_effect_free(compressor);
if (value) merge_sequence(side_effects, value);
if (value) side_effects.push(value);
def.value = null;
head.push(def);
} else {
var value = def.value && def.value.drop_side_effect_free(compressor);
if (value) {
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
merge_sequence(side_effects, value);
side_effects.push(value);
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
drop_decl(sym);
sym.eliminated++;
}
});
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
var var_defs = var_defs_by_id.get(tail[0].name.definition().id);
if (var_defs.length > 1) {
var def = tail.pop();
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
remove(var_defs, def);
side_effects.unshift(make_node(AST_Assign, def, {
operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name),
right: def.value
}));
def = def.name.definition();
drop_decl(def);
def.replaced--;
}
}
if (head.length > 0 || tail.length > 0) {
node.definitions = head.concat(tail);
body.push(node);
@@ -2606,18 +2614,6 @@ merge(Compressor.prototype, {
});
}
}
if (drop_vars) {
var sym = assign_as_unused(node);
if (sym instanceof AST_SymbolRef
&& !(sym.definition().id in in_use_ids)) {
if (node instanceof AST_Assign) {
return maintain_this_binding(parent, node, node.right.transform(tt));
}
return make_node(AST_Number, node, {
value: 0
});
}
}
// certain combination of unused name + side effect leads to:
// https://github.com/mishoo/UglifyJS2/issues/44
// https://github.com/mishoo/UglifyJS2/issues/1830
@@ -2649,8 +2645,13 @@ merge(Compressor.prototype, {
}
return node;
}
if (node instanceof AST_Scope && node !== self)
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend(node, this);
scope = save_scope;
return node;
}
function template(sym) {
return {
@@ -2663,6 +2664,30 @@ merge(Compressor.prototype, {
}
);
self.transform(tt);
function scan_ref_scoped(node, descend) {
var sym;
if ((sym = assign_as_unused(node)) instanceof AST_SymbolRef
&& self.variables.get(sym.name) === sym.definition()) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
}
});
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
@@ -2818,7 +2843,7 @@ merge(Compressor.prototype, {
if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return self;
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
var defs_by_id = Object.create(null);
return self.transform(new TreeTransformer(function(node) {
return self.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_VarDef) {
var sym = node.name, def, value;
if (sym.scope === self
@@ -2828,6 +2853,7 @@ merge(Compressor.prototype, {
&& !top_retain(def)
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
descend(node, this);
var defs = new Dictionary();
var assignments = [];
value.properties.forEach(function(prop) {
@@ -2884,7 +2910,7 @@ merge(Compressor.prototype, {
var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
changed |= node !== nodes[i];
if (node) {
merge_sequence(ret, node);
ret.push(node);
first_in_statement = false;
}
}
@@ -2995,11 +3021,11 @@ merge(Compressor.prototype, {
return make_sequence(this, [ expression, property ]);
});
def(AST_Sequence, function(compressor){
var last = this.expressions[this.expressions.length - 1];
var last = this.tail_node();
var expr = last.drop_side_effect_free(compressor);
if (expr === last) return this;
var expressions = this.expressions.slice(0, -1);
if (expr) merge_sequence(expressions, expr);
if (expr) expressions.push(expr);
return make_sequence(this, expressions);
});
})(function(node, func){
@@ -3021,21 +3047,24 @@ merge(Compressor.prototype, {
return self;
});
OPT(AST_DWLoop, function(self, compressor){
if (!compressor.option("loops")) return self;
var cond = self.condition.evaluate(compressor);
if (cond !== self.condition) {
if (cond) {
return make_node(AST_For, self, {
body: self.body
OPT(AST_While, function(self, compressor){
return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
});
}
if (compressor.option("dead_code") && self instanceof AST_While) {
var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
}
if (self instanceof AST_Do) {
OPT(AST_Do, function(self, compressor){
if (!compressor.option("loops")) return self;
var cond = self.condition.tail_node().evaluate(compressor);
if (!(cond instanceof AST_Node)) {
if (cond) return make_node(AST_For, self, {
body: make_node(AST_BlockStatement, self.body, {
body: [
self.body,
make_node(AST_SimpleStatement, self.condition, {
body: self.condition
})
]
})
}).optimize(compressor);
var has_loop_control = false;
var tw = new TreeWalker(function(node) {
if (node instanceof AST_Scope || has_loop_control) return true;
@@ -3044,11 +3073,14 @@ merge(Compressor.prototype, {
});
var parent = compressor.parent();
(parent instanceof AST_LabeledStatement ? parent : self).walk(tw);
if (!has_loop_control) return self.body;
}
}
if (self instanceof AST_While) {
return make_node(AST_For, self, self).optimize(compressor);
if (!has_loop_control) return make_node(AST_BlockStatement, self.body, {
body: [
self.body,
make_node(AST_SimpleStatement, self.condition, {
body: self.condition
})
]
}).optimize(compressor);
}
return self;
});
@@ -3100,24 +3132,36 @@ merge(Compressor.prototype, {
OPT(AST_For, function(self, compressor){
if (!compressor.option("loops")) return self;
if (compressor.option("side_effects") && self.init) {
self.init = self.init.drop_side_effect_free(compressor);
}
if (self.condition) {
var cond = self.condition.evaluate(compressor);
if (compressor.option("dead_code") && !cond) {
var a = [];
if (self.init instanceof AST_Statement) {
a.push(self.init);
if (!(cond instanceof AST_Node)) {
if (cond) self.condition = null;
else if (!compressor.option("dead_code")) {
var orig = self.condition;
self.condition = make_node_from_constant(cond, self.condition);
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
else if (self.init) {
a.push(make_node(AST_SimpleStatement, self.init, {
}
if (compressor.option("dead_code")) {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
if (!cond) {
var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body);
if (self.init instanceof AST_Statement) {
body.push(self.init);
} else if (self.init) {
body.push(make_node(AST_SimpleStatement, self.init, {
body: self.init
}));
}
extract_declarations_from_unreachable_code(compressor, self.body, a);
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}));
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
}
if (cond !== self.condition) {
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
}
}
if_break_in_loop(self, compressor);
@@ -3133,28 +3177,34 @@ merge(Compressor.prototype, {
// “has no side effects”; also it doesn't work for cases like
// `x && true`, though it probably should.
var cond = self.condition.evaluate(compressor);
if (cond !== self.condition) {
if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
if (!compressor.option("dead_code") && !(cond instanceof AST_Node)) {
var orig = self.condition;
self.condition = make_node_from_constant(cond, orig);
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
if (compressor.option("dead_code")) {
var a = [];
if (self.alternative) {
extract_declarations_from_unreachable_code(compressor, self.alternative, a);
}
a.push(self.body);
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
}
} else {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
if (compressor.option("dead_code")) {
var a = [];
extract_declarations_from_unreachable_code(compressor, self.body, a);
if (self.alternative) a.push(self.alternative);
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body);
body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}));
if (self.alternative) body.push(self.alternative);
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
} else if (!(cond instanceof AST_Node)) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
var body = [];
if (self.alternative) {
extract_declarations_from_unreachable_code(compressor, self.alternative, body);
}
body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}));
body.push(self.body);
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
}
cond = make_node_from_constant(cond, self.condition).transform(compressor);
self.condition = best_of_expression(cond, self.condition);
}
var negated = self.condition.negate(compressor);
var self_condition_length = self.condition.print_to_string().length;
@@ -3266,11 +3316,15 @@ merge(Compressor.prototype, {
if (!compressor.option("switches")) return self;
var branch;
var value = self.expression.evaluate(compressor);
if (value !== self.expression) {
var expression = make_node_from_constant(value, self.expression).transform(compressor);
self.expression = best_of_expression(expression, self.expression);
if (!(value instanceof AST_Node)) {
var orig = self.expression;
self.expression = make_node_from_constant(value, orig);
self.expression = best_of_expression(self.expression.transform(compressor), orig);
}
if (!compressor.option("dead_code")) return self;
if (value instanceof AST_Node) {
value = self.expression.tail_node().evaluate(compressor);
}
var decl = [];
var body = [];
var default_branch;
@@ -3283,8 +3337,13 @@ merge(Compressor.prototype, {
} else {
eliminate_branch(branch, body[body.length - 1]);
}
} else if (value !== self.expression) {
} else if (!(value instanceof AST_Node)) {
var exp = branch.expression.evaluate(compressor);
if (!(exp instanceof AST_Node) && exp !== value) {
eliminate_branch(branch, body[body.length - 1]);
continue;
}
if (exp instanceof AST_Node) exp = branch.expression.tail_node().evaluate(compressor);
if (exp === value) {
exact_match = branch;
if (default_branch) {
@@ -3293,9 +3352,6 @@ merge(Compressor.prototype, {
eliminate_branch(default_branch, body[default_index - 1]);
default_branch = null;
}
} else if (exp !== branch.expression) {
eliminate_branch(branch, body[body.length - 1]);
continue;
}
}
if (aborts(branch)) {
@@ -3338,12 +3394,16 @@ merge(Compressor.prototype, {
});
self.walk(tw);
if (!has_break) {
body = body[0].body.slice();
body.unshift(make_node(AST_SimpleStatement, self.expression, {
var statements = body[0].body.slice();
var exp = body[0].expression;
if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
body: exp
}));
statements.unshift(make_node(AST_SimpleStatement, self.expression, {
body:self.expression
}));
return make_node(AST_BlockStatement, self, {
body: body
body: statements
}).optimize(compressor);
}
}
@@ -3388,7 +3448,9 @@ merge(Compressor.prototype, {
}));
if (reduce_vars) name.definition().fixed = false;
}
drop_decl(def.name.definition());
def = def.name.definition();
def.eliminated++;
def.replaced--;
return a;
}, []);
if (assignments.length == 0) return null;
@@ -3404,11 +3466,11 @@ merge(Compressor.prototype, {
OPT(AST_Call, function(self, compressor){
var exp = self.expression;
var fn = exp;
if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
fn = fn.fixed_value();
}
if (compressor.option("unused")
&& (fn instanceof AST_Function
|| compressor.option("reduce_vars")
&& fn instanceof AST_SymbolRef
&& (fn = fn.fixed_value()) instanceof AST_Function)
&& fn instanceof AST_Function
&& !fn.uses_arguments
&& !fn.uses_eval) {
var pos = 0, last = 0;
@@ -3627,14 +3689,15 @@ merge(Compressor.prototype, {
return make_sequence(self, args).optimize(compressor);
}
}
if (exp instanceof AST_Function) {
if (fn instanceof AST_Function) {
if (compressor.option("inline")
&& !exp.name
&& !exp.uses_arguments
&& !exp.uses_eval
&& exp.body.length == 1
&& !exp.contains_this()
&& all(exp.argnames, function(arg) {
&& exp === fn
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& fn.body.length == 1
&& !fn.contains_this()
&& all(fn.argnames, function(arg) {
return arg.__unused;
})
&& !self.has_pure_annotation(compressor)) {
@@ -3652,7 +3715,7 @@ merge(Compressor.prototype, {
return make_sequence(self, args).optimize(compressor);
}
}
if (compressor.option("side_effects") && all(exp.body, is_empty)) {
if (compressor.option("side_effects") && all(fn.body, is_empty)) {
var args = self.args.concat(make_node(AST_Undefined, self));
return make_sequence(self, args).optimize(compressor);
}
@@ -3706,7 +3769,7 @@ merge(Compressor.prototype, {
trim_right_for_undefined();
if (end > 0 && compressor.option("cascade")) trim_left_for_assignment();
if (end == 0) {
self = maintain_this_binding(compressor.parent(), self, expressions[0]);
self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]);
if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
return self;
}
@@ -3845,7 +3908,7 @@ merge(Compressor.prototype, {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
if (compressor.in_boolean_context()) {
switch (self.operator) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
@@ -3998,7 +4061,7 @@ merge(Compressor.prototype, {
}
break;
}
if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
if (self.operator == "+" && compressor.in_boolean_context()) {
var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor);
if (ll && typeof ll == "string") {
@@ -4055,49 +4118,72 @@ merge(Compressor.prototype, {
if (compressor.option("evaluate")) {
switch (self.operator) {
case "&&":
var ll = self.left.evaluate(compressor);
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
if (!ll) {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.left).optimize(compressor);
} else if (ll !== self.left) {
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
} else if (!(ll instanceof AST_Node)) {
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.right).optimize(compressor);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
var rr = self.right.evaluate(compressor);
if (!rr) {
if (compressor.in_boolean_context()) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
make_node(AST_False, self)
]).optimize(compressor);
} else if (rr !== self.right) {
compressor.warn("Dropping side-effect-free && in boolean context [{file}:{line},{col}]", self.start);
} else self.falsy = true;
} else if (!(rr instanceof AST_Node)) {
var parent = compressor.parent();
if (parent.operator == "&&" && parent.left === compressor.self() || compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
}
}
// x || false && y ---> x ? y : false
if (self.left.operator == "||") {
var lr = self.left.right.evaluate(compressor);
if (!lr) return make_node(AST_Conditional, self, {
condition: self.left.left,
consequent: self.right,
alternative: self.left.right
}).optimize(compressor);
}
break;
case "||":
var ll = self.left.evaluate(compressor);
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
if (!ll) {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.right).optimize(compressor);
} else if (ll !== self.left) {
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
} else if (!(ll instanceof AST_Node)) {
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.left).optimize(compressor);
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
}
if (compressor.option("booleans") && compressor.in_boolean_context()) {
var rr = self.right.evaluate(compressor);
if (!rr) {
compressor.warn("Dropping side-effect-free || in boolean context [{file}:{line},{col}]", self.start);
var parent = compressor.parent();
if (parent.operator == "||" && parent.left === compressor.self() || compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
} else if (rr !== self.right) {
}
} else if (!(rr instanceof AST_Node)) {
if (compressor.in_boolean_context()) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
make_node(AST_True, self)
]).optimize(compressor);
} else self.truthy = true;
}
if (self.left.operator == "&&") {
var lr = self.left.right.evaluate(compressor);
if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
condition: self.left.left,
consequent: self.left.right,
alternative: self.right
}).optimize(compressor);
}
break;
}
@@ -4523,10 +4609,10 @@ merge(Compressor.prototype, {
if (cond !== self.condition) {
if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.consequent);
return maintain_this_binding(compressor.parent(), compressor.self(), self.consequent);
} else {
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), self, self.alternative);
return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative);
}
}
var negated = cond.negate(compressor, first_in_statement(compressor));
@@ -4572,18 +4658,22 @@ merge(Compressor.prototype, {
});
}
// x ? y(a) : y(b) --> y(x ? a : b)
var arg_index;
if (consequent instanceof AST_Call
&& alternative.TYPE === consequent.TYPE
&& consequent.args.length == 1
&& alternative.args.length == 1
&& consequent.args.length > 0
&& consequent.args.length == alternative.args.length
&& consequent.expression.equivalent_to(alternative.expression)
&& !consequent.expression.has_side_effects(compressor)) {
consequent.args[0] = make_node(AST_Conditional, self, {
&& !self.condition.has_side_effects(compressor)
&& !consequent.expression.has_side_effects(compressor)
&& typeof (arg_index = single_arg_diff()) == "number") {
var node = consequent.clone();
node.args[arg_index] = make_node(AST_Conditional, self, {
condition: self.condition,
consequent: consequent.args[0],
alternative: alternative.args[0]
consequent: consequent.args[arg_index],
alternative: alternative.args[arg_index]
});
return consequent;
return node;
}
// x?y?z:a:a --> x&&y?z:a
if (consequent instanceof AST_Conditional
@@ -4605,7 +4695,7 @@ merge(Compressor.prototype, {
consequent
]).optimize(compressor);
}
var in_bool = compressor.in_boolean_context();
if (is_true(self.consequent)) {
if (is_false(self.alternative)) {
// c ? true : false ---> !!c
@@ -4661,22 +4751,44 @@ merge(Compressor.prototype, {
// AST_True or !0
function is_true(node) {
return node instanceof AST_True
|| in_bool
&& node instanceof AST_Constant
&& node.getValue()
|| (node instanceof AST_UnaryPrefix
&& node.operator == "!"
&& node.expression instanceof AST_Constant
&& !node.expression.value);
&& !node.expression.getValue());
}
// AST_False or !1
function is_false(node) {
return node instanceof AST_False
|| in_bool
&& node instanceof AST_Constant
&& !node.getValue()
|| (node instanceof AST_UnaryPrefix
&& node.operator == "!"
&& node.expression instanceof AST_Constant
&& !!node.expression.value);
&& node.expression.getValue());
}
function single_arg_diff() {
var a = consequent.args;
var b = alternative.args;
for (var i = 0, len = a.length; i < len; i++) {
if (!a[i].equivalent_to(b[i])) {
for (var j = i + 1; j < len; j++) {
if (!a[j].equivalent_to(b[j])) return;
}
return i;
}
}
}
});
OPT(AST_Boolean, function(self, compressor){
if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
value: +self.value
});
if (compressor.option("booleans")) {
var p = compressor.parent();
if (p instanceof AST_Binary && (p.operator == "=="
@@ -4860,7 +4972,7 @@ merge(Compressor.prototype, {
});
function literals_in_boolean_context(self, compressor) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
if (compressor.in_boolean_context()) {
return best_of(compressor, self, make_sequence(self, [
self,
make_node(AST_True, self)

View File

@@ -84,7 +84,7 @@ function reserve_quoted_keys(ast, reserved) {
function addStrings(node, add) {
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Sequence) {
addStrings(node.expressions[node.expressions.length - 1], add);
addStrings(node.tail_node(), add);
} else if (node instanceof AST_String) {
add(node.value);
} else if (node instanceof AST_Conditional) {

View File

@@ -510,6 +510,11 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
}
});
AST_Node.DEFMETHOD("tail_node", return_this);
AST_Sequence.DEFMETHOD("tail_node", function() {
return this.expressions[this.expressions.length - 1];
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
try {
@@ -538,7 +543,7 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
skip_string(node.consequent);
skip_string(node.alternative);
} else if (node instanceof AST_Sequence) {
skip_string(node.expressions[node.expressions.length - 1]);
skip_string(node.tail_node());
}
}
});

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.2.0",
"version": "3.2.2",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -705,7 +705,7 @@ collapse_vars_lvalues_drop_assign: {
function f2(x) { var z = x, a = ++z; return z += a; }
function f3(x) { var a = (x -= 3); return x + a; }
function f4(x) { var a = (x -= 3); return x + a; }
function f5(x) { e1(); var v = e2(), c = v = --x; return x - c; }
function f5(x) { e1(), e2(); var c = --x; return x - c; }
function f6(x) { e1(), e2(); return --x - x; }
function f7(x) { e1(); return x - (e2() - x); }
function f8(x) { e1(); return x - (e2() - x); }
@@ -2013,7 +2013,8 @@ chained_3: {
}
expect: {
console.log(function(a, b) {
var c = 1, c = b;
var c = 1;
c = b;
b++;
return c;
}(0, 2));
@@ -2081,7 +2082,7 @@ inner_lvalues: {
expect_stdout: true
}
double_def: {
double_def_1: {
options = {
collapse_vars: true,
unused: true,
@@ -2091,8 +2092,23 @@ double_def: {
a();
}
expect: {
var a = x;
(a = a && y)();
var a;
(a = (a = x) && y)();
}
}
double_def_2: {
options = {
collapse_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = x, a = a && y;
a();
}
expect: {
(x && y)();
}
}
@@ -2201,7 +2217,7 @@ lvalues_def: {
}
expect: {
var a = 0, b = 1;
var a = b++, b = +void 0;
a = b++, b = +void 0;
a && a[a++];
console.log(a, b);
}
@@ -2386,6 +2402,7 @@ duplicate_argname: {
issue_2298: {
options = {
collapse_vars: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
@@ -3073,8 +3090,7 @@ issue_2437: {
var result = !!req.onreadystatechange;
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {});
return result;
}
else {
} else {
var req = new XMLHttpRequest();
var detectFunc = function () {};
req.onreadystatechange = detectFunc;
@@ -3087,13 +3103,14 @@ issue_2437: {
}
expect: {
!function() {
if (xhrDesc)
return result = !!(req = new XMLHttpRequest()).onreadystatechange,
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
if (xhrDesc) {
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result;
var req = new XMLHttpRequest(), detectFunc = function() {};
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
}
var req, detectFunc = function() {};
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
}();
}
@@ -3522,6 +3539,7 @@ issue_2436_12: {
issue_2436_13: {
options = {
collapse_vars: true,
passes: 2,
reduce_vars: true,
unused: true,
}
@@ -3606,14 +3624,14 @@ issue_2497: {
expect: {
function sample() {
if (true)
for (i = 0; i < 1; ++i)
for (k = 0; k < 1; ++k) {
for (var i = 0; i < 1; ++i)
for (var k = 0; k < 1; ++k) {
value = 1;
value = value ? value + 1 : 0;
}
else
for (var i = 0; i < 1; ++i)
for (var k = 0; k < 1; ++k)
for (i = 0; i < 1; ++i)
for (k = 0; k < 1; ++k)
var value = 1;
}
}
@@ -3622,6 +3640,7 @@ issue_2497: {
issue_2506: {
options = {
collapse_vars: true,
passes: 2,
reduce_vars: true,
unused: true,
}

View File

@@ -166,22 +166,24 @@ cond_1: {
conditionals: true
};
input: {
var do_something; // if undeclared it's assumed to have side-effects
if (some_condition()) {
function foo(do_something, some_condition) {
if (some_condition) {
do_something(x);
} else {
do_something(y);
}
if (some_condition()) {
if (some_condition) {
side_effects(x);
} else {
side_effects(y);
}
}
}
expect: {
var do_something;
do_something(some_condition() ? x : y);
some_condition() ? side_effects(x) : side_effects(y);
function foo(do_something, some_condition) {
do_something(some_condition ? x : y);
some_condition ? side_effects(x) : side_effects(y);
}
}
}
@@ -190,16 +192,18 @@ cond_2: {
conditionals: true
};
input: {
var x, FooBar;
if (some_condition()) {
function foo(x, FooBar, some_condition) {
if (some_condition) {
x = new FooBar(1);
} else {
x = new FooBar(2);
}
}
}
expect: {
var x, FooBar;
x = new FooBar(some_condition() ? 1 : 2);
function foo(x, FooBar, some_condition) {
x = new FooBar(some_condition ? 1 : 2);
}
}
}
@@ -605,6 +609,42 @@ cond_8c: {
}
}
cond_9: {
options = {
conditionals: true,
}
input: {
function f(x, y) {
g() ? x(1) : x(2);
x ? (y || x)() : (y || x)();
x ? y(a, b) : y(d, b, c);
x ? y(a, b, c) : y(a, b, c);
x ? y(a, b, c) : y(a, b, f);
x ? y(a, b, c) : y(a, e, c);
x ? y(a, b, c) : y(a, e, f);
x ? y(a, b, c) : y(d, b, c);
x ? y(a, b, c) : y(d, b, f);
x ? y(a, b, c) : y(d, e, c);
x ? y(a, b, c) : y(d, e, f);
}
}
expect: {
function f(x, y) {
g() ? x(1) : x(2);
x, (y || x)();
x ? y(a, b) : y(d, b, c);
x, y(a, b, c);
y(a, b, x ? c : f);
y(a, x ? b : e, c);
x ? y(a, b, c) : y(a, e, f);
y(x ? a : d, b, c);
x ? y(a, b, c) : y(d, b, f);
x ? y(a, b, c) : y(d, e, c);
x ? y(a, b, c) : y(d, e, f);
}
}
}
ternary_boolean_consequent: {
options = {
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
@@ -1015,3 +1055,151 @@ delete_conditional_2: {
}
expect_stdout: true
}
issue_2535_1: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
passes: 2,
side_effects: true,
}
input: {
if (true || x()) y();
if (true && x()) y();
if (x() || true) y();
if (x() && true) y();
if (false || x()) y();
if (false && x()) y();
if (x() || false) y();
if (x() && false) y();
}
expect: {
y();
x() && y();
(x(), 1) && y();
x() && y();
x() && y();
x() && y();
(x(), 0) && y();
}
}
issue_2535_2: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
side_effects: true,
}
input: {
function x() {}
function y() {
return "foo";
}
console.log((x() || true) || y());
console.log((y() || true) || x());
console.log((x() || true) && y());
console.log((y() || true) && x());
console.log((x() && true) || y());
console.log((y() && true) || x());
console.log((x() && true) && y());
console.log((y() && true) && x());
console.log((x() || false) || y());
console.log((y() || false) || x());
console.log((x() || false) && y());
console.log((y() || false) && x());
console.log((x() && false) || y());
console.log((y() && false) || x());
console.log((x() && false) && y());
console.log((y() && false) && x());
}
expect: {
function x() {}
function y() {
return "foo";
}
console.log(x() || !0);
console.log(y() || !0);
console.log((x(), y()));
console.log((y(), x()));
console.log(!!x() || y());
console.log(!!y() || x());
console.log(x() && y());
console.log(y() && x());
console.log(x() || y());
console.log(y() || x());
console.log(!!x() && y());
console.log(!!y() && x());
console.log((x(), y()));
console.log((y(), x()));
console.log(x() && !1);
console.log(y() && !1);
}
expect_stdout: [
"true",
"foo",
"foo",
"undefined",
"foo",
"true",
"undefined",
"undefined",
"foo",
"foo",
"false",
"undefined",
"foo",
"undefined",
"undefined",
"false",
]
}
issue_2560: {
options = {
conditionals: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function log(x) {
console.log(x);
}
function foo() {
return log;
}
function bar() {
if (x !== (x = foo())) {
x(1);
} else {
x(2);
}
}
var x = function() {
console.log("init");
};
bar();
bar();
}
expect: {
function log(x) {
console.log(x);
}
function bar() {
x !== (x = log) ? x(1) : x(2);
}
var x = function() {
console.log("init");
};
bar();
bar();
}
expect_stdout: [
"1",
"2",
]
}

View File

@@ -129,8 +129,8 @@ dead_code_constant_boolean_should_warn_more: {
function bar() {}
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo;
var x = 10, y;
bar();
}
expect_stdout: true
@@ -165,8 +165,8 @@ dead_code_constant_boolean_should_warn_more_strict: {
var foo;
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo;
var x = 10, y;
bar();
}
expect_stdout: true
@@ -178,6 +178,8 @@ try_catch_finally: {
conditionals: true,
dead_code: true,
evaluate: true,
passes: 2,
side_effects: true,
}
input: {
var a = 1;

View File

@@ -179,7 +179,9 @@ keep_fnames: {
}
drop_assign: {
options = { unused: true };
options = {
unused: true,
}
input: {
function f1() {
var a;
@@ -200,7 +202,7 @@ drop_assign: {
var a;
return function() {
a = 1;
}
};
}
}
expect: {
@@ -217,16 +219,17 @@ drop_assign: {
return 1;
}
function f5() {
var a;
return function() {
a = 1;
}
1;
};
}
}
}
keep_assign: {
options = { unused: "keep_assign" };
options = {
unused: "keep_assign",
}
input: {
function f1() {
var a;
@@ -247,7 +250,7 @@ keep_assign: {
var a;
return function() {
a = 1;
}
};
}
}
expect: {
@@ -270,19 +273,22 @@ keep_assign: {
var a;
return function() {
a = 1;
}
};
}
}
}
drop_toplevel_funcs: {
options = { toplevel: "funcs", unused: true };
options = {
toplevel: "funcs",
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -298,13 +304,16 @@ drop_toplevel_funcs: {
}
drop_toplevel_vars: {
options = { toplevel: "vars", unused: true };
options = {
toplevel: "vars",
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -312,11 +321,10 @@ drop_toplevel_vars: {
console.log(b = 3);
}
expect: {
var c = g;
function f(d) {
return function() {
c = 2;
}
2;
};
}
2;
function g() {}
@@ -326,13 +334,17 @@ drop_toplevel_vars: {
}
drop_toplevel_vars_fargs: {
options = { keep_fargs: false, toplevel: "vars", unused: true };
options = {
keep_fargs: false,
toplevel: "vars",
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -340,11 +352,10 @@ drop_toplevel_vars_fargs: {
console.log(b = 3);
}
expect: {
var c = g;
function f() {
return function() {
c = 2;
}
2;
};
}
2;
function g() {}
@@ -354,13 +365,16 @@ drop_toplevel_vars_fargs: {
}
drop_toplevel_all: {
options = { toplevel: true, unused: true };
options = {
toplevel: true,
unused: true
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -374,13 +388,16 @@ drop_toplevel_all: {
}
drop_toplevel_retain: {
options = { top_retain: "f,a,o", unused: true };
options = {
top_retain: "f,a,o",
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -388,26 +405,28 @@ drop_toplevel_retain: {
console.log(b = 3);
}
expect: {
var a, c = g;
var a;
function f(d) {
return function() {
c = 2;
}
2;
};
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_retain_array: {
options = { top_retain: [ "f", "a", "o" ], unused: true };
options = {
top_retain: [ "f", "a", "o" ],
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -415,26 +434,28 @@ drop_toplevel_retain_array: {
console.log(b = 3);
}
expect: {
var a, c = g;
var a;
function f(d) {
return function() {
c = 2;
}
2;
};
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_retain_regex: {
options = { top_retain: /^[fao]$/, unused: true };
options = {
top_retain: /^[fao]$/,
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -442,26 +463,29 @@ drop_toplevel_retain_regex: {
console.log(b = 3);
}
expect: {
var a, c = g;
var a;
function f(d) {
return function() {
c = 2;
}
2;
};
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_all_retain: {
options = { toplevel: true, top_retain: "f,a,o", unused: true };
options = {
toplevel: true,
top_retain: "f,a,o",
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -469,26 +493,29 @@ drop_toplevel_all_retain: {
console.log(b = 3);
}
expect: {
var a, c = g;
var a;
function f(d) {
return function() {
c = 2;
}
2;
};
}
a = 2;
function g() {}
console.log(3);
}
}
drop_toplevel_funcs_retain: {
options = { toplevel: "funcs", top_retain: "f,a,o", unused: true };
options = {
toplevel: "funcs",
top_retain: "f,a,o",
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -500,7 +527,7 @@ drop_toplevel_funcs_retain: {
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -509,13 +536,17 @@ drop_toplevel_funcs_retain: {
}
drop_toplevel_vars_retain: {
options = { toplevel: "vars", top_retain: "f,a,o", unused: true };
options = {
toplevel: "vars",
top_retain: "f,a,o",
unused: true,
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -523,11 +554,11 @@ drop_toplevel_vars_retain: {
console.log(b = 3);
}
expect: {
var a, c = g;
var a;
function f(d) {
return function() {
c = 2;
}
2;
};
}
a = 2;
function g() {}
@@ -537,13 +568,16 @@ drop_toplevel_vars_retain: {
}
drop_toplevel_keep_assign: {
options = { toplevel: true, unused: "keep_assign" };
options = {
toplevel: true,
unused: "keep_assign",
}
input: {
var a, b = 1, c = g;
function f(d) {
return function() {
c = 2;
}
};
}
a = 2;
function g() {}
@@ -753,11 +787,11 @@ issue_1583: {
expect: {
function m(t) {
(function(e) {
t = function() {
(function() {
return (function(a) {
return function(a) {};
})();
}();
})();
})();
}
}
@@ -1294,8 +1328,88 @@ issue_2288: {
expect: {
function foo(o) {
o.a;
for (i = 0; i < 0; i++);
for (var i = 0; i < 0; i++);
for (i = 0; i < 0; i++);
}
}
}
issue_2516_1: {
options = {
collapse_vars: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function foo() {
function qux(x) {
bar.call(null, x);
}
function bar(x) {
var FOUR = 4;
var trouble = x || never_called();
var value = (FOUR - 1) * trouble;
console.log(value == 6 ? "PASS" : value);
}
Baz = qux;
}
var Baz;
foo();
Baz(2);
}
expect: {
function foo() {
Baz = function(x) {
(function(x) {
var trouble = x || never_called();
var value = (4 - 1) * trouble;
console.log(6 == value ? "PASS" : value);
}).call(null, x);
};
}
var Baz;
foo();
Baz(2);
}
}
issue_2516_2: {
options = {
collapse_vars: true,
reduce_funcs: true,
reduce_vars: true,
passes: 2,
unused: true,
}
input: {
function foo() {
function qux(x) {
bar.call(null, x);
}
function bar(x) {
var FOUR = 4;
var trouble = x || never_called();
var value = (FOUR - 1) * trouble;
console.log(value == 6 ? "PASS" : value);
}
Baz = qux;
}
var Baz;
foo();
Baz(2);
}
expect: {
function foo() {
Baz = function(x) {
(function(x) {
var value = (4 - 1) * (x || never_called());
console.log(6 == value ? "PASS" : value);
}).call(null, x);
};
}
var Baz;
foo();
Baz(2);
}
}

View File

@@ -1,6 +1,7 @@
and: {
options = {
evaluate: true
evaluate: true,
side_effects: true,
}
input: {
var a;
@@ -76,7 +77,8 @@ and: {
or: {
options = {
evaluate: true
evaluate: true,
side_effects: true,
}
input: {
var a;
@@ -158,7 +160,8 @@ or: {
unary_prefix: {
options = {
evaluate: true
evaluate: true,
side_effects: true,
}
input: {
a = !0 && b;
@@ -1245,3 +1248,95 @@ self_comparison_2: {
}
expect_stdout: "false false true true 'number'"
}
issue_2535_1: {
options = {
booleans: true,
evaluate: true,
sequences: true,
side_effects: true,
}
input: {
if ((x() || true) || y()) z();
if ((x() || true) && y()) z();
if ((x() && true) || y()) z();
if ((x() && true) && y()) z();
if ((x() || false) || y()) z();
if ((x() || false) && y()) z();
if ((x() && false) || y()) z();
if ((x() && false) && y()) z();
}
expect: {
if (x(), 1) z();
if (x(), y()) z();
if (x() || y()) z();
if (x() && y()) z();
if (x() || y()) z();
if (x() && y()) z();
if (x(), y()) z();
if (x(), 0) z();
}
}
issue_2535_2: {
options = {
booleans: true,
evaluate: true,
sequences: true,
side_effects: true,
}
input: {
(x() || true) || y();
(x() || true) && y();
(x() && true) || y();
(x() && true) && y();
(x() || false) || y();
(x() || false) && y();
(x() && false) || y();
(x() && false) && y();
}
expect: {
x(),
x(), y(),
x() || y(),
x() && y(),
x() || y(),
x() && y(),
x(), y(),
x();
}
}
issue_2535_3: {
options = {
booleans: true,
evaluate: true,
}
input: {
console.log(Object(1) && 1 && 2);
console.log(Object(1) && true && 1 && 2 && Object(2));
console.log(Object(1) && true && 1 && null && 2 && Object(2));
console.log(2 == Object(1) || 0 || void 0 || null);
console.log(2 == Object(1) || 0 || void 0 || null || Object(2));
console.log(2 == Object(1) || 0 || void 0 || "ok" || null || Object(2));
}
expect: {
console.log(Object(1) && 2);
console.log(Object(1) && Object(2));
console.log(Object(1) && null);
console.log(2 == Object(1) || null);
console.log(2 == Object(1) || Object(2));
console.log(2 == Object(1) || "ok");
}
expect_stdout: true
expect_warnings: [
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1316,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1317,20]",
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1318,20]",
"WARN: Condition left of && always false [test/compress/evaluate.js:1318,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1319,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1320,20]",
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1321,20]",
"WARN: Condition left of || always true [test/compress/evaluate.js:1321,20]",
]
}

View File

@@ -87,6 +87,7 @@ issue_485_crashing_1530: {
dead_code: true,
evaluate: true,
inline: true,
side_effects: true,
}
input: {
(function(a) {
@@ -94,9 +95,7 @@ issue_485_crashing_1530: {
var b = 42;
})(this);
}
expect: {
this, void 0;
}
expect: {}
}
issue_1841_1: {
@@ -554,3 +553,122 @@ issue_2428: {
"PASS",
]
}
issue_2531_1: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function outer() {
function inner(value) {
function closure() {
return value;
}
return function() {
return closure();
};
}
return inner("Hello");
}
console.log("Greeting:", outer()());
}
expect: {
function outer() {
return function(value) {
return function() {
return value;
};
}("Hello");
}
console.log("Greeting:", outer()());
}
expect_stdout: "Greeting: Hello"
}
issue_2531_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function outer() {
function inner(value) {
function closure() {
return value;
}
return function() {
return closure();
};
}
return inner("Hello");
}
console.log("Greeting:", outer()());
}
expect: {
function outer() {
return function() {
return "Hello";
};
}
console.log("Greeting:", outer()());
}
expect_stdout: "Greeting: Hello"
}
issue_2531_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function outer() {
function inner(value) {
function closure() {
return value;
}
return function() {
return closure();
};
}
return inner("Hello");
}
console.log("Greeting:", outer()());
}
expect: {
console.log("Greeting:", "Hello");
}
expect_stdout: "Greeting: Hello"
}
empty_body: {
options = {
reduce_vars: true,
side_effects: true,
}
input: {
function f() {
function noop() {}
noop();
return noop;
}
}
expect: {
function f() {
function noop() {}
return noop;
}
}
}

View File

@@ -184,6 +184,7 @@ issue_2167: {
global_defs: {
"@isDevMode": "function(){}",
},
passes: 2,
side_effects: true,
}
input: {

View File

@@ -633,3 +633,34 @@ issue_2508_5: {
}
expect_stdout: true
}
issue_2519: {
options = {
collapse_vars: true,
evaluate: true,
hoist_props: true,
reduce_vars: true,
unused: true,
}
input: {
function testFunc() {
var dimensions = {
minX: 5,
maxX: 6,
};
var scale = 1;
var d = {
x: (dimensions.maxX + dimensions.minX) / 2,
};
return d.x * scale;
}
console.log(testFunc());
}
expect: {
function testFunc() {
return 1 * ((6 + 5) / 2);
}
console.log(testFunc());
}
expect_stdout: "5.5"
}

View File

@@ -26,7 +26,7 @@ issue_1639_1: {
}
expect: {
for (var a = 100, b = 10, L1 = 5; --L1 > 0;)
if (--b, !1) var ignore = 0;
if (--b, 0) var ignore = 0;
console.log(a, b);
}
expect_stdout: true
@@ -57,7 +57,7 @@ issue_1639_2: {
expect: {
var a = 100, b = 10;
function f19() {
++a, 1;
++a, 0;
}
f19(),
console.log(a, b);

View File

@@ -39,7 +39,7 @@ f7: {
"var b = 10;",
"",
"!function() {",
" for (;b = 100, !1; ) ;",
" b = 100;",
"}(), console.log(100, b);",
]
expect_stdout: true

View File

@@ -7,7 +7,7 @@ case_1: {
input: {
var a = 0, b = 1;
switch (true) {
case a, true:
case a || true:
default:
b = 2;
case true:
@@ -17,7 +17,7 @@ case_1: {
expect: {
var a = 0, b = 1;
switch (true) {
case a, true:
case a || true:
b = 2;
}
console.log(a, b);

View File

@@ -134,5 +134,5 @@ label_while: {
L: while (0) continue L;
}
}
expect_exact: "function f(){L:;}"
expect_exact: "function f(){L:0}"
}

View File

@@ -17,6 +17,6 @@ wrongly_optimized: {
foo();
}
// TODO: optimize to `func(), bar()`
(func(), 0) || bar();
(func(), 1) && bar();
}
}

View File

@@ -100,7 +100,7 @@ wrongly_optimized: {
foo();
}
// TODO: optimize to `func(), bar()`
if (func(), !0) bar();
if (func(), 1) bar();
}
}

View File

@@ -1,7 +1,8 @@
this_binding_conditionals: {
options = {
conditionals: true,
evaluate : true
evaluate: true,
side_effects: true,
};
input: {
(1 && a)();

View File

@@ -148,9 +148,11 @@ parse_do_while_without_semicolon: {
evaluate: {
options = {
loops: true,
dead_code: true,
evaluate: true,
loops: true,
passes: 2,
side_effects: true,
};
input: {
while (true) {
@@ -450,3 +452,43 @@ in_parenthesis_2: {
}
expect_exact: 'for(function(){"foo"in{}};0;);'
}
init_side_effects: {
options = {
loops: true,
side_effects: true,
};
input: {
for (function() {}(), i = 0; i < 5; i++) console.log(i);
for (function() {}(); i < 10; i++) console.log(i);
}
expect: {
for (i = 0; i < 5; i++) console.log(i);
for (; i < 10; i++) console.log(i);
}
expect_stdout: true
}
dead_code_condition: {
options = {
dead_code: true,
evaluate: true,
loops: true,
sequences: true,
}
input: {
for (var a = 0, b = 5; (a += 1, 3) - 3 && b > 0; b--) {
var c = function() {
b--;
}(a++);
}
console.log(a);
}
expect: {
var c;
var a = 0, b = 5;
a += 1, 0,
console.log(a);
}
expect_stdout: "1"
}

View File

@@ -972,8 +972,8 @@ inner_var_for_2: {
}
expect: {
!function() {
a = 1;
for (var b = 1; --b;) var a = 2;
var a = 1;
for (var b = 1; --b;) a = 2;
console.log(a);
}();
}
@@ -1209,6 +1209,7 @@ toplevel_on_loops_2: {
loops: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel:true,
unused: true,
}
@@ -2346,7 +2347,7 @@ booleans: {
}
expect: {
console.log(function(a) {
if (!1);
if (0);
switch (!1) {
case 0:
return "FAIL";
@@ -4525,3 +4526,261 @@ issue_2485: {
}
expect_stdout: "6"
}
issue_2455: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function foo() {
var that = this;
for (;;) that.bar();
}
}
expect: {
function foo() {
for (;;) this.bar();
}
}
}
issue_2560_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function main() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("FAIL");
else
console.log("PASS");
}
function baz(s) {
return s ? foo : bar;
}
function foo() {}
function bar() {}
main();
}
expect: {
function baz(s) {
return s ? foo : bar;
}
function foo() {}
function bar() {}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("FAIL");
else
console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_2560_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function main() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("FAIL");
else
console.log("PASS");
}
function baz() {
return foo, bar;
}
function foo() {}
function bar() {}
main();
}
expect: {
function baz() {
return function() {}, bar;
}
function bar() {}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("FAIL");
else
console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_2560_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function main() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("FAIL");
else
console.log("PASS");
}
function baz() {
try {
throw foo;
} catch (bar) {
return bar;
}
}
function foo() {}
main();
}
expect: {
function baz() {
try {
throw foo;
} catch (bar) {
return bar;
}
}
function foo() {}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("FAIL");
else
console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_2560_4: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function main() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("PASS");
else
console.log("FAIL");
}
function baz(s) {
function foo() {}
function bar() {}
return s ? foo : bar;
}
main();
}
expect: {
function baz(s) {
return s ? function() {} : function() {};
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("PASS");
else
console.log("FAIL");
})();
}
expect_stdout: "PASS"
}
issue_2560_5: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function main() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("PASS");
else
console.log("FAIL");
}
function baz() {
function foo() {}
function bar() {}
return foo, bar;
}
main();
}
expect: {
function baz() {
return function() {}, function() {};
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("PASS");
else
console.log("FAIL");
})();
}
expect_stdout: "PASS"
}
issue_2560_6: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function main() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("PASS");
else
console.log("FAIL");
}
function baz() {
function foo() {}
try {
throw foo;
} catch (bar) {
return bar;
}
}
main();
}
expect: {
function baz() {
try {
throw function() {};
} catch (bar) {
return bar;
}
}
(function() {
var thing = baz();
if (thing !== (thing = baz()))
console.log("PASS");
else
console.log("FAIL");
})();
}
expect_stdout: "PASS"
}

View File

@@ -122,3 +122,25 @@ return_undefined: {
}
}
}
return_void: {
options = {
if_return: true,
inline: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function g() {
h();
}
return g();
}
}
expect: {
function f() {
h();
}
}
}

View File

@@ -252,13 +252,12 @@ negate_iife_for: {
input: {
(function() {})();
for (i = 0; i < 5; i++) console.log(i);
(function() {})();
for (; i < 5; i++) console.log(i);
for (; i < 10; i++) console.log(i);
}
expect: {
for (!function() {}(), i = 0; i < 5; i++) console.log(i);
for (function() {}(); i < 5; i++) console.log(i);
for (!function() {}(); i < 10; i++) console.log(i);
}
expect_stdout: true
}

View File

@@ -817,3 +817,50 @@ issue_1758: {
}
expect_stdout: "0 3"
}
issue_2535: {
options = {
evaluate: true,
dead_code: true,
switches: true,
}
input: {
switch(w(), 42) {
case 13: x();
case 42: y();
default: z();
}
}
expect: {
w(), 42;
42;
y();
z();
}
}
issue_1750: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
var a = 0, b = 1;
switch (true) {
case a, true:
default:
b = 2;
case true:
}
console.log(a, b);
}
expect: {
var a = 0, b = 1;
true;
a, true;
b = 2;
console.log(a, b);
}
expect_stdout: "0 2"
}

View File

@@ -45,9 +45,9 @@ condition_evaluate: {
if (void 0 == null);
}
expect: {
while (!1);
for (; !0;);
if (!0);
while (0);
for (; 1;);
if (1);
}
}
@@ -68,6 +68,7 @@ label_if_break: {
conditionals: true,
dead_code: true,
evaluate: true,
side_effects: true,
}
input: {
L: if (true) {

View File

@@ -1,6 +1,7 @@
typeof_evaluation: {
options = {
evaluate: true
evaluate: true,
typeofs: true,
};
input: {
a = typeof 1;
@@ -44,7 +45,7 @@ typeof_in_boolean_context: {
function f2() { return g(), "Yes"; }
foo();
console.log(1);
var a = !(console.log(2), !0);
var a = !(console.log(2), 1);
foo();
}
}
@@ -57,6 +58,83 @@ issue_1668: {
if (typeof bar);
}
expect: {
if (!0);
if (1);
}
}
typeof_defun_1: {
options = {
evaluate: true,
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
function f() {
console.log("YES");
}
function g() {
h = 42;
console.log("NOPE");
}
function h() {
console.log("YUP");
}
g = 42;
"function" == typeof f && f();
"function" == typeof g && g();
"function" == typeof h && h();
}
expect: {
function g() {
h = 42;
console.log("NOPE");
}
function h() {
console.log("YUP");
}
g = 42;
console.log("YES");
"function" == typeof g && g();
h();
}
expect_stdout: [
"YES",
"YUP",
]
}
typeof_defun_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
typeofs: true,
}
input: {
var f = function() {
console.log(x);
};
var x = 0;
x++ < 2 && typeof f == "function" && f();
x++ < 2 && typeof f == "function" && f();
x++ < 2 && typeof f == "function" && f();
}
expect: {
var f = function() {
console.log(x);
};
var x = 0;
x++ < 2 && f();
x++ < 2 && f();
x++ < 2 && f();
}
expect_stdout: [
"1",
"2",
]
}