Compare commits

..

32 Commits

Author SHA1 Message Date
Alex Lam S.L
01d6e0f223 v3.7.3 2019-12-27 06:11:29 +08:00
Alex Lam S.L
ab050e7a94 fix corner case in directives (#3645) 2019-12-25 00:55:39 +00:00
Alex Lam S.L
75aa6ef848 enhance conditionals (#3643) 2019-12-22 04:29:32 +00:00
Alex Lam S.L
519a00bd8a fix corner case in collapse_vars (#3642)
fixes #3641
2019-12-22 01:08:56 +00:00
Alex Lam S.L
3ff0feddee suppress false positives from fuzzer (#3638) 2019-12-16 17:32:47 +02:00
Alex Lam S.L
74396acc86 fix corner case in loops (#3635)
fixes #3634
2019-12-11 06:39:46 +08:00
Alex Lam S.L
036bca980c enhance loops (#3633) 2019-12-10 12:57:47 +00:00
Alex Lam S.L
18c2b1841b fix corner case in reduce_vars (#3632)
fixes #3631
2019-12-10 09:45:51 +00:00
Alex Lam S.L
fe19ab7c57 v3.7.2 2019-12-08 15:36:18 +00:00
Alex Lam S.L
9074f05129 fix corner case in collapse_vars (#3629)
fixes #3628
2019-12-05 05:08:37 +08:00
Alex Lam S.L
04fbb1f949 avoid collision with HTML comments (#3625)
fixes #3624
2019-12-05 02:43:25 +08:00
Alex Lam S.L
bf7e4ca1a3 fix corner case in collapse_vars (#3627)
fixes #3626
2019-12-05 00:59:57 +08:00
Alex Lam S.L
d68ddc31f9 fix corner case in reduce_vars (#3623)
fixes #3622
2019-12-04 20:24:55 +08:00
Alex Lam S.L
500e31e03b enhance collapse_vars (#3621) 2019-12-02 15:25:38 +08:00
Alex Lam S.L
bef856addb fix corner case in keep_fargs (#3620)
fixes #3619
2019-12-02 12:28:17 +08:00
Alex Lam S.L
9a6faf365b fix corner cases in keep_fargs & unused (#3618) 2019-12-02 06:43:54 +08:00
Alex Lam S.L
e915832a36 enhance unused (#3617) 2019-12-01 18:10:37 +08:00
Alex Lam S.L
0593892d6e enhance collapse_vars (#3616) 2019-12-01 02:31:04 +08:00
Alex Lam S.L
b866a23671 v3.7.1 2019-11-30 06:02:09 +08:00
Alex Lam S.L
1283d73853 fix corner case in parsing directives (#3615) 2019-11-29 18:57:29 +08:00
Alex Lam S.L
1b61a81b5d enhance collapse_vars (#3613) 2019-11-29 17:45:49 +08:00
Alex Lam S.L
5a88c30d65 enhance assignments (#3612) 2019-11-28 07:40:34 +08:00
Alex Lam S.L
168ae747ad enhance collapse_vars (#3611) 2019-11-28 03:57:10 +08:00
Alex Lam S.L
d4b7010678 fix corner case in unsafe_regexp (#3609) 2019-11-27 17:35:21 +08:00
Alex Lam S.L
e27493f3c2 fix corner case in inline (#3608) 2019-11-27 14:54:36 +08:00
Alex Lam S.L
292d1de363 use stable Node.js version for fuzzing (#3605) 2019-11-26 02:11:11 +08:00
Alex Lam S.L
6768e6578f inline functions with directives more effectively (#3604) 2019-11-26 01:51:04 +08:00
Alex Lam S.L
48a0f6fe41 enhance unsafe_math (#3603) 2019-11-25 21:14:13 +08:00
Alex Lam S.L
81caadb709 enhance collapse_vars (#3602) 2019-11-20 12:54:49 +08:00
Alex Lam S.L
d959e0b86f fix corner case in if_return (#3601)
fixes #3600
2019-11-19 15:45:20 +08:00
Alex Lam S.L
67278e76c8 fix corner case in unused (#3599)
fixes #3598
2019-11-19 04:26:41 +08:00
Alex Lam S.L
c289ba1139 fix corner case in collapse_vars (#3597)
fixes #3596
2019-11-19 02:30:52 +08:00
26 changed files with 2733 additions and 405 deletions

View File

@@ -17,8 +17,8 @@ jobs:
run: |
git clone --branch v1.5.3 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
. ~/.nvs/nvs.sh --version
nvs add node
nvs use node
nvs add 10
nvs use 10
node --version
npm --version --no-update-notifier
npm install --no-audit --no-optional --no-save --no-update-notifier

View File

@@ -362,6 +362,7 @@ merge(Compressor.prototype, {
function reset_def(tw, compressor, def) {
def.assignments = 0;
def.chained = false;
def.cross_loop = false;
def.direct_access = false;
def.escaped = [];
def.fixed = !def.scope.pinned()
@@ -455,7 +456,7 @@ merge(Compressor.prototype, {
if (def.fixed == null) {
if (is_arguments(def)) return false;
if (def.global && def.name == "arguments") return false;
def.fixed = make_node(AST_Undefined, def.orig);
def.fixed = make_node(AST_Undefined, def.orig[0]);
}
return true;
}
@@ -647,7 +648,7 @@ merge(Compressor.prototype, {
tw.in_loop = this;
push(tw);
this.body.walk(tw);
if (has_break_or_continue(this)) {
if (has_break_or_continue(this, tw.parent())) {
pop(tw);
push(tw);
}
@@ -664,7 +665,7 @@ merge(Compressor.prototype, {
if (this.condition) this.condition.walk(tw);
this.body.walk(tw);
if (this.step) {
if (has_break_or_continue(this)) {
if (has_break_or_continue(this, tw.parent())) {
pop(tw);
push(tw);
}
@@ -700,8 +701,11 @@ merge(Compressor.prototype, {
node.argnames.forEach(function(arg, i) {
var d = arg.definition();
if (d.fixed === undefined && (!node.uses_arguments || tw.has_directive("use strict"))) {
var value = iife.args[i];
d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife);
var j = node.argnames.indexOf(arg);
if (j < 0) return value;
return iife.args[j] || make_node(AST_Undefined, iife);
};
tw.loop_ids[d.id] = tw.in_loop;
mark(tw, d, true);
@@ -765,6 +769,9 @@ merge(Compressor.prototype, {
d.fixed = false;
}
}
if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) {
d.cross_loop = true;
}
mark_escaped(tw, d, this.scope, this, value, 0, 1);
}
var parent;
@@ -1112,7 +1119,7 @@ merge(Compressor.prototype, {
var args;
var candidates = [];
var stat_index = statements.length;
var scanner = new TreeTransformer(function(node) {
var scanner = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
@@ -1120,7 +1127,7 @@ merge(Compressor.prototype, {
hit_index++;
if (hit_index < hit_stack.length) return handle_custom_scan_order(node);
hit = true;
stop_after = find_stop(node, 0);
stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
if (stop_after === node) abort = true;
return node;
}
@@ -1143,15 +1150,20 @@ merge(Compressor.prototype, {
&& (scan_lhs && lhs.equivalent_to(node)
|| scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
abort = true;
if (!hit_rhs || !value_def) abort = true;
return node;
}
if (is_lhs(node, parent)) {
if (value_def) replaced++;
if (value_def && !hit_rhs) {
assign_used = true;
replaced++;
}
return node;
} else if (value_def) {
if (!hit_rhs) replaced++;
return node;
} else {
replaced++;
if (value_def && candidate instanceof AST_VarDef) return node;
}
CHANGED = abort = true;
AST_Node.info("Collapsing {name} [{file}:{line},{col}]", {
@@ -1184,6 +1196,14 @@ merge(Compressor.prototype, {
stop_after = node;
if (node instanceof AST_Scope) abort = true;
}
// Scan but don't replace inside getter/setter
if (node instanceof AST_Accessor) {
var replace = can_replace;
can_replace = false;
descend(node, scanner);
can_replace = replace;
return node;
}
return handle_custom_scan_order(node);
}, function(node) {
if (abort) return;
@@ -1196,9 +1216,28 @@ merge(Compressor.prototype, {
if (!hit) {
if (node !== hit_stack[hit_index]) return node;
hit_index++;
if (hit_index < hit_stack.length) return;
hit = true;
return node;
switch (hit_stack.length - hit_index) {
case 0:
hit = true;
if (assign_used) return node;
if (node instanceof AST_VarDef) return node;
def.replaced++;
var parent = multi_replacer.parent();
if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
value_def.replaced++;
return MAP.skip;
}
return get_rvalue(candidate);
case 1:
if (!assign_used && node.body === candidate) {
hit = true;
def.replaced++;
value_def.replaced++;
return null;
}
default:
return;
}
}
// Replace variable when found
if (node instanceof AST_SymbolRef
@@ -1207,11 +1246,12 @@ merge(Compressor.prototype, {
if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++;
value_def.replaced--;
return candidate.value.clone();
return get_rvalue(candidate).clone();
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
}, patch_sequence);
var force_single;
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
@@ -1244,7 +1284,10 @@ merge(Compressor.prototype, {
} : side_effects_external : return_false;
var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit;
var abort = false;
var replaced = 0;
var assign_used = false;
var can_replace = !args || !hit;
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
args[j].transform(scanner);
@@ -1255,14 +1298,23 @@ merge(Compressor.prototype, {
statements[i].transform(scanner);
}
if (value_def) {
var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false;
else {
var def = lhs.definition();
var referenced = def.references.length - def.replaced;
if (candidate instanceof AST_Assign) referenced--;
if (replaced && referenced == replaced) {
abort = false;
} else if (candidate instanceof AST_Assign) {
candidates.push(hit_stack);
force_single = true;
continue;
} else {
replaced = false;
}
if (replaced) {
hit_index = 0;
hit = funarg;
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer);
if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
}
value_def.single_use = false;
}
@@ -1331,10 +1383,10 @@ merge(Compressor.prototype, {
return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
}
if (node instanceof AST_Function) {
return compressor.option("ie8") && node.name && node.name.name in lvalues;
return compressor.option("ie8") && node.name && lvalues.has(node.name.name);
}
if (node instanceof AST_PropAccess) {
return side_effects || node.expression.may_throw_on_access(compressor);
return side_effects || !value_def && node.expression.may_throw_on_access(compressor);
}
if (node instanceof AST_SymbolRef) {
if (symbol_in_lvalues(node, parent)) {
@@ -1345,10 +1397,10 @@ merge(Compressor.prototype, {
if (node instanceof AST_This) return symbol_in_lvalues(node, parent);
if (node instanceof AST_VarDef) {
if (!node.value) return false;
return node.name.name in lvalues || side_effects && may_modify(node.name);
return lvalues.has(node.name.name) || side_effects && may_modify(node.name);
}
var sym = is_lhs(node.left, node);
if (sym && sym.name in lvalues) return true;
if (sym && lvalues.has(sym.name)) return true;
if (sym instanceof AST_PropAccess) return true;
}
@@ -1404,7 +1456,9 @@ merge(Compressor.prototype, {
function extract_candidates(expr) {
hit_stack.push(expr);
if (expr instanceof AST_Assign) {
if (expr instanceof AST_Array) {
expr.elements.forEach(extract_candidates);
} else if (expr instanceof AST_Assign) {
candidates.push(hit_stack.slice());
extract_candidates(expr.left);
extract_candidates(expr.right);
@@ -1451,6 +1505,14 @@ merge(Compressor.prototype, {
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Object) {
expr.properties.forEach(function(prop) {
if (prop instanceof AST_ObjectKeyVal) {
hit_stack.push(prop);
extract_candidates(prop.value);
hit_stack.pop();
}
});
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
@@ -1481,6 +1543,7 @@ merge(Compressor.prototype, {
function find_stop(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Array) return node;
if (parent instanceof AST_Assign) return node;
if (parent instanceof AST_Binary) return node;
if (parent instanceof AST_Call) return node;
@@ -1490,6 +1553,7 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_ObjectKeyVal) return node;
if (parent instanceof AST_PropAccess) return node;
if (parent instanceof AST_Sequence) {
return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
@@ -1501,11 +1565,92 @@ merge(Compressor.prototype, {
return null;
}
function find_stop_value(node, level) {
var parent = scanner.parent(level);
if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
if (parent instanceof AST_Assign) {
if (may_throw(parent)) return node;
if (parent.left instanceof AST_SymbolRef) {
var name = parent.left.name;
if (lhs.name == name) return node;
if (value_def.name == name) return node;
}
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Binary) {
if (lazy_op[parent.operator] && parent.left !== node) {
do {
node = parent;
parent = scanner.parent(++level);
} while (parent instanceof AST_Binary && parent.operator == node.operator);
return node;
}
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Call) return parent;
if (parent instanceof AST_Case) {
if (parent.expression !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Conditional) {
if (parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Do) return node;
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_For) {
if (parent.init !== node && parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_ForIn) {
if (parent.init !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_If) {
if (parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_ObjectKeyVal) {
var obj = scanner.parent(level + 1);
return all(obj.properties, function(prop) {
return prop instanceof AST_ObjectKeyVal;
}) ? find_stop_value(obj, level + 2) : obj;
}
if (parent instanceof AST_PropAccess) return find_stop_value(parent, level + 1);
if (parent instanceof AST_Sequence) {
return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
}
if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Switch) {
if (parent.expression !== node) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_Unary) {
if (parent.operator == "delete") return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_VarDef) {
var name = parent.name.name;
if (lhs.name == name) return node;
if (value_def.name == name) return node;
return find_stop_value(parent, level + 1);
}
if (parent instanceof AST_While) {
if (parent.condition !== node) return node;
return find_stop_value(parent, level + 1);
}
return null;
}
function find_stop_unused(node, level) {
var parent = scanner.parent(level);
if (is_last_node(node, parent)) return node;
if (in_conditional(node, parent)) return node;
if (parent instanceof AST_Assign) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Assign) {
return may_throw(parent) ? node : find_stop_unused(parent, level + 1);
}
if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
@@ -1514,7 +1659,22 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_PropAccess) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_ObjectKeyVal) {
var obj = scanner.parent(level + 1);
return all(obj.properties, function(prop) {
return prop instanceof AST_ObjectKeyVal;
}) ? find_stop_unused(obj, level + 2) : obj;
}
if (parent instanceof AST_PropAccess) {
var exp = parent.expression;
if (exp === node) return find_stop_unused(parent, level + 1);
var sym = root_expr(exp);
if (!(sym instanceof AST_SymbolRef)) return find_stop_unused(parent, level + 1);
var lvalue = lvalues.get(sym.name);
return !lvalue || all(lvalue, function(lhs) {
return !(lhs instanceof AST_PropAccess);
}) ? find_stop_unused(parent, level + 1) : node;
}
if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
@@ -1523,8 +1683,11 @@ merge(Compressor.prototype, {
return null;
}
function mangleable_var(var_def) {
var value = var_def.value;
function mangleable_var(value) {
if (force_single) {
force_single = false;
return;
}
if (!(value instanceof AST_SymbolRef)) return;
var def = value.definition();
if (def.undeclared) return;
@@ -1539,11 +1702,21 @@ merge(Compressor.prototype, {
var referenced = def.references.length - def.replaced;
var declared = def.orig.length - def.eliminated;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
|| (referenced > 1 ? mangleable_var(expr.value) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else if (expr instanceof AST_Assign) {
var lhs = expr.left;
if (expr.operator == "=" && lhs instanceof AST_SymbolRef) {
var def = lhs.definition();
if (def.references[0] === lhs) {
var referenced = def.references.length - def.replaced;
if (referenced > 1) mangleable_var(expr.right);
}
}
return lhs;
} else {
return expr[expr instanceof AST_Assign ? "left" : "expression"];
return expr.expression;
}
}
@@ -1612,10 +1785,8 @@ merge(Compressor.prototype, {
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (candidate instanceof AST_VarDef) {
lvalues[candidate.name.name] = lhs;
}
var lvalues = new Dictionary();
if (candidate instanceof AST_VarDef) lvalues.add(candidate.name.name, lhs);
var scan_iife = scope instanceof AST_Toplevel;
var tw = new TreeWalker(function(node) {
if (scan_iife && node.TYPE == "Call") {
@@ -1632,9 +1803,7 @@ merge(Compressor.prototype, {
} else if (node instanceof AST_This) {
value = node;
}
if (value && !lvalues[node.name]) {
lvalues[node.name] = is_modified(compressor, tw, node, value, 0);
}
if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
});
expr.walk(tw);
return lvalues;
@@ -1652,26 +1821,22 @@ merge(Compressor.prototype, {
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
if (node instanceof AST_Scope) return node;
if (node !== expr && node.body !== expr) return;
found = true;
if (node instanceof AST_VarDef) {
found = true;
node.value = null;
return node;
}
if (in_list) {
found = true;
return MAP.skip;
}
if (!this.parent()) {
found = true;
return null;
}
}, function(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}));
return in_list ? MAP.skip : null;
}, patch_sequence));
}
function patch_sequence(node) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
case 1: return node.expressions[0];
}
}
function is_lhs_local(lhs) {
@@ -1679,7 +1844,7 @@ merge(Compressor.prototype, {
return sym instanceof AST_SymbolRef
&& sym.definition().scope === scope
&& !(in_loop
&& (sym.name in lvalues && lvalues[sym.name] !== lhs
&& (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
|| candidate instanceof AST_Unary
|| candidate instanceof AST_Assign && candidate.operator != "="));
}
@@ -1707,9 +1872,11 @@ merge(Compressor.prototype, {
}
function symbol_in_lvalues(sym, parent) {
var lvalue = lvalues[sym.name];
if (!lvalue) return;
if (lvalue !== lhs) return true;
var lvalue = lvalues.get(sym.name);
if (!lvalue || all(lvalue, function(lhs) {
return !lhs;
})) return;
if (lvalue[0] !== lhs) return true;
scan_rhs = false;
}
@@ -1798,6 +1965,7 @@ merge(Compressor.prototype, {
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
statements[i] = stat;
statements[i] = stat.transform(compressor);
continue;
}
@@ -1810,6 +1978,7 @@ merge(Compressor.prototype, {
stat.condition = negated;
statements[j] = stat.body;
stat.body = next;
statements[i] = stat;
statements[i] = stat.transform(compressor);
continue;
}
@@ -1827,6 +1996,7 @@ merge(Compressor.prototype, {
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
statements[i] = stat;
statements[i] = stat.transform(compressor);
continue;
}
@@ -2707,6 +2877,14 @@ merge(Compressor.prototype, {
});
}
AST_Lambda.DEFMETHOD("first_statement", function() {
var body = this.body;
for (var i = 0; i < body.length; i++) {
var stat = body[i];
if (!(stat instanceof AST_Directive)) return stat;
}
});
function try_evaluate(compressor, node) {
var ev = node.evaluate(compressor);
if (ev === node) return node;
@@ -3081,7 +3259,7 @@ merge(Compressor.prototype, {
if (fn instanceof AST_Lambda) {
if (fn.evaluating) return this;
if (fn.name && fn.name.definition().recursive_refs > 0) return this;
var stat = fn.body[0];
var stat = fn.first_statement();
if (!(stat instanceof AST_Return)) return this;
var args = eval_args(this.args);
if (!args) return this;
@@ -3863,7 +4041,7 @@ merge(Compressor.prototype, {
def.value = null;
head.push(def);
} else {
var value = def.value && def.value.drop_side_effect_free(compressor);
var value = def.value && !def.value.single_use && def.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);
@@ -4543,8 +4721,11 @@ merge(Compressor.prototype, {
});
function if_break_in_loop(self, compressor) {
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
if (compressor.option("dead_code") && is_break(first)) {
var first = first_statement(self.body);
if (compressor.option("dead_code")
&& (first instanceof AST_Break
|| first instanceof AST_Continue && external_target(first)
|| first instanceof AST_Exit)) {
var body = [];
if (self.init instanceof AST_Statement) {
body.push(self.init);
@@ -4553,10 +4734,19 @@ merge(Compressor.prototype, {
body: self.init
}));
}
if (self.condition) {
var retain = external_target(first) || first instanceof AST_Exit;
if (self.condition && retain) {
body.push(make_node(AST_If, self, {
condition: self.condition,
body: first,
alternative: null
}));
} else if (self.condition) {
body.push(make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}));
} else if (retain) {
body.push(first);
}
extract_declarations_from_unreachable_code(self.body, body);
return make_node(AST_BlockStatement, self, {
@@ -4564,7 +4754,8 @@ merge(Compressor.prototype, {
});
}
if (first instanceof AST_If) {
if (is_break(first.body)) {
var ab = first_statement(first.body);
if (ab instanceof AST_Break && !external_target(ab)) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
@@ -4574,8 +4765,12 @@ merge(Compressor.prototype, {
} else {
self.condition = first.condition.negate(compressor);
}
drop_it(first.alternative);
} else if (is_break(first.alternative)) {
var body = as_statement_array(first.alternative);
extract_declarations_from_unreachable_code(first.body, body);
return drop_it(body);
}
ab = first_statement(first.alternative);
if (ab instanceof AST_Break && !external_target(ab)) {
if (self.condition) {
self.condition = make_node(AST_Binary, self.condition, {
left: self.condition,
@@ -4585,18 +4780,22 @@ merge(Compressor.prototype, {
} else {
self.condition = first.condition;
}
drop_it(first.body);
var body = as_statement_array(first.body);
extract_declarations_from_unreachable_code(first.alternative, body);
return drop_it(body);
}
}
return self;
function is_break(node) {
return node instanceof AST_Break
&& compressor.loopcontrol_target(node) === compressor.self();
function first_statement(body) {
return body instanceof AST_BlockStatement ? body.body[0] : body;
}
function external_target(node) {
return compressor.loopcontrol_target(node) !== compressor.self();
}
function drop_it(rest) {
rest = as_statement_array(rest);
if (self.body instanceof AST_BlockStatement) {
self.body = self.body.clone();
self.body.body = rest.concat(self.body.body.slice(1));
@@ -4606,7 +4805,7 @@ merge(Compressor.prototype, {
body: rest
}).transform(compressor);
}
self = if_break_in_loop(self, compressor);
return if_break_in_loop(self, compressor);
}
}
@@ -5062,24 +5261,52 @@ merge(Compressor.prototype, {
&& !fn.uses_arguments
&& !fn.pinned()) {
var pos = 0, last = 0;
var drop_fargs = exp === fn && !fn.name && compressor.drop_fargs(fn, self);
var side_effects = [];
for (var i = 0; i < self.args.length; i++) {
var trim = i >= fn.argnames.length;
if (trim || fn.argnames[i].__unused) {
var node = self.args[i].drop_side_effect_free(compressor);
if (node) {
self.args[pos++] = node;
if (drop_fargs) {
fn.argnames.splice(i, 1);
self.args.splice(i, 1);
if (node) side_effects.push(node);
i--;
continue;
} else if (node) {
side_effects.push(node);
self.args[pos++] = make_sequence(self, side_effects);
side_effects = [];
} else if (!trim) {
self.args[pos++] = make_node(AST_Number, self.args[i], {
value: 0
});
if (side_effects.length) {
node = make_sequence(self, side_effects);
side_effects = [];
} else {
node = make_node(AST_Number, self.args[i], {
value: 0
});
}
self.args[pos++] = node;
continue;
}
} else {
self.args[pos++] = self.args[i];
side_effects.push(self.args[i]);
self.args[pos++] = make_sequence(self, side_effects);
side_effects = [];
}
last = pos;
}
if (drop_fargs) for (; i < fn.argnames.length; i++) {
if (fn.argnames[i].__unused) fn.argnames.splice(i--, 1);
}
self.args.length = last;
if (side_effects.length) {
var arg = make_sequence(self, side_effects);
self.args.push(self.args.length < fn.argnames.length ? make_node(AST_UnaryPrefix, self, {
operator: "void",
expression: arg
}) : arg);
}
}
if (compressor.option("unsafe")) {
if (is_undeclared_ref(exp)) switch (exp.name) {
@@ -5346,7 +5573,7 @@ merge(Compressor.prototype, {
}
}
}
var stat = is_func && fn.body[0];
var stat = is_func && fn.first_statement();
var can_inline = compressor.option("inline") && !self.is_expr_pure(compressor);
if (exp === fn && can_inline && stat instanceof AST_Return) {
var value = stat.value;
@@ -5794,14 +6021,15 @@ merge(Compressor.prototype, {
self.right = tmp;
}
}
if (commutativeOperators[self.operator] && self.right.is_constant() && !self.left.is_constant()) {
if (commutativeOperators[self.operator]
&& self.right.is_constant()
&& !self.left.is_constant()
&& !(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
// if right is a constant, whatever side effects the
// left side might have could not influence the
// result. hence, force switch.
if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
reverse();
}
reverse();
}
self = self.lift_sequences(compressor);
if (compressor.option("assignments") && lazy_op[self.operator]) {
@@ -6112,6 +6340,26 @@ merge(Compressor.prototype, {
});
break;
}
// (a + b) + 3 => 3 + (a + b)
if (compressor.option("unsafe_math")
&& self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
&& self.right.is_constant()
&& (self.right.is_boolean(compressor) || self.right.is_number(compressor))
&& self.left.is_number(compressor)
&& !self.left.right.is_constant()
&& (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
self = make_node(AST_Binary, self, {
operator: self.left.operator,
left: make_node(AST_Binary, self, {
operator: self.operator,
left: self.right,
right: self.left.left
}),
right: self.left.right
});
break;
}
case "-":
// a - -b => a + b
if (self.right instanceof AST_UnaryPrefix
@@ -6398,6 +6646,15 @@ merge(Compressor.prototype, {
fixed = make_node(AST_Function, fixed, fixed);
fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
}
if (fixed instanceof AST_Lambda) {
var scope = self.scope;
fixed.enclosed.forEach(function(def) {
if (fixed.variables.has(def.name)) return;
if (scope.var_names()[def.name]) return;
scope.enclosed.push(def);
scope.var_names()[def.name] = true;
});
}
var value;
if (def.recursive_refs > 0) {
value = fixed.clone(true);
@@ -6409,6 +6666,7 @@ merge(Compressor.prototype, {
name.scope = value;
value.name = name;
lambda_def = value.def_function(name);
lambda_def.recursive_refs = def.recursive_refs;
}
value.walk(new TreeWalker(function(node) {
if (!(node instanceof AST_SymbolRef)) return;
@@ -6439,14 +6697,13 @@ merge(Compressor.prototype, {
if (fixed && def.should_replace === undefined) {
var init;
if (fixed instanceof AST_This) {
if (!(def.orig[0] instanceof AST_SymbolFunarg) && all(def.references, function(ref) {
return def.scope === ref.scope;
})) {
if (!(def.orig[0] instanceof AST_SymbolFunarg) && same_scope(def)) {
init = fixed;
}
} else {
var ev = fixed.evaluate(compressor);
if (ev !== fixed && (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))) {
if (ev !== fixed && (!(ev instanceof RegExp)
|| compressor.option("unsafe_regexp") && !def.cross_loop && same_scope(def))) {
init = make_node_from_constant(ev, fixed);
}
}
@@ -6483,6 +6740,13 @@ merge(Compressor.prototype, {
}
return self;
function same_scope(def) {
var scope = def.scope.resolve();
return all(def.references, function(ref) {
return scope === ref.scope.resolve();
});
}
function has_symbol_ref(value) {
var found;
value.walk(new TreeWalker(function(node) {
@@ -6640,8 +6904,8 @@ merge(Compressor.prototype, {
self.right = self.right.left;
}
}
if ((self.operator == "+=" || self.operator == "-=")
&& self.left.is_number(compressor)
if ((self.operator == "-=" || self.operator == "+="
&& (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
&& self.right instanceof AST_Number
&& self.right.getValue() === 1) {
var op = self.operator.slice(0, -1);
@@ -6741,25 +7005,42 @@ merge(Compressor.prototype, {
});
}
}
// x ? y(a) : y(b) --> y(x ? a : b)
var arg_index;
// x ? y : y --> x, y
if (consequent.equivalent_to(alternative)) return make_sequence(self, [
condition,
consequent
]).optimize(compressor);
if (consequent instanceof AST_Call
&& alternative.TYPE === consequent.TYPE
&& consequent.args.length > 0
&& consequent.args.length == alternative.args.length
&& consequent.expression.equivalent_to(alternative.expression)
&& !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: condition,
consequent: consequent.args[arg_index],
alternative: alternative.args[arg_index]
});
return node;
&& consequent.args.length == alternative.args.length) {
var arg_index = arg_diff();
// x ? y(a) : z(a) --> (x ? y : z)(a)
if (arg_index == -1
&& !(consequent.expression instanceof AST_PropAccess)
&& !(alternative.expression instanceof AST_PropAccess)) {
var node = consequent.clone();
node.expression = make_node(AST_Conditional, self, {
condition: condition,
consequent: consequent.expression,
alternative: alternative.expression
});
return node;
}
// x ? y(a) : y(b) --> y(x ? a : b)
if (arg_index >= 0
&& consequent.expression.equivalent_to(alternative.expression)
&& !condition.has_side_effects(compressor)
&& !consequent.expression.has_side_effects(compressor)) {
var node = consequent.clone();
node.args[arg_index] = make_node(AST_Conditional, self, {
condition: condition,
consequent: consequent.args[arg_index],
alternative: alternative.args[arg_index]
});
return node;
}
}
// x?y?z:a:a --> x&&y?z:a
// x ? (y ? a : b) : b --> x && y ? a : b
if (consequent instanceof AST_Conditional
&& consequent.alternative.equivalent_to(alternative)) {
return make_node(AST_Conditional, self, {
@@ -6772,12 +7053,18 @@ merge(Compressor.prototype, {
alternative: alternative
});
}
// x ? y : y --> x, y
if (consequent.equivalent_to(alternative)) {
return make_sequence(self, [
condition,
consequent
]).optimize(compressor);
// x ? a : (y ? a : b)--> x || y ? a : b
if (alternative instanceof AST_Conditional
&& consequent.equivalent_to(alternative.consequent)) {
return make_node(AST_Conditional, self, {
condition: make_node(AST_Binary, self, {
left: condition,
operator: "||",
right: alternative.condition
}),
consequent: consequent,
alternative: alternative.alternative
});
}
// x ? (y, w) : (z, w) --> x ? y : z, w
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
@@ -6881,17 +7168,18 @@ merge(Compressor.prototype, {
&& node.expression.getValue());
}
function single_arg_diff() {
function 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;
if (!a[j].equivalent_to(b[j])) return -2;
}
return i;
}
}
return -1;
}
function can_shift_lhs_of_tail(node) {

View File

@@ -43,8 +43,6 @@
"use strict";
var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
function is_some_comments(comment) {
// multiline comment
return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value);
@@ -269,7 +267,7 @@ function OutputStream(options) {
}
}
newline_insert = -1;
var prev = last.charAt(last.length - 1);
var prev = last.slice(-1);
if (might_need_semicolon) {
might_need_semicolon = false;
@@ -298,16 +296,16 @@ function OutputStream(options) {
}
if (might_need_space) {
if ((is_identifier_char(prev)
&& (is_identifier_char(ch) || ch == "\\"))
if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last))
{
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| last == "--" && ch == ">") {
OUTPUT += " ";
current_col++;
current_pos++;
}
might_need_space = false;
if (prev != "<" || str != "!") might_need_space = false;
}
if (mapping_token) {
@@ -322,7 +320,7 @@ function OutputStream(options) {
}
OUTPUT += str;
has_parens = str[str.length - 1] == "(";
has_parens = str.slice(-1) == "(";
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1;
current_line += n;
@@ -378,7 +376,7 @@ function OutputStream(options) {
};
function force_semicolon() {
might_need_semicolon = false;
if (might_need_semicolon) print(";");
print(";");
}
@@ -585,17 +583,7 @@ function OutputStream(options) {
force_semicolon : force_semicolon,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote, escape_directive) {
var encoded = encode_string(str, quote);
if (escape_directive === true && encoded.indexOf("\\") === -1) {
// Insert semicolons to break directive prologue
if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
force_semicolon();
}
force_semicolon();
}
print(encoded);
},
print_string : function(str, quote) { print(encode_string(str, quote)) },
next_indent : next_indent,
with_indent : with_indent,
with_block : with_block,
@@ -633,17 +621,10 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
}
var in_directive = false;
var active_scope = null;
var use_asm = null;
var use_asm = false;
AST_Node.DEFMETHOD("print", function(stream, force_parens) {
var self = this, generator = self._codegen;
if (self instanceof AST_Scope) {
active_scope = self;
} else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
use_asm = active_scope;
}
function doit() {
stream.prepend_comments(self);
self.add_source_map(stream);
@@ -657,9 +638,6 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self === use_asm) {
use_asm = null;
}
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
@@ -711,16 +689,23 @@ function OutputStream(options) {
PARENS(AST_Sequence, function(output) {
var p = output.parent();
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|| p instanceof AST_Unary // !(foo, bar, baz)
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|| p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
* ==> 20 (side effect, set a := 10 and b := 20) */
;
// (foo, bar)() or foo(1, (2, 3), 4)
return p instanceof AST_Call
// !(foo, bar, baz)
|| p instanceof AST_Unary
// 1 + (2, 3) + 4 ==> 8
|| p instanceof AST_Binary
// var a = (1, 2), b = a + a; ==> b == 4
|| p instanceof AST_VarDef
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|| p instanceof AST_PropAccess && p.expression === this
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|| p instanceof AST_Array
// { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_ObjectProperty
// (false, true) ? (a = 10, b = 20) : (c = 30)
// ==> 20 (side effect, set a := 10 and b := 20)
|| p instanceof AST_Conditional;
});
PARENS(AST_Binary, function(output) {
@@ -821,7 +806,18 @@ function OutputStream(options) {
/* -----[ PRINTERS ]----- */
DEFPRINT(AST_Directive, function(self, output) {
output.print_string(self.value, self.quote);
var quote = self.quote;
var value = self.value;
switch (output.option("quote_style")) {
case 0:
case 2:
if (value.indexOf('"') == -1) quote = '"';
break;
case 1:
if (value.indexOf("'") == -1) quote = "'";
break;
}
output.print(quote + value + quote);
output.semicolon();
});
DEFPRINT(AST_Debugger, function(self, output) {
@@ -833,30 +829,27 @@ function OutputStream(options) {
function display_body(body, is_toplevel, output, allow_directives) {
var last = body.length - 1;
in_directive = allow_directives;
var in_directive = allow_directives;
var was_asm = use_asm;
body.forEach(function(stmt, i) {
if (in_directive === true && !(stmt instanceof AST_Directive ||
stmt instanceof AST_EmptyStatement ||
(stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String)
)) {
in_directive = false;
}
if (!(stmt instanceof AST_EmptyStatement)) {
output.indent();
stmt.print(output);
if (!(i == last && is_toplevel)) {
output.newline();
if (is_toplevel) output.newline();
if (in_directive) {
if (stmt instanceof AST_Directive) {
if (stmt.value == "use asm") use_asm = true;
} else if (!(stmt instanceof AST_EmptyStatement)) {
if (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) {
output.force_semicolon();
}
in_directive = false;
}
}
if (in_directive === true &&
stmt instanceof AST_SimpleStatement &&
stmt.body instanceof AST_String
) {
in_directive = false;
}
if (stmt instanceof AST_EmptyStatement) return;
output.indent();
stmt.print(output);
if (i == last && is_toplevel) return;
output.newline();
if (is_toplevel) output.newline();
});
in_directive = false;
use_asm = was_asm;
}
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) {
@@ -1247,29 +1240,10 @@ function OutputStream(options) {
output.print(self.operator);
});
DEFPRINT(AST_Binary, function(self, output) {
var op = self.operator;
self.left.print(output);
if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
&& self.left instanceof AST_UnaryPostfix
&& self.left.operator == "--") {
// space is mandatory to avoid outputting -->
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space();
}
output.print(op);
if ((op == "<" || op == "<<")
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "!"
&& self.right.expression instanceof AST_UnaryPrefix
&& self.right.expression.operator == "--") {
// space is mandatory to avoid outputting <!--
output.print(" ");
} else {
// the space is optional depending on "beautify"
output.space();
}
output.space();
output.print(self.operator);
output.space();
self.right.print(output);
});
DEFPRINT(AST_Conditional, function(self, output) {
@@ -1360,7 +1334,7 @@ function OutputStream(options) {
output.print(self.getValue());
});
DEFPRINT(AST_String, function(self, output) {
output.print_string(self.getValue(), self.quote, in_directive);
output.print_string(self.getValue(), self.quote);
});
DEFPRINT(AST_Number, function(self, output) {
if (use_asm && self.start && self.start.raw != null) {

View File

@@ -787,20 +787,19 @@ function parse($TEXT, options) {
handle_regexp();
switch (S.token.type) {
case "string":
if (S.in_directives) {
var token = peek();
if (S.token.raw.indexOf("\\") == -1
&& (is_token(token, "punc", ";")
|| is_token(token, "punc", "}")
|| has_newline_before(token)
|| is_token(token, "eof"))) {
S.input.add_directive(S.token.value);
var dir = S.in_directives;
var body = expression(true);
if (dir) {
if (body instanceof AST_String) {
var value = body.start.raw.slice(1, -1);
S.input.add_directive(value);
body.value = value;
} else {
S.in_directives = false;
S.in_directives = dir = false;
}
}
var dir = S.in_directives, stat = simple_statement();
return dir ? new AST_Directive(stat.body) : stat;
semicolon();
return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body });
case "num":
case "regexp":
case "operator":
@@ -965,8 +964,10 @@ function parse($TEXT, options) {
return new AST_LabeledStatement({ body: stat, label: label });
}
function simple_statement(tmp) {
return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
function simple_statement() {
var body = expression(true);
semicolon();
return new AST_SimpleStatement({ body: body });
}
function break_cont(type) {

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.7.0",
"version": "3.7.3",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -290,26 +290,60 @@ increment_decrement_2: {
expect_stdout: "42"
}
issue_3375: {
issue_3375_1: {
options = {
assignments: true,
reduce_vars: true,
}
input: {
console.log(typeof function(b) {
function p(o) {
console.log(typeof o, o);
}
p(function(b) {
var a = b += 1;
--b;
return a;
}("object"));
}
expect: {
console.log(typeof function(b) {
function p(o) {
console.log(typeof o, o);
}
p(function(b) {
var a = b += 1;
--b;
return a;
}("object"));
}
expect_stdout: "string"
expect_stdout: "string object1"
}
issue_3375_2: {
options = {
assignments: true,
reduce_vars: true,
}
input: {
function p(o) {
console.log(typeof o, o);
}
p(function(b) {
var a = b -= 1;
--b;
return a;
}("object"));
}
expect: {
function p(o) {
console.log(typeof o, o);
}
p(function(b) {
var a = --b;
--b;
return a;
}("object"));
}
expect_stdout: "number NaN"
}
issue_3427: {

File diff suppressed because it is too large Load Diff

View File

@@ -33,10 +33,10 @@ unsafe_comps: {
}
expect: {
var obj1, obj2;
obj2 < obj1 ? g1() : f1();
obj1 < obj2 ? f2() : g2();
obj1 < obj2 ? g3() : f3();
obj2 < obj1 ? f4() : g4();
(obj2 < obj1 ? g1 : f1)();
(obj1 < obj2 ? f2 : g2)();
(obj1 < obj2 ? g3 : f3)();
(obj2 < obj1 ? f4 : g4)();
}
}

View File

@@ -41,7 +41,7 @@ ifs_2: {
}
expect: {
foo ? x() : bar ? y() : baz && z();
foo ? x() : bar ? y() : baz ? z() : t();
(foo ? x : bar ? y : baz ? z : t)();
}
}
@@ -289,7 +289,7 @@ cond_5: {
}
}
expect: {
some_condition() && some_other_condition() ? do_something() : alternate();
(some_condition() && some_other_condition() ? do_something : alternate)();
some_condition() && some_other_condition() && do_something();
}
}
@@ -663,6 +663,69 @@ cond_9: {
}
}
cond_10: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f(a) {
if (1 == a) return "foo";
if (2 == a) return "foo";
if (3 == a) return "foo";
if (4 == a) return 42;
if (5 == a) return "foo";
if (6 == a) return "foo";
return "bar";
}
console.log(f(1), f(2), f(3), f(4), f(5), f(6), f(7));
}
expect: {
function f(a) {
return 1 == a || 2 == a || 3 == a ? "foo" : 4 == a ? 42 : 5 == a || 6 == a ? "foo" : "bar";
}
console.log(f(1), f(2), f(3), f(4), f(5), f(6), f(7));
}
expect_stdout: "foo foo foo 42 foo foo bar"
}
cond_11: {
options = {
conditionals: true,
}
input: {
var o = {
p: "foo",
q: function() {
return this.p;
}
};
function f() {
return "bar";
}
function g(a) {
return a ? f() : o.q();
}
console.log(g(0), g(1));
}
expect: {
var o = {
p: "foo",
q: function() {
return this.p;
}
};
function f() {
return "bar";
}
function g(a) {
return a ? f() : o.q();
}
console.log(g(0), g(1));
}
expect_stdout: "foo bar"
}
ternary_boolean_consequent: {
options = {
booleans: true,

133
test/compress/directives.js Normal file
View File

@@ -0,0 +1,133 @@
simple_statement_is_not_a_directive: {
input: {
"use strict"
.split(" ")
.forEach(function(s) {
console.log(s);
});
console.log(!this); // is strict mode?
(function() {
"directive"
""
"use strict"
"hello world"
.split(" ")
.forEach(function(s) {
console.log(s);
});
console.log(!this); // is strict mode?
})();
}
expect: {
"use strict".split(" ").forEach(function(s) {
console.log(s);
});
console.log(!this);
(function() {
"directive";
"";
"use strict";
"hello world".split(" ").forEach(function(s) {
console.log(s);
});
console.log(!this);
})();
}
expect_stdout: [
"use",
"strict",
"false",
"hello",
"world",
"true",
]
}
drop_lone_use_strict: {
options = {
directives: true,
side_effects: true,
}
input: {
function f1() {
"use strict";
}
function f2() {
"use strict";
function f3() {
"use strict";
}
}
(function f4() {
"use strict";
})();
}
expect: {
function f1() {
}
function f2() {
"use strict";
function f3() {
}
}
}
}
issue_3166: {
options = {
directives: true,
}
input: {
"foo";
"use strict";
function f() {
"use strict";
"bar";
"use asm";
}
}
expect: {
"use strict";
function f() {
"use asm";
}
}
}
valid_after_invalid_1: {
input: {
console.log(typeof function() {
"use\x20strict";
"use strict";
return this;
}());
}
expect: {
console.log(typeof function() {
"use\x20strict";
"use strict";
return this;
}());
}
expect_stdout: "undefined"
}
valid_after_invalid_2: {
options = {
directives: true,
}
input: {
console.log(typeof function() {
"use\x20strict";
"use strict";
return this;
}());
}
expect: {
console.log(typeof function() {
"use strict";
return this;
}());
}
expect_stdout: "undefined"
}

View File

@@ -2235,3 +2235,34 @@ function_assign: {
}
expect_stdout: "PASS"
}
issue_3598: {
options = {
collapse_vars: true,
reduce_vars: true,
unused: true,
}
input: {
var a = "FAIL";
try {
(function() {
var b = void 0;
a = "PASS";
c.p = 0;
var c = b[!1];
})();
} catch (e) {}
console.log(a);
}
expect: {
var a = "FAIL";
try {
(function() {
a = "PASS";
var c = (void (c.p = 0))[!1];
})();
} catch (e) {}
console.log(a);
}
expect_stdout: "PASS"
}

View File

@@ -2057,3 +2057,107 @@ threshold_evaluate_999: {
}
expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
}
collapse_vars_regexp: {
options = {
booleans: true,
collapse_vars: true,
comparisons: true,
conditionals: true,
dead_code: true,
evaluate: true,
hoist_funs: true,
if_return: true,
join_vars: true,
keep_fargs: true,
loops: false,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unsafe_regexp: true,
unused: true,
}
input: {
function f1() {
var k = 9;
var rx = /[A-Z]+/;
return [rx, k];
}
function f2() {
var rx = /ab*/g;
return function(s) {
return rx.exec(s);
};
}
function f3() {
var rx = /ab*/g;
return function() {
return rx;
};
}
(function() {
var result;
var s = "acdabcdeabbb";
var rx = /ab*/g;
while (result = rx.exec(s))
console.log(result[0]);
})();
(function() {
var result;
var s = "acdabcdeabbb";
var rx = f2();
while (result = rx(s))
console.log(result[0]);
})();
(function() {
var result;
var s = "acdabcdeabbb";
var rx = f3();
while (result = rx().exec(s))
console.log(result[0]);
})();
}
expect: {
function f1() {
return [/[A-Z]+/, 9];
}
function f2() {
var rx = /ab*/g;
return function(s) {
return rx.exec(s);
};
}
function f3() {
var rx = /ab*/g;
return function() {
return rx;
};
}
(function() {
var result, rx = /ab*/g;
while (result = rx.exec("acdabcdeabbb"))
console.log(result[0]);
})();
(function() {
var result, rx = f2();
while (result = rx("acdabcdeabbb"))
console.log(result[0]);
})();
(function() {
var result, rx = f3();
while (result = rx().exec("acdabcdeabbb"))
console.log(result[0]);
})();
}
expect_stdout: [
"a",
"ab",
"abbb",
"a",
"ab",
"abbb",
"a",
"ab",
"abbb",
]
}

View File

@@ -2004,57 +2004,6 @@ deduplicate_parenthesis: {
expect_exact: "({}).a=b;({}.a=b)();(function(){}).a=b;(function(){}.a=b)();"
}
drop_lone_use_strict: {
options = {
directives: true,
side_effects: true,
}
input: {
function f1() {
"use strict";
}
function f2() {
"use strict";
function f3() {
"use strict";
}
}
(function f4() {
"use strict";
})();
}
expect: {
function f1() {
}
function f2() {
"use strict";
function f3() {
}
}
}
}
issue_3166: {
options = {
directives: true,
}
input: {
"foo";
"use strict";
function f() {
"use strict";
"bar";
"use asm";
}
}
expect: {
"use strict";
function f() {
"use asm";
}
}
}
issue_3016_1: {
options = {
inline: true,
@@ -3568,3 +3517,191 @@ pr_3592_2: {
}
expect_stdout: "PASS"
}
inline_use_strict: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
console.log(function() {
"use strict";
return function() {
"use strict";
var a = "foo";
a += "bar";
return a;
};
}()());
}
expect: {
console.log("foobar");
}
expect_stdout: "foobar"
}
pr_3595_1: {
options = {
collapse_vars: false,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var g = [ "PASS" ];
function problem(arg) {
return g.indexOf(arg);
}
function unused(arg) {
return problem(arg);
}
function a(arg) {
return problem(arg);
}
function b(problem) {
return g[problem];
}
function c(arg) {
return b(a(arg));
}
console.log(c("PASS"));
}
expect: {
var g = [ "PASS" ];
function problem(arg) {
return g.indexOf(arg);
}
console.log((arg = "PASS", function(problem) {
return g[problem];
}(function(arg) {
return problem(arg);
}(arg))));
var arg;
}
expect_stdout: "PASS"
}
pr_3595_2: {
options = {
collapse_vars: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var g = [ "PASS" ];
function problem(arg) {
return g.indexOf(arg);
}
function unused(arg) {
return problem(arg);
}
function a(arg) {
return problem(arg);
}
function b(problem) {
return g[problem];
}
function c(arg) {
return b(a(arg));
}
console.log(c("PASS"));
}
expect: {
var g = [ "PASS" ];
function problem(arg) {
return g.indexOf(arg);
}
console.log(function(problem) {
return g[problem];
}(function(arg) {
return problem(arg);
}("PASS")));
}
expect_stdout: "PASS"
}
pr_3595_3: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var g = [ "PASS" ];
function problem(arg) {
return g.indexOf(arg);
}
function unused(arg) {
return problem(arg);
}
function a(arg) {
return problem(arg);
}
function b(problem) {
return g[problem];
}
function c(arg) {
return b(a(arg));
}
console.log(c("PASS"));
}
expect: {
var g = [ "PASS" ];
console.log(function(problem) {
return g[problem];
}(function(arg) {
return g.indexOf(arg);
}("PASS")));
}
expect_stdout: "PASS"
}
pr_3595_4: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var g = [ "PASS" ];
function problem(arg) {
return g.indexOf(arg);
}
function unused(arg) {
return problem(arg);
}
function a(arg) {
return problem(arg);
}
function b(problem) {
return g[problem];
}
function c(arg) {
return b(a(arg));
}
console.log(c("PASS"));
}
expect: {
var g = [ "PASS" ];
console.log((problem = g.indexOf("PASS"), g[problem]));
var problem;
}
expect_stdout: "PASS"
}

View File

@@ -1,55 +1,107 @@
html_comment_in_expression: {
input: {
function f(a, b, x, y) { return a < !--b && x-- > y; }
(function(a, b) {
console.log(a < !--b && a-- > b, a, b);
})(1, 2);
}
expect_exact: "function f(a,b,x,y){return a< !--b&&x-- >y}";
expect_exact: "(function(a,b){console.log(a<! --b&&a-- >b,a,b)})(1,2);"
expect_stdout: "false 1 1"
}
html_comment_in_less_than: {
input: {
function f(a, b) { return a < !--b; }
(function(a, b, c) {
console.log(
a < !--b,
a < !--b + c,
a + b < !--c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "function f(a,b){return a< !--b}";
expect_exact: "(function(a,b,c){console.log(a<! --b,a<! --b+c,a+b<! --c,a,b,c)})(1,2,3);"
expect_stdout: "false true false 1 0 2"
}
html_comment_in_left_shift: {
input: {
function f(a, b) { return a << !--b; }
(function(a, b, c) {
console.log(
a << !--b,
a << !--b + c,
a + b << !--c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "function f(a,b){return a<< !--b}";
}
html_comment_in_right_shift: {
input: {
function f(a, b) { return a-- >> b; }
}
expect_exact: "function f(a,b){return a-- >>b}";
}
html_comment_in_zero_fill_right_shift: {
input: {
function f(a, b) { return a-- >>> b; }
}
expect_exact: "function f(a,b){return a-- >>>b}";
expect_exact: "(function(a,b,c){console.log(a<<! --b,a<<! --b+c,a+b<<! --c,a,b,c)})(1,2,3);"
expect_stdout: "1 16 1 1 0 2"
}
html_comment_in_greater_than: {
input: {
function f(a, b) { return a-- > b; }
(function(a, b, c) {
console.log(
a-- > b,
a-- > b + c,
a + b-- > c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "function f(a,b){return a-- >b}";
expect_exact: "(function(a,b,c){console.log(a-- >b,a-- >b+c,a+b-- >c,a,b,c)})(1,2,3);"
expect_stdout: "false false false -1 1 3"
}
html_comment_in_greater_than_or_equal: {
input: {
function f(a, b) { return a-- >= b; }
(function(a, b, c) {
console.log(
a-- >= b,
a-- >= b + c,
a + b-- >= c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "function f(a,b){return a-- >=b}";
expect_exact: "(function(a,b,c){console.log(a-- >=b,a-- >=b+c,a+b-- >=c,a,b,c)})(1,2,3);"
expect_stdout: "false false false -1 1 3"
}
html_comment_in_right_shift: {
input: {
(function(a, b, c) {
console.log(
a-- >> b,
a-- >> b + c,
a + b-- >> c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "(function(a,b,c){console.log(a-- >>b,a-- >>b+c,a+b-- >>c,a,b,c)})(1,2,3);"
expect_stdout: "0 0 0 -1 1 3"
}
html_comment_in_zero_fill_right_shift: {
input: {
(function(a, b, c) {
console.log(
a-- >>> b,
a-- >>> b + c,
a + b-- >>> c,
a, b, c
);
})(1, 2, 3);
}
expect_exact: "(function(a,b,c){console.log(a-- >>>b,a-- >>>b+c,a+b-- >>>c,a,b,c)})(1,2,3);"
expect_stdout: "0 0 0 -1 1 3"
}
html_comment_in_string_literal: {
input: {
function f() { return "<!--HTML-->comment in<!--string literal-->"; }
console.log("<!--HTML-->comment in<!--string literal-->".length);
}
expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}';
expect_exact: 'console.log("\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e".length);'
expect_stdout: "42"
}

View File

@@ -544,3 +544,32 @@ if_body_return_3: {
"PASS",
]
}
issue_3600: {
options = {
if_return: true,
inline: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
(function() {
if ([ ][c++]); else return;
return void function() {
var b = --b, a = c = 42;
return c;
}();
})();
console.log(c);
}
expect: {
var c = 0;
(function() {
if ([][c++]) b = --b, c = 42;
var b;
})();
console.log(c);
}
expect_stdout: "1"
}

View File

@@ -30,7 +30,7 @@ non_hoisted_function_after_return: {
}
expect: {
function foo(x) {
return x ? bar() : baz();
return (x ? bar : baz)();
function bar() { return 7 }
function baz() { return 8 }
}
@@ -181,7 +181,7 @@ non_hoisted_function_after_return_strict: {
expect: {
"use strict";
function foo(x) {
return x ? bar() : baz();
return (x ? bar : baz)();
function bar() { return 7 }
function baz() { return 8 }
}

View File

@@ -21,7 +21,7 @@ cond_5: {
}
}
expect: {
some_condition() && some_other_condition() ? do_something() : alternate();
(some_condition() && some_other_condition() ? do_something : alternate)();
if (some_condition() && some_other_condition()) do_something();
}
}

View File

@@ -873,13 +873,13 @@ iife_func_side_effects: {
function z() {
console.log("z");
}
(function(a, b) {
(function(b) {
return function() {
console.log("FAIL");
} + b();
})(x(), function() {
})((x(), function() {
return y();
}, z());
}), z());
}
expect_stdout: [
"x",
@@ -1155,3 +1155,303 @@ issue_3423_2: {
}
expect_stdout: "1"
}
collapse_vars_repeated: {
options = {
booleans: true,
collapse_vars: true,
comparisons: true,
conditionals: true,
dead_code: true,
evaluate: true,
hoist_funs: true,
if_return: true,
join_vars: true,
keep_fargs: "strict",
loops: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
var dummy = 3, a = 5, unused = 2, a = 1, a = 3;
return -a;
}
function f2(x) {
var a = 3, a = x;
return a;
}
(function(x) {
var a = "GOOD" + x, e = "BAD", k = "!", e = a;
console.log(e + k);
})("!"),
(function(x) {
var a = "GOOD" + x, e = "BAD" + x, k = "!", e = a;
console.log(e + k);
})("!");
}
expect: {
function f1() {
return -3;
}
function f2(x) {
return x;
}
(function() {
console.log("GOOD!!");
})(),
(function() {
console.log("GOOD!!");
})();
}
expect_stdout: true
}
chained_3: {
options = {
collapse_vars: true,
keep_fargs: "strict",
unused: true,
}
input: {
console.log(function(a, b) {
var c = a, c = b;
b++;
return c;
}(1, 2));
}
expect: {
console.log(function(b) {
var c = 1;
c = b;
b++;
return c;
}(2));
}
expect_stdout: "2"
}
replace_all_var_scope: {
rename = true
options = {
collapse_vars: true,
keep_fargs: "strict",
unused: true,
}
mangle = {}
input: {
var a = 100, b = 10;
(function(r, a) {
switch (~a) {
case (b += a):
case a++:
}
})(--b, a);
console.log(a, b);
}
expect: {
var a = 100, b = 10;
(function(c) {
switch (~a) {
case (b += a):
case c++:
}
})((--b, a));
console.log(a, b);
}
expect_stdout: "100 109"
}
issue_1583: {
options = {
keep_fargs: "strict",
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function m(t) {
(function(e) {
t = e();
})(function() {
return (function(a) {
return a;
})(function(a) {});
});
}
}
expect: {
function m(t) {
(function() {
(function() {
return (function() {
return function(a) {};
})();
})();
})();
}
}
}
issues_3267_1: {
options = {
collapse_vars: true,
conditionals: true,
dead_code: true,
evaluate: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(x) {
x();
})(function() {
(function(i) {
if (i)
return console.log("PASS");
throw "FAIL";
})(Object());
});
}
expect: {
!function() {
if (Object())
return console.log("PASS");
throw "FAIL";
}();
}
expect_stdout: "PASS"
}
trailing_argument_side_effects: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
function f() {
return "FAIL";
}
console.log(function(a, b) {
return b || "PASS";
}(f()));
}
expect: {
function f() {
return "FAIL";
}
console.log(function(b) {
return b || "PASS";
}(void f()));
}
expect_stdout: "PASS"
}
recursive_iife_1: {
options = {
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
console.log(function f(a, b) {
return b || f("FAIL", "PASS");
}());
}
expect: {
console.log(function f(a, b) {
return b || f("FAIL", "PASS");
}());
}
expect_stdout: "PASS"
}
recursive_iife_2: {
options = {
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
console.log(function f(a, b) {
return b || f("FAIL", "PASS");
}(null, 0));
}
expect: {
console.log(function f(a, b) {
return b || f("FAIL", "PASS");
}(0, 0));
}
expect_stdout: "PASS"
}
recursive_iife_3: {
options = {
keep_fargs: false,
reduce_vars: true,
unused: true,
}
input: {
var a = 1, c = "PASS";
(function() {
function f(b, d, e) {
a-- && f(null, 42, 0);
e && (c = "FAIL");
d && d.p;
}
var a_1 = f();
})();
console.log(c);
}
expect: {
var a = 1, c = "PASS";
(function() {
(function f(b, d, e) {
a-- && f(null, 42, 0);
e && (c = "FAIL");
d && d.p;
})();
})();
console.log(c);
}
expect_stdout: "PASS"
}
issue_3619: {
options = {
keep_fargs: false,
unused: true,
}
input: {
var a = 1, b = "FAIL";
(function f(c, d) {
function g() {
d && (b = "PASS", 0 <= --a && g());
0 <= --a && f(0, "function");
}
g();
})();
console.log(b);
}
expect: {
var a = 1, b = "FAIL";
(function f(c, d) {
function g() {
d && (b = "PASS", 0 <= --a && g());
0 <= --a && f(0, "function");
}
g();
})();
console.log(b);
}
expect_stdout: "PASS"
}

View File

@@ -574,9 +574,11 @@ issue_2740_3: {
console.log(x, y);
}
expect: {
L1: for (var x = 0; x < 3; x++)
for (var y = 0; y < 2; y++)
L1: for (var x = 0; x < 3; x++) {
var y = 0;
if (y < 2)
break L1;
}
console.log(x, y);
}
expect_stdout: "0 0"
@@ -753,3 +755,175 @@ empty_for_in_side_effects: {
"WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]",
]
}
issue_3631_1: {
options = {
dead_code: true,
evaluate: true,
loops: true,
reduce_vars: true,
toplevel: true,
}
input: {
var c = 0;
L: do {
for (;;) continue L;
var b = 1;
} while (b && c++);
console.log(c);
}
expect: {
var c = 0;
do {
var b;
} while (b && c++);
console.log(c);
}
expect_stdout: "0"
}
issue_3631_2: {
options = {
dead_code: true,
evaluate: true,
loops: true,
reduce_vars: true,
toplevel: true,
}
input: {
L: for (var a = 1; a--; console.log(b)) {
for (;;) continue L;
var b = "FAIL";
}
}
expect: {
for (var a = 1; a--; console.log(b))
var b;
}
expect_stdout: "undefined"
}
loop_if_break: {
options = {
dead_code: true,
loops: true,
}
input: {
function f(a, b) {
try {
while (a) {
if (b) {
break;
var c = 42;
console.log(c);
} else {
var d = false;
throw d;
}
}
} catch (e) {
console.log("E:", e);
}
console.log(a, b, c, d);
}
f(0, 0);
f(0, 1);
f(1, 0);
f(1, 1);
}
expect: {
function f(a, b) {
try {
for (;a && !b;) {
var d = false;
throw d;
var c;
}
} catch (e) {
console.log("E:", e);
}
console.log(a, b, c, d);
}
f(0, 0);
f(0, 1);
f(1, 0);
f(1, 1);
}
expect_stdout: [
"0 0 undefined undefined",
"0 1 undefined undefined",
"E: false",
"1 0 undefined false",
"1 1 undefined undefined",
]
}
loop_return: {
options = {
dead_code: true,
loops: true,
}
input: {
function f(a) {
while (a) return 42;
return "foo";
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
if (a) return 42;
return "foo";
}
console.log(f(0), f(1));
}
expect_stdout: "foo 42"
}
issue_3634_1: {
options = {
loops: true,
}
input: {
var b = 0;
L: while (++b < 2)
while (1)
if (b) break L;
console.log(b);
}
expect: {
var b = 0;
L: for (;++b < 2;)
for (;1;)
if (b) break L;
console.log(b);
}
expect_stdout: "1"
}
issue_3634_2: {
options = {
loops: true,
}
input: {
var b = 0;
L: while (++b < 2)
while (1)
if (!b)
continue L;
else
break L;
console.log(b);
}
expect: {
var b = 0;
L: for (;++b < 2;)
for (;1;)
if (!b)
continue L;
else
break L;
console.log(b);
}
expect_stdout: "1"
}

View File

@@ -945,3 +945,37 @@ issue_3593: {
}
expect_stdout: "-2"
}
unsafe_math_swap_constant: {
options = {
evaluate: true,
unsafe_math: true,
}
input: {
var a = 1, b = 2;
console.log(
a++ + b-- + 3,
a++ + b + 3,
a + b-- + 3,
a + b + 3,
a++ - b-- + 3,
a++ - b + 3,
a - b-- + 3,
a - b + 3
);
}
expect: {
var a = 1, b = 2;
console.log(
3 + a++ + b--,
a++ + b + 3,
a + b-- + 3,
a + b + 3,
3 + a++ - b--,
3 + a++ - b,
a - b-- + 3,
a - b + 3
);
}
expect_stdout: "6 6 7 6 6 8 9 10"
}

View File

@@ -6774,3 +6774,76 @@ issue_3509: {
}
expect_stdout: "PASS"
}
issue_3622: {
options = {
evaluate: true,
inline: true,
keep_fargs: "strict",
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var c = "FAIL";
!function(b, a) {
a && (c = "PASS");
}(42, this);
console.log(c);
}
expect: {
var c = "FAIL";
var a;
a = this,
!void (a && (c = "PASS")),
console.log(c);
}
expect_stdout: "PASS"
}
issue_3631_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var c = 0;
L: do {
for (;;) continue L;
var b = 1;
} while (b && c++);
console.log(c);
}
expect: {
var c = 0;
L: do {
for (;;) continue L;
var b = 1;
} while (b && c++);
console.log(c);
}
expect_stdout: "0"
}
issue_3631_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
L: for (var a = 1; a--; console.log(b)) {
for (;;) continue L;
var b = "FAIL";
}
}
expect: {
L: for (var a = 1; a--; console.log(b)) {
for (;;) continue L;
var b = "FAIL";
}
}
expect_stdout: "undefined"
}

View File

@@ -54,8 +54,8 @@ describe("Directives", function() {
[
[
'"use strict"\n',
[ "use strict"],
[ "use asm"]
[ "use strict" ],
[ "use asm" ]
],
[
'"use\\\nstrict";',
@@ -69,19 +69,19 @@ describe("Directives", function() {
],
[
'"use \\\nstrict";"use strict";',
[],
[ "use strict", "use\nstrict", "use \nstrict", "use asm" ]
[ "use strict" ],
[ "use\nstrict", "use \nstrict", "use asm" ]
],
[
'"\\76";',
[],
[ ">", "\\76" ]
[ "\\76" ],
[ ">" ]
],
[
// no ; or newline
'"use strict"',
[],
[ "use strict", "use\nstrict", "use \nstrict", "use asm" ]
[ "use strict" ],
[ "use\nstrict", "use \nstrict", "use asm" ]
],
[
';"use strict"',
@@ -106,18 +106,18 @@ describe("Directives", function() {
],
[
'function foo() {"use \\\nstrict";"use strict";',
[],
[ "use strict", "use\nstrict", "use \nstrict", "use asm" ]
[ "use strict" ],
[ "use\nstrict", "use \nstrict", "use asm" ]
],
[
'var foo = function() {"\\76";',
[],
[ ">", "\\76" ]
[ "\\76" ],
[ ">" ]
],
[
'var foo = function() {"use strict"', // no ; or newline
[],
[ "use strict", "use\nstrict", "use \nstrict", "use asm" ]
[ "use strict" ],
[ "use\nstrict", "use \nstrict", "use asm" ]
],
[
'var foo = function() {;"use strict"',
@@ -156,21 +156,24 @@ describe("Directives", function() {
});
});
});
it("Should test EXPECT_DIRECTIVE RegExp", function() {
it("Should print semicolon to separate strings from directives", function() {
[
[ "", true ],
[ "'test';", true ],
[ "'test';;", true ],
[ "'tests';\n", true ],
[ "'tests'", false ],
[ "'tests'; \n\t", true ],
[ "'tests';\n\n", true ],
[ "\n\n\"use strict\";\n\n", true ],
[ "", ';"";' ],
[ '"test";', '"test";;"";' ],
[ '"test";;', '"test";;"";' ],
[ '"tests";\n', '"tests";;"";' ],
[ '"tests"', '"tests";;"";' ],
[ '"tests"; \n\t', '"tests";;"";' ],
[ '"tests";\n\n', '"tests";;"";' ],
[ '\n\n"use strict";\n\n', '"use strict";;"";' ],
].forEach(function(test) {
var ast = UglifyJS.parse(test[0]);
ast.body.push(new UglifyJS.AST_SimpleStatement({
body: new UglifyJS.AST_String({ value: "" })
}));
var out = UglifyJS.OutputStream();
out.print(test[0]);
out.print_string("", null, true);
assert.strictEqual(out.get() === test[0] + ';""', test[1], test[0]);
ast.print(out);
assert.strictEqual(out.get(), test[1], test[0]);
});
});
it("Should only print 2 semicolons spread over 2 lines in beautify mode", function() {
@@ -178,8 +181,8 @@ describe("Directives", function() {
'"use strict";',
"'use strict';",
'"use strict";',
'"use strict";;',
"'use strict';",
'"use strict";',
";'use strict';",
"console.log('use strict');"
].join(""), {
compress: false,
@@ -201,19 +204,23 @@ describe("Directives", function() {
it("Should not add double semicolons in non-scoped block statements to avoid strings becoming directives", function() {
[
[
'{"use\x20strict"}',
'"use strict";"use\\x20strict";',
'"use strict";"use\\x20strict";'
],
[
'{"use\\x20strict"}',
'{"use strict"}'
],
[
'function foo(){"use\x20strict";}', // Valid place for directives
'function foo(){"use strict"}'
'function foo(){"use\\x20strict";}', // Valid place for directives
'function foo(){"use\\x20strict"}'
],
[
'try{"use\x20strict"}catch(e){}finally{"use\x20strict"}',
'try{"use\\x20strict"}catch(e){}finally{"use\\x20strict"}',
'try{"use strict"}catch(e){}finally{"use strict"}'
],
[
'if(1){"use\x20strict"} else {"use strict"}',
'if(1){"use\\x20strict"} else {"use strict"}',
'if(1){"use strict"}else{"use strict"}'
]
].forEach(function(test) {
@@ -225,16 +232,6 @@ describe("Directives", function() {
assert.strictEqual(result.code, test[1], test[0]);
});
});
it("Should add double semicolon when relying on automatic semicolon insertion", function() {
var result = UglifyJS.minify('"use strict";"use\\x20strict";', {
compress: false,
output: {
semicolons: false
}
});
if (result.error) throw result.error;
assert.strictEqual(result.code, '"use strict";;"use strict"\n');
});
it("Should check quote style of directives", function() {
[
// 0. Prefer double quotes, unless string contains more double quotes than single quotes
@@ -249,9 +246,9 @@ describe("Directives", function() {
'"use strict";'
],
[
'"\\\'use strict\\\'";', // Not a directive as it contains quotes
'"\\\'use strict\\\'";',
0,
';"\'use strict\'";',
'"\\\'use strict\\\'";',
],
[
"'\"use strict\"';",
@@ -273,7 +270,7 @@ describe("Directives", function() {
'"\'use strict\'";',
1,
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
"'\\'use strict\\'';",
'"\'use strict\'";',
],
[
"'\\'use strict\\'';", // Not a valid directive
@@ -305,7 +302,7 @@ describe("Directives", function() {
"'\"use strict\"';",
2,
// Intentionally causes directive breakage at cost of less logic, usage should be rare anyway
'"\\\"use strict\\\"";',
"'\"use strict\"';",
],
[
'"\\"use strict\\"";', // Not a valid directive
@@ -353,8 +350,7 @@ describe("Directives", function() {
[
// Nothing gets optimised in the compressor because "use asm" is the first statement
'"use asm";"use\\x20strict";1+1;',
// Yet, the parser noticed that "use strict" wasn't a directive
'"use asm";;"use strict";1+1;',
'"use asm";"use\\x20strict";1+1;'
],
[
'function f(){ "use strict" }',

View File

@@ -2,10 +2,18 @@ var assert = require("assert");
var UglifyJS = require("../node");
describe("Number literals", function() {
it("Should allow legacy octal literals in non-strict mode", function() {
[
"'use strict'\n.slice()\n00",
'"use strict"\n.slice()\nvar foo = 00',
].forEach(function(input) {
UglifyJS.parse(input);
});
});
it("Should not allow legacy octal literals in strict mode", function() {
var inputs = [
'"use strict";00;',
'"use strict"; var foo = 00;'
'"use strict"; var foo = 00;',
];
var test = function(input) {
return function() {

View File

@@ -59,13 +59,13 @@ describe("String literals", function() {
it("Should not throw error outside strict mode if string contains escaped octalIntegerLiteral", function() {
var tests = [
['"\\76";', ';">";'],
['"\\0"', '"\\0";'],
['"\\08"', '"\\x008";'],
['"\\008"', '"\\x008";'],
['"\\0008"', '"\\x008";'],
['"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";'],
['"use\\\n strict";\n"\\07";', ';"use strict";"\07";']
[ ';"\\76";', ';">";' ],
[ ';"\\0";', ';"\\0";' ],
[ ';"\\08"', ';"\\x008";' ],
[ ';"\\008"', ';"\\x008";' ],
[ ';"\\0008"', ';"\\x008";' ],
[ ';"use\\\n strict";\n"\\07";', ';"use strict";"\07";' ],
[ '"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";' ],
];
for (var test in tests) {
@@ -75,8 +75,8 @@ describe("String literals", function() {
});
it("Should not throw error when digit is 8 or 9", function() {
assert.equal(UglifyJS.parse('"use strict";"\\08"').print_to_string(), '"use strict";"\\x008";');
assert.equal(UglifyJS.parse('"use strict";"\\09"').print_to_string(), '"use strict";"\\x009";');
assert.equal(UglifyJS.parse('"use strict";;"\\08"').print_to_string(), '"use strict";;"\\x008";');
assert.equal(UglifyJS.parse('"use strict";;"\\09"').print_to_string(), '"use strict";;"\\x009";');
});
it("Should not unescape unpaired surrogates", function() {
@@ -93,7 +93,7 @@ describe("String literals", function() {
for (; i <= 0xFFFF; i++) {
code.push("\\u" + i.toString(16));
}
code = '"' + code.join() + '"';
code = ';"' + code.join() + '"';
var normal = UglifyJS.minify(code, {
compress: false,
mangle: false,

View File

@@ -1089,6 +1089,19 @@ function log(options) {
}
}
function fuzzy_match(original, uglified) {
original = original.split(" ", 5);
uglified = uglified.split(" ", 5);
for (var i = 0; i < 5; i++) {
if (original[i] === uglified[i]) continue;
var a = +original[i];
var b = +uglified[i];
if (Math.abs((b - a) / a) < 1e-10) continue;
return false;
}
return true;
}
var fallback_options = [ JSON.stringify({
compress: false,
mangle: false
@@ -1111,8 +1124,12 @@ for (var round = 1; round <= num_iterations; round++) {
uglify_code = uglify_code.code;
uglify_result = sandbox.run_code(uglify_code, o.toplevel);
ok = sandbox.same_stdout(original_result, uglify_result);
if (!ok && o.compress.unsafe_math) {
ok = sandbox.same_stdout(sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3")), uglify_result, o.toplevel);
if (!ok && typeof uglify_result == "string" && o.compress.unsafe_math) {
ok = fuzzy_match(original_result, uglify_result);
if (!ok) {
var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"));
ok = sandbox.same_stdout(fuzzy_result, uglify_result, o.toplevel);
}
}
} else {
uglify_code = uglify_code.error;

View File

@@ -12,17 +12,16 @@ function spawn(endTime) {
], {
stdio: [ "ignore", "pipe", "pipe" ]
}).on("exit", respawn);
var line = "";
var stdout = "";
child.stdout.on("data", function(data) {
line += data;
stdout += data;
});
child.stderr.once("data", function() {
process.exitCode = 1;
}).pipe(process.stdout);
var stderr = "";
child.stderr.on("data", trap).pipe(process.stdout);
var keepAlive = setInterval(function() {
var end = line.lastIndexOf("\r");
console.log(line.slice(line.lastIndexOf("\r", end - 1) + 1, end));
line = line.slice(end + 1);
var end = stdout.lastIndexOf("\r");
console.log(stdout.slice(stdout.lastIndexOf("\r", end - 1) + 1, end));
stdout = stdout.slice(end + 1);
}, ping);
var timer = setTimeout(function() {
clearInterval(keepAlive);
@@ -31,9 +30,17 @@ function spawn(endTime) {
}, endTime - Date.now());
function respawn() {
console.log(line);
console.log(stdout.replace(/[^\r\n]*\r/g, ""));
clearInterval(keepAlive);
clearTimeout(timer);
spawn(endTime);
}
function trap(data) {
stderr += data;
if (~stderr.indexOf("\nminify(options):\n")) {
process.exitCode = 1;
child.stderr.removeListener("data", trap);
}
}
}