Compare commits
42 Commits
harmony-v3
...
v3.3.14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e00a12741 | ||
|
|
10b3752b1e | ||
|
|
fe51a91395 | ||
|
|
951d87ca94 | ||
|
|
798fc21530 | ||
|
|
a75a046abb | ||
|
|
38f2b4579f | ||
|
|
56e2a369d0 | ||
|
|
0daa199fa8 | ||
|
|
73e98dcda4 | ||
|
|
36bca6934d | ||
|
|
ace5811691 | ||
|
|
ba7bad0dbd | ||
|
|
b8b2ac5230 | ||
|
|
ea2359381b | ||
|
|
52de64cf16 | ||
|
|
455790202a | ||
|
|
f40f5eb228 | ||
|
|
604caa09e7 | ||
|
|
29a71d3aae | ||
|
|
39a907bde3 | ||
|
|
70474310f3 | ||
|
|
b5f0f4f3a1 | ||
|
|
2905fd625a | ||
|
|
4facd94029 | ||
|
|
4b5993ff15 | ||
|
|
2351a672ea | ||
|
|
4a528c469c | ||
|
|
82d1ef0242 | ||
|
|
7fdd2082a6 | ||
|
|
e529f54e90 | ||
|
|
d626e9bf19 | ||
|
|
a2a9459684 | ||
|
|
a3dfeea144 | ||
|
|
d316fb139d | ||
|
|
83d8aa8b12 | ||
|
|
4f1c12b6fd | ||
|
|
d8e0e34354 | ||
|
|
0c4f315c02 | ||
|
|
0809699bdc | ||
|
|
2088e1c19d | ||
|
|
bf1d47180c |
15
README.md
15
README.md
@@ -598,6 +598,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
|
||||
## Compress options
|
||||
|
||||
- `arguments` (default: `true`) -- replace `arguments[index]` with function
|
||||
parameter name whenever possible.
|
||||
|
||||
- `booleans` (default: `true`) -- various optimizations for boolean context,
|
||||
for example `!!a ? b : c → a ? b : c`
|
||||
|
||||
@@ -605,8 +608,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
side effects permitting.
|
||||
|
||||
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
|
||||
e.g. `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
|
||||
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
|
||||
e.g. `!(a <= b) → a > b`, attempts to negate binary nodes, e.g.
|
||||
`a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
|
||||
|
||||
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
|
||||
expressions
|
||||
@@ -730,12 +733,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
|
||||
- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below)
|
||||
|
||||
- `unsafe_comps` (default: `false`) -- Reverse `<` and `<=` to `>` and `>=` to
|
||||
allow improved compression. This might be unsafe when an at least one of two
|
||||
operands is an object with computed values due the use of methods like `get`,
|
||||
or `valueOf`. This could cause change in execution order after operands in the
|
||||
comparison are switching. Compression only works if both `comparisons` and
|
||||
`unsafe_comps` are both set to true.
|
||||
- `unsafe_comps` (default: `false`) -- compress expressions like `a <= b` assuming
|
||||
none of the operands can be (coerced to) `NaN`.
|
||||
|
||||
- `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
|
||||
when both `args` and `code` are string literals.
|
||||
|
||||
390
lib/compress.js
390
lib/compress.js
@@ -48,6 +48,7 @@ function Compressor(options, false_by_default) {
|
||||
return new Compressor(options, false_by_default);
|
||||
TreeTransformer.call(this, this.before, this.after);
|
||||
this.options = defaults(options, {
|
||||
arguments : !false_by_default,
|
||||
booleans : !false_by_default,
|
||||
collapse_vars : !false_by_default,
|
||||
comparisons : !false_by_default,
|
||||
@@ -777,8 +778,7 @@ merge(Compressor.prototype, {
|
||||
lhs = lhs.fixed_value();
|
||||
}
|
||||
if (!lhs) return true;
|
||||
if (lhs instanceof AST_RegExp) return false;
|
||||
if (lhs instanceof AST_Constant) return true;
|
||||
if (lhs.is_constant()) return true;
|
||||
return is_lhs_read_only(lhs);
|
||||
}
|
||||
return false;
|
||||
@@ -890,6 +890,11 @@ merge(Compressor.prototype, {
|
||||
return x;
|
||||
};
|
||||
|
||||
function root_expr(prop) {
|
||||
while (prop instanceof AST_PropAccess) prop = prop.expression;
|
||||
return prop;
|
||||
}
|
||||
|
||||
function is_iife_call(node) {
|
||||
if (node.TYPE != "Call") return false;
|
||||
return node.expression instanceof AST_Function || is_iife_call(node.expression);
|
||||
@@ -913,7 +918,8 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function tighten_body(statements, compressor) {
|
||||
var scope = compressor.find_parent(AST_Scope);
|
||||
var in_loop, in_try, scope;
|
||||
find_loop_scope_try();
|
||||
var CHANGED, max_iter = 10;
|
||||
do {
|
||||
CHANGED = false;
|
||||
@@ -936,6 +942,22 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
} while (CHANGED && max_iter-- > 0);
|
||||
|
||||
function find_loop_scope_try() {
|
||||
var node = compressor.self(), level = 0;
|
||||
do {
|
||||
if (node instanceof AST_Catch || node instanceof AST_Finally) {
|
||||
level++;
|
||||
} else if (node instanceof AST_IterationStatement) {
|
||||
in_loop = true;
|
||||
} else if (node instanceof AST_Scope) {
|
||||
scope = node;
|
||||
break;
|
||||
} else if (node instanceof AST_Try) {
|
||||
in_try = true;
|
||||
}
|
||||
} while (node = compressor.parent(level++));
|
||||
}
|
||||
|
||||
// Search from right to left for assignment-like expressions:
|
||||
// - `var a = x;`
|
||||
// - `a = x;`
|
||||
@@ -948,7 +970,6 @@ merge(Compressor.prototype, {
|
||||
if (scope.uses_eval || scope.uses_with) return statements;
|
||||
var args;
|
||||
var candidates = [];
|
||||
var in_try = compressor.self() instanceof AST_Try;
|
||||
var stat_index = statements.length;
|
||||
var scanner = new TreeTransformer(function(node, descend) {
|
||||
if (abort) return node;
|
||||
@@ -972,23 +993,25 @@ merge(Compressor.prototype, {
|
||||
|| node instanceof AST_Try
|
||||
|| node instanceof AST_With
|
||||
|| parent instanceof AST_For && node !== parent.init
|
||||
|| (side_effects || !replace_all)
|
||||
|| !replace_all
|
||||
&& (node instanceof AST_SymbolRef && !node.is_declared(compressor))) {
|
||||
abort = true;
|
||||
return node;
|
||||
}
|
||||
// Stop only if candidate is found within conditional branches
|
||||
if (!stop_if_hit && (side_effects || !replace_all)
|
||||
if (!stop_if_hit
|
||||
&& (parent instanceof AST_Binary && lazy_op(parent.operator) && parent.left !== node
|
||||
|| parent instanceof AST_Conditional && parent.condition !== node
|
||||
|| parent instanceof AST_If && parent.condition !== node)) {
|
||||
stop_if_hit = parent;
|
||||
}
|
||||
// Replace variable with assignment when found
|
||||
var hit_lhs, hit_rhs;
|
||||
if (can_replace
|
||||
&& !(node instanceof AST_SymbolDeclaration)
|
||||
&& lhs.equivalent_to(node)) {
|
||||
if (stop_if_hit) {
|
||||
&& (scan_lhs && (hit_lhs = lhs.equivalent_to(node))
|
||||
|| scan_rhs && (hit_rhs = rhs.equivalent_to(node)))) {
|
||||
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
|
||||
abort = true;
|
||||
return node;
|
||||
}
|
||||
@@ -1040,7 +1063,7 @@ merge(Compressor.prototype, {
|
||||
|| node instanceof AST_PropAccess
|
||||
&& (side_effects || node.expression.may_throw_on_access(compressor))
|
||||
|| node instanceof AST_SymbolRef
|
||||
&& (lvalues[node.name] || side_effects && may_modify(node))
|
||||
&& (symbol_in_lvalues(node) || side_effects && may_modify(node))
|
||||
|| node instanceof AST_VarDef && node.value
|
||||
&& (node.name.name in lvalues || side_effects && may_modify(node.name))
|
||||
|| (sym = is_lhs(node.left, node))
|
||||
@@ -1095,18 +1118,16 @@ merge(Compressor.prototype, {
|
||||
var stop_after = null;
|
||||
var stop_if_hit = null;
|
||||
var lhs = get_lhs(candidate);
|
||||
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
|
||||
var rhs = get_rhs(candidate);
|
||||
var side_effects = lhs && lhs.has_side_effects(compressor);
|
||||
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs);
|
||||
var scan_rhs = rhs && foldable(rhs);
|
||||
if (!scan_lhs && !scan_rhs) continue;
|
||||
// Locate symbols which may execute code outside of scanning range
|
||||
var lvalues = get_lvalues(candidate);
|
||||
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
|
||||
var replace_all = value_def;
|
||||
if (!replace_all && lhs instanceof AST_SymbolRef) {
|
||||
var def = lhs.definition();
|
||||
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
|
||||
replace_all = true;
|
||||
}
|
||||
}
|
||||
var side_effects = value_has_side_effects(candidate);
|
||||
var lhs_local = is_lhs_local(lhs);
|
||||
if (!side_effects) side_effects = value_has_side_effects(candidate);
|
||||
var replace_all = replace_all_symbols();
|
||||
var may_throw = candidate.may_throw(compressor);
|
||||
var funarg = candidate.name instanceof AST_SymbolFunarg;
|
||||
var hit = funarg;
|
||||
@@ -1151,7 +1172,7 @@ merge(Compressor.prototype, {
|
||||
hit_index++;
|
||||
}
|
||||
branch.expression = branch.expression.transform(scanner);
|
||||
if (side_effects || !replace_all) break;
|
||||
if (!replace_all) break;
|
||||
}
|
||||
}
|
||||
abort = true;
|
||||
@@ -1210,9 +1231,7 @@ merge(Compressor.prototype, {
|
||||
function extract_candidates(expr) {
|
||||
hit_stack.push(expr);
|
||||
if (expr instanceof AST_Assign) {
|
||||
if (!expr.left.has_side_effects(compressor)) {
|
||||
candidates.push(hit_stack.slice());
|
||||
}
|
||||
candidates.push(hit_stack.slice());
|
||||
extract_candidates(expr.right);
|
||||
} else if (expr instanceof AST_Binary) {
|
||||
extract_candidates(expr.left);
|
||||
@@ -1265,6 +1284,8 @@ merge(Compressor.prototype, {
|
||||
} else if (expr instanceof AST_Unary) {
|
||||
if (expr.operator == "++" || expr.operator == "--") {
|
||||
candidates.push(hit_stack.slice());
|
||||
} else {
|
||||
extract_candidates(expr.expression);
|
||||
}
|
||||
} else if (expr instanceof AST_VarDef) {
|
||||
if (expr.value) {
|
||||
@@ -1319,6 +1340,7 @@ merge(Compressor.prototype, {
|
||||
return find_stop(parent, level + 1, true);
|
||||
}
|
||||
if (parent instanceof AST_Switch) return node;
|
||||
if (parent instanceof AST_Unary) return node;
|
||||
if (parent instanceof AST_VarDef) return node;
|
||||
return null;
|
||||
}
|
||||
@@ -1347,21 +1369,47 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
function get_rhs(expr) {
|
||||
if (!(candidate instanceof AST_Assign && candidate.operator == "=")) return;
|
||||
return candidate.right;
|
||||
}
|
||||
|
||||
function get_rvalue(expr) {
|
||||
return expr[expr instanceof AST_Assign ? "right" : "value"];
|
||||
}
|
||||
|
||||
function foldable(expr) {
|
||||
if (expr.is_constant()) return true;
|
||||
if (expr instanceof AST_Array) return false;
|
||||
if (expr instanceof AST_Function) return false;
|
||||
if (expr instanceof AST_Object) return false;
|
||||
if (expr instanceof AST_RegExp) return false;
|
||||
if (expr instanceof AST_Symbol) return true;
|
||||
if (!(lhs instanceof AST_SymbolRef)) return false;
|
||||
if (expr.has_side_effects(compressor)) return false;
|
||||
var circular;
|
||||
var def = lhs.definition();
|
||||
expr.walk(new TreeWalker(function(node) {
|
||||
if (circular) return true;
|
||||
if (node instanceof AST_SymbolRef && node.definition() === def) {
|
||||
circular = true;
|
||||
}
|
||||
}));
|
||||
return !circular;
|
||||
}
|
||||
|
||||
function get_lvalues(expr) {
|
||||
var lvalues = Object.create(null);
|
||||
if (expr instanceof AST_Unary) return lvalues;
|
||||
var tw = new TreeWalker(function(node, descend) {
|
||||
var sym = node;
|
||||
while (sym instanceof AST_PropAccess) sym = sym.expression;
|
||||
if (candidate instanceof AST_VarDef) {
|
||||
lvalues[candidate.name.name] = lhs;
|
||||
}
|
||||
var tw = new TreeWalker(function(node) {
|
||||
var sym = root_expr(node);
|
||||
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
|
||||
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
|
||||
}
|
||||
});
|
||||
get_rvalue(expr).walk(tw);
|
||||
expr.walk(tw);
|
||||
return lvalues;
|
||||
}
|
||||
|
||||
@@ -1393,17 +1441,49 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
}
|
||||
|
||||
function is_lhs_local(lhs) {
|
||||
var sym = root_expr(lhs);
|
||||
return sym instanceof AST_SymbolRef
|
||||
&& sym.definition().scope === scope
|
||||
&& !(in_loop
|
||||
&& (sym.name in lvalues && lvalues[sym.name] !== lhs
|
||||
|| candidate instanceof AST_Unary
|
||||
|| candidate instanceof AST_Assign && candidate.operator != "="));
|
||||
}
|
||||
|
||||
function value_has_side_effects(expr) {
|
||||
if (expr instanceof AST_Unary) return false;
|
||||
return get_rvalue(expr).has_side_effects(compressor);
|
||||
}
|
||||
|
||||
function replace_all_symbols() {
|
||||
if (side_effects) return false;
|
||||
if (value_def) return true;
|
||||
if (lhs instanceof AST_SymbolRef) {
|
||||
var def = lhs.definition();
|
||||
if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function symbol_in_lvalues(sym) {
|
||||
var lvalue = lvalues[sym.name];
|
||||
if (!lvalue) return;
|
||||
if (lvalue !== lhs) return true;
|
||||
scan_rhs = false;
|
||||
}
|
||||
|
||||
function may_modify(sym) {
|
||||
var def = sym.definition();
|
||||
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
|
||||
if (def.scope !== scope) return true;
|
||||
return !all(def.references, function(ref) {
|
||||
return ref.scope === scope;
|
||||
var s = ref.scope;
|
||||
// "block" scope within AST_Catch
|
||||
if (s.TYPE == "Scope") s = s.parent_scope;
|
||||
return s === scope;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1816,11 +1896,12 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (prop instanceof AST_Node) break;
|
||||
prop = "" + prop;
|
||||
if (compressor.has_directive("use strict")) {
|
||||
if (!all(def.value.properties, function(node) {
|
||||
return node.key != prop && node.key.name != prop;
|
||||
})) break;
|
||||
}
|
||||
var diff = compressor.has_directive("use strict") ? function(node) {
|
||||
return node.key != prop && node.key.name != prop;
|
||||
} : function(node) {
|
||||
return node.key.name != prop;
|
||||
};
|
||||
if (!all(def.value.properties, diff)) break;
|
||||
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
|
||||
key: prop,
|
||||
value: node.right
|
||||
@@ -1974,7 +2055,7 @@ merge(Compressor.prototype, {
|
||||
if (this.properties[i].value instanceof AST_Accessor) return true;
|
||||
return false;
|
||||
});
|
||||
def(AST_Function, return_false);
|
||||
def(AST_Lambda, return_false);
|
||||
def(AST_UnaryPostfix, return_false);
|
||||
def(AST_UnaryPrefix, function() {
|
||||
return this.operator == "void";
|
||||
@@ -1993,8 +2074,9 @@ merge(Compressor.prototype, {
|
||||
})
|
||||
def(AST_Dot, function(compressor) {
|
||||
if (!is_strict(compressor)) return false;
|
||||
if (this.expression instanceof AST_Function && this.property == "prototype") return false;
|
||||
return true;
|
||||
var exp = this.expression;
|
||||
if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
|
||||
return !(exp instanceof AST_Lambda && this.property == "prototype");
|
||||
});
|
||||
def(AST_Sequence, function(compressor) {
|
||||
return this.tail_node()._dot_throw(compressor);
|
||||
@@ -2201,6 +2283,7 @@ merge(Compressor.prototype, {
|
||||
"slice",
|
||||
].concat(object_fns),
|
||||
Boolean: object_fns,
|
||||
Function: object_fns,
|
||||
Number: [
|
||||
"toExponential",
|
||||
"toFixed",
|
||||
@@ -2282,8 +2365,14 @@ merge(Compressor.prototype, {
|
||||
// descendant of AST_Node.
|
||||
AST_Node.DEFMETHOD("evaluate", function(compressor){
|
||||
if (!compressor.option("evaluate")) return this;
|
||||
var val = this._eval(compressor, 1);
|
||||
return !val || val instanceof RegExp || typeof val != "object" ? val : this;
|
||||
var cached = [];
|
||||
var val = this._eval(compressor, cached, 1);
|
||||
cached.forEach(function(node) {
|
||||
delete node._eval;
|
||||
});
|
||||
if (!val || val instanceof RegExp) return val;
|
||||
if (typeof val == "function" || typeof val == "object") return this;
|
||||
return val;
|
||||
});
|
||||
var unaryPrefix = makePredicate("! ~ - + void");
|
||||
AST_Node.DEFMETHOD("is_constant", function(){
|
||||
@@ -2305,16 +2394,23 @@ merge(Compressor.prototype, {
|
||||
def(AST_Constant, function(){
|
||||
return this.getValue();
|
||||
});
|
||||
def(AST_Array, function(compressor, depth) {
|
||||
def(AST_Function, function(compressor) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var fn = function() {};
|
||||
fn.node = this;
|
||||
fn.toString = function() {
|
||||
return "function(){}";
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
return this;
|
||||
});
|
||||
def(AST_Array, function(compressor, cached, depth) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var elements = [];
|
||||
for (var i = 0, len = this.elements.length; i < len; i++) {
|
||||
var element = this.elements[i];
|
||||
if (element instanceof AST_Function) {
|
||||
elements.push(element);
|
||||
continue;
|
||||
}
|
||||
var value = element._eval(compressor, depth);
|
||||
var value = element._eval(compressor, cached, depth);
|
||||
if (element === value) return this;
|
||||
elements.push(value);
|
||||
}
|
||||
@@ -2322,7 +2418,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return this;
|
||||
});
|
||||
def(AST_Object, function(compressor, depth) {
|
||||
def(AST_Object, function(compressor, cached, depth) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var val = {};
|
||||
for (var i = 0, len = this.properties.length; i < len; i++) {
|
||||
@@ -2331,21 +2427,22 @@ merge(Compressor.prototype, {
|
||||
if (key instanceof AST_Symbol) {
|
||||
key = key.name;
|
||||
} else if (key instanceof AST_Node) {
|
||||
key = key._eval(compressor, depth);
|
||||
key = key._eval(compressor, cached, depth);
|
||||
if (key === prop.key) return this;
|
||||
}
|
||||
if (typeof Object.prototype[key] === 'function') {
|
||||
return this;
|
||||
}
|
||||
if (prop.value instanceof AST_Function) continue;
|
||||
val[key] = prop.value._eval(compressor, depth);
|
||||
val[key] = prop.value._eval(compressor, cached, depth);
|
||||
if (val[key] === prop.value) return this;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
return this;
|
||||
});
|
||||
def(AST_UnaryPrefix, function(compressor, depth) {
|
||||
var non_converting_unary = makePredicate("! typeof void");
|
||||
def(AST_UnaryPrefix, function(compressor, cached, depth) {
|
||||
var e = this.expression;
|
||||
// Function would be evaluated to an array and so typeof would
|
||||
// incorrectly return 'object'. Hence making is a special case.
|
||||
@@ -2356,7 +2453,8 @@ merge(Compressor.prototype, {
|
||||
&& e.fixed_value() instanceof AST_Lambda)) {
|
||||
return typeof function(){};
|
||||
}
|
||||
e = e._eval(compressor, depth);
|
||||
if (!non_converting_unary(this.operator)) depth++;
|
||||
e = e._eval(compressor, cached, depth);
|
||||
if (e === this.expression) return this;
|
||||
switch (this.operator) {
|
||||
case "!": return !e;
|
||||
@@ -2372,10 +2470,12 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return this;
|
||||
});
|
||||
def(AST_Binary, function(compressor, depth) {
|
||||
var left = this.left._eval(compressor, depth);
|
||||
var non_converting_binary = makePredicate("&& || === !==");
|
||||
def(AST_Binary, function(compressor, cached, depth) {
|
||||
if (!non_converting_binary(this.operator)) depth++;
|
||||
var left = this.left._eval(compressor, cached, depth);
|
||||
if (left === this.left) return this;
|
||||
var right = this.right._eval(compressor, depth);
|
||||
var right = this.right._eval(compressor, cached, depth);
|
||||
if (right === this.right) return this;
|
||||
var result;
|
||||
switch (this.operator) {
|
||||
@@ -2409,27 +2509,28 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return result;
|
||||
});
|
||||
def(AST_Conditional, function(compressor, depth) {
|
||||
var condition = this.condition._eval(compressor, depth);
|
||||
def(AST_Conditional, function(compressor, cached, depth) {
|
||||
var condition = this.condition._eval(compressor, cached, depth);
|
||||
if (condition === this.condition) return this;
|
||||
var node = condition ? this.consequent : this.alternative;
|
||||
var value = node._eval(compressor, depth);
|
||||
var value = node._eval(compressor, cached, depth);
|
||||
return value === node ? this : value;
|
||||
});
|
||||
def(AST_SymbolRef, function(compressor, depth) {
|
||||
def(AST_SymbolRef, function(compressor, cached, depth) {
|
||||
var fixed = this.fixed_value();
|
||||
if (!fixed) return this;
|
||||
var value;
|
||||
if (HOP(fixed, "_eval")) {
|
||||
if (cached.indexOf(fixed) >= 0) {
|
||||
value = fixed._eval();
|
||||
} else {
|
||||
this._eval = return_this;
|
||||
value = fixed._eval(compressor, depth);
|
||||
value = fixed._eval(compressor, cached, depth);
|
||||
delete this._eval;
|
||||
if (value === fixed) return this;
|
||||
fixed._eval = function() {
|
||||
return value;
|
||||
};
|
||||
cached.push(fixed);
|
||||
}
|
||||
if (value && typeof value == "object") {
|
||||
var escaped = this.definition().escaped;
|
||||
@@ -2464,11 +2565,11 @@ merge(Compressor.prototype, {
|
||||
],
|
||||
};
|
||||
convert_to_predicate(static_values);
|
||||
def(AST_PropAccess, function(compressor, depth) {
|
||||
def(AST_PropAccess, function(compressor, cached, depth) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var key = this.property;
|
||||
if (key instanceof AST_Node) {
|
||||
key = key._eval(compressor, depth);
|
||||
key = key._eval(compressor, cached, depth);
|
||||
if (key === this.property) return this;
|
||||
}
|
||||
var exp = this.expression;
|
||||
@@ -2477,19 +2578,27 @@ merge(Compressor.prototype, {
|
||||
if (!(static_values[exp.name] || return_false)(key)) return this;
|
||||
val = global_objs[exp.name];
|
||||
} else {
|
||||
val = exp._eval(compressor, depth + 1);
|
||||
val = exp._eval(compressor, cached, depth + 1);
|
||||
if (!val || val === exp || !HOP(val, key)) return this;
|
||||
if (typeof val == "function") switch (key) {
|
||||
case "name":
|
||||
return val.node.name ? val.node.name.name : "";
|
||||
case "length":
|
||||
return val.node.argnames.length;
|
||||
default:
|
||||
return this;
|
||||
}
|
||||
}
|
||||
return val[key];
|
||||
}
|
||||
return this;
|
||||
});
|
||||
def(AST_Call, function(compressor, depth) {
|
||||
def(AST_Call, function(compressor, cached, depth) {
|
||||
var exp = this.expression;
|
||||
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||
var key = exp.property;
|
||||
if (key instanceof AST_Node) {
|
||||
key = key._eval(compressor, depth);
|
||||
key = key._eval(compressor, cached, depth);
|
||||
if (key === exp.property) return this;
|
||||
}
|
||||
var val;
|
||||
@@ -2498,13 +2607,13 @@ merge(Compressor.prototype, {
|
||||
if (!(static_fns[e.name] || return_false)(key)) return this;
|
||||
val = global_objs[e.name];
|
||||
} else {
|
||||
val = e._eval(compressor, depth + 1);
|
||||
val = e._eval(compressor, cached, depth + 1);
|
||||
if (val === e || !(val && native_fns[val.constructor.name] || return_false)(key)) return this;
|
||||
}
|
||||
var args = [];
|
||||
for (var i = 0, len = this.args.length; i < len; i++) {
|
||||
var arg = this.args[i];
|
||||
var value = arg._eval(compressor, depth);
|
||||
var value = arg._eval(compressor, cached, depth);
|
||||
if (arg === value) return this;
|
||||
args.push(value);
|
||||
}
|
||||
@@ -2810,6 +2919,9 @@ merge(Compressor.prototype, {
|
||||
def(AST_ObjectProperty, function(compressor){
|
||||
return this.value.may_throw(compressor);
|
||||
});
|
||||
def(AST_Return, function(compressor){
|
||||
return this.value && this.value.may_throw(compressor);
|
||||
});
|
||||
def(AST_Sequence, function(compressor){
|
||||
return any(this.expressions, compressor);
|
||||
});
|
||||
@@ -2829,8 +2941,7 @@ merge(Compressor.prototype, {
|
||||
return !this.is_declared(compressor);
|
||||
});
|
||||
def(AST_Try, function(compressor){
|
||||
return any(this.body, compressor)
|
||||
|| this.bcatch && this.bcatch.may_throw(compressor)
|
||||
return this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor)
|
||||
|| this.bfinally && this.bfinally.may_throw(compressor);
|
||||
});
|
||||
def(AST_Unary, function(compressor){
|
||||
@@ -2959,6 +3070,16 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Lambda, function(self, compressor){
|
||||
tighten_body(self.body, compressor);
|
||||
if (compressor.option("side_effects")
|
||||
&& self.body.length == 1
|
||||
&& self.body[0] === compressor.has_directive("use strict")) {
|
||||
self.body.length = 0;
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("drop_unused", function(compressor){
|
||||
if (!compressor.option("unused")) return;
|
||||
if (compressor.has_directive("use asm")) return;
|
||||
@@ -2966,11 +3087,20 @@ merge(Compressor.prototype, {
|
||||
if (self.uses_eval || self.uses_with) return;
|
||||
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
|
||||
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
|
||||
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node) {
|
||||
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
|
||||
var sym;
|
||||
if (node instanceof AST_Assign && (node.write_only || node.operator == "=")) {
|
||||
return node.left;
|
||||
sym = node.left;
|
||||
} else if (node instanceof AST_Unary && node.write_only) {
|
||||
sym = node.expression;
|
||||
}
|
||||
if (node instanceof AST_Unary && node.write_only) return node.expression;
|
||||
if (/strict/.test(compressor.option("pure_getters"))) {
|
||||
while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) {
|
||||
if (sym instanceof AST_Sub) props.unshift(sym.property);
|
||||
sym = sym.expression;
|
||||
}
|
||||
}
|
||||
return sym;
|
||||
};
|
||||
var in_use = [];
|
||||
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
|
||||
@@ -3046,17 +3176,26 @@ merge(Compressor.prototype, {
|
||||
function before(node, descend, in_list) {
|
||||
var parent = tt.parent();
|
||||
if (drop_vars) {
|
||||
var sym = assign_as_unused(node);
|
||||
var props = [], sym = assign_as_unused(node, props);
|
||||
if (sym instanceof AST_SymbolRef) {
|
||||
var def = sym.definition();
|
||||
var in_use = def.id in in_use_ids;
|
||||
var value = null;
|
||||
if (node instanceof AST_Assign) {
|
||||
if (!in_use || def.id in fixed_ids && fixed_ids[def.id] !== node) {
|
||||
return maintain_this_binding(parent, node, node.right.transform(tt));
|
||||
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
|
||||
value = node.right;
|
||||
}
|
||||
} else if (!in_use) return make_node(AST_Number, node, {
|
||||
value: 0
|
||||
});
|
||||
} else if (!in_use) {
|
||||
value = make_node(AST_Number, node, {
|
||||
value: 0
|
||||
});
|
||||
}
|
||||
if (value) {
|
||||
props.push(value);
|
||||
return maintain_this_binding(parent, node, make_sequence(node, props.map(function(prop) {
|
||||
return prop.transform(tt);
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scope !== self) return;
|
||||
@@ -3234,12 +3373,15 @@ merge(Compressor.prototype, {
|
||||
self.transform(tt);
|
||||
|
||||
function scan_ref_scoped(node, descend) {
|
||||
var node_def, sym = assign_as_unused(node);
|
||||
var node_def, props = [], sym = assign_as_unused(node, props);
|
||||
if (sym instanceof AST_SymbolRef
|
||||
&& self.variables.get(sym.name) === (node_def = sym.definition())) {
|
||||
props.forEach(function(prop) {
|
||||
prop.walk(tw);
|
||||
});
|
||||
if (node instanceof AST_Assign) {
|
||||
node.right.walk(tw);
|
||||
if (!node_def.chained && node.left.fixed_value() === node.right) {
|
||||
if (node.left === sym && !node_def.chained && sym.fixed_value() === node.right) {
|
||||
fixed_ids[node_def.id] = node;
|
||||
}
|
||||
}
|
||||
@@ -3409,7 +3551,7 @@ merge(Compressor.prototype, {
|
||||
|
||||
AST_Scope.DEFMETHOD("make_var_name", function(prefix) {
|
||||
var var_names = this.var_names();
|
||||
prefix = prefix.replace(/[^a-z_$]+/ig, "_");
|
||||
prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
|
||||
var name = prefix;
|
||||
for (var i = 0; var_names[name]; i++) name = prefix + "$" + i;
|
||||
var_names[name] = true;
|
||||
@@ -3507,7 +3649,15 @@ merge(Compressor.prototype, {
|
||||
if (this.expression instanceof AST_Function
|
||||
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
||||
var node = this.clone();
|
||||
node.expression.process_expression(false, compressor);
|
||||
var exp = node.expression;
|
||||
exp.process_expression(false, compressor);
|
||||
exp.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_Return && node.value) {
|
||||
node.value = node.value.drop_side_effect_free(compressor);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope && node !== exp) return true;
|
||||
}));
|
||||
return node;
|
||||
}
|
||||
return this;
|
||||
@@ -3543,10 +3693,7 @@ merge(Compressor.prototype, {
|
||||
return this;
|
||||
}
|
||||
this.write_only = true;
|
||||
while (left instanceof AST_PropAccess) {
|
||||
left = left.expression;
|
||||
}
|
||||
if (left.is_constant_expression(compressor.find_parent(AST_Scope))) {
|
||||
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
|
||||
return this.right.drop_side_effect_free(compressor);
|
||||
}
|
||||
return this;
|
||||
@@ -3671,6 +3818,13 @@ merge(Compressor.prototype, {
|
||||
]
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
|
||||
condition: make_sequence(self.condition, [
|
||||
self.body.body,
|
||||
self.condition
|
||||
]),
|
||||
body: make_node(AST_EmptyStatement, self)
|
||||
}).optimize(compressor);
|
||||
return self;
|
||||
});
|
||||
|
||||
@@ -4321,7 +4475,6 @@ merge(Compressor.prototype, {
|
||||
var comp = new Compressor(compressor.options);
|
||||
ast = ast.transform(comp);
|
||||
ast.figure_out_scope(mangle);
|
||||
base54.reset();
|
||||
ast.compute_char_frequency(mangle);
|
||||
ast.mangle_names(mangle);
|
||||
var fun;
|
||||
@@ -4378,6 +4531,7 @@ merge(Compressor.prototype, {
|
||||
&& !self.pure
|
||||
&& !fn.contains_this()
|
||||
&& can_inject_symbols()) {
|
||||
fn._squeezed = true;
|
||||
return make_sequence(self, flatten_fn()).optimize(compressor);
|
||||
}
|
||||
if (compressor.option("side_effects") && all(fn.body, is_empty)) {
|
||||
@@ -4436,6 +4590,8 @@ merge(Compressor.prototype, {
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
} else if (line instanceof AST_EmptyStatement) {
|
||||
continue;
|
||||
} else if (stat) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -4890,11 +5046,9 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
self = best_of(compressor, self, negated);
|
||||
}
|
||||
if (compressor.option("unsafe_comps")) {
|
||||
switch (self.operator) {
|
||||
case "<": reverse(">"); break;
|
||||
case "<=": reverse(">="); break;
|
||||
}
|
||||
switch (self.operator) {
|
||||
case ">": reverse("<"); break;
|
||||
case ">=": reverse("<="); break;
|
||||
}
|
||||
}
|
||||
if (self.operator == "+") {
|
||||
@@ -5429,7 +5583,7 @@ merge(Compressor.prototype, {
|
||||
node = parent;
|
||||
parent = compressor.parent(level++);
|
||||
if (parent instanceof AST_Exit) {
|
||||
if (in_try(level, parent instanceof AST_Throw)) break;
|
||||
if (in_try(level, parent)) break;
|
||||
if (is_reachable(def.scope, [ def ])) break;
|
||||
if (self.operator == "=") return self.right;
|
||||
def.fixed = false;
|
||||
@@ -5463,13 +5617,17 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return self;
|
||||
|
||||
function in_try(level, no_catch) {
|
||||
function in_try(level, node) {
|
||||
var right = self.right;
|
||||
self.right = make_node(AST_Null, right);
|
||||
var may_throw = node.may_throw(compressor);
|
||||
self.right = right;
|
||||
var scope = self.left.definition().scope;
|
||||
var parent;
|
||||
while ((parent = compressor.parent(level++)) !== scope) {
|
||||
if (parent instanceof AST_Try) {
|
||||
if (parent.bfinally) return true;
|
||||
if (no_catch && parent.bcatch) return true;
|
||||
if (may_throw && parent.bcatch) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5574,6 +5732,18 @@ merge(Compressor.prototype, {
|
||||
consequent
|
||||
]).optimize(compressor);
|
||||
}
|
||||
// x ? (y, w) : (z, w) --> x ? y : z, w
|
||||
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
|
||||
&& consequent.tail_node().equivalent_to(alternative.tail_node())) {
|
||||
return make_sequence(self, [
|
||||
make_node(AST_Conditional, self, {
|
||||
condition: self.condition,
|
||||
consequent: pop_seq(consequent),
|
||||
alternative: pop_seq(alternative)
|
||||
}),
|
||||
consequent.tail_node()
|
||||
]).optimize(compressor);
|
||||
}
|
||||
// x ? y || z : z --> x && y || z
|
||||
if (consequent instanceof AST_Binary
|
||||
&& consequent.operator == "||"
|
||||
@@ -5676,6 +5846,13 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pop_seq(node) {
|
||||
if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
|
||||
value: 0
|
||||
});
|
||||
return make_sequence(node, node.expressions.slice(0, -1));
|
||||
}
|
||||
});
|
||||
|
||||
OPT(AST_Boolean, function(self, compressor){
|
||||
@@ -5777,6 +5954,31 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
}
|
||||
}
|
||||
var fn;
|
||||
if (compressor.option("arguments")
|
||||
&& expr instanceof AST_SymbolRef
|
||||
&& expr.name == "arguments"
|
||||
&& expr.definition().orig.length == 1
|
||||
&& (fn = expr.scope) instanceof AST_Lambda
|
||||
&& prop instanceof AST_Number) {
|
||||
var index = prop.getValue();
|
||||
var argname = fn.argnames[index];
|
||||
if (!argname && !compressor.option("keep_fargs")) {
|
||||
while (index >= fn.argnames.length) {
|
||||
argname = make_node(AST_SymbolFunarg, fn, {
|
||||
name: fn.make_var_name("argument_" + fn.argnames.length),
|
||||
scope: fn
|
||||
});
|
||||
fn.argnames.push(argname);
|
||||
fn.enclosed.push(fn.def_variable(argname));
|
||||
}
|
||||
}
|
||||
if (argname) {
|
||||
var sym = make_node(AST_SymbolRef, self, argname);
|
||||
sym.reference({});
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
var ev = self.evaluate(compressor);
|
||||
if (ev !== self) {
|
||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
||||
|
||||
@@ -150,7 +150,6 @@ function minify(files, options) {
|
||||
if (options.mangle) toplevel.figure_out_scope(options.mangle);
|
||||
if (timings) timings.mangle = Date.now();
|
||||
if (options.mangle) {
|
||||
base54.reset();
|
||||
toplevel.compute_char_frequency(options.mangle);
|
||||
toplevel.mangle_names(options.mangle);
|
||||
}
|
||||
|
||||
@@ -576,6 +576,7 @@ function OutputStream(options) {
|
||||
indentation : function() { return indentation },
|
||||
current_width : function() { return current_col - indentation },
|
||||
should_break : function() { return options.width && this.current_width() >= options.width },
|
||||
has_parens : function() { return OUTPUT.slice(-1) == "(" },
|
||||
newline : newline,
|
||||
print : print,
|
||||
space : space,
|
||||
@@ -683,7 +684,7 @@ function OutputStream(options) {
|
||||
// a function expression needs parens around it when it's provably
|
||||
// the first token to appear in a statement.
|
||||
PARENS(AST_Function, function(output){
|
||||
if (first_in_statement(output)) {
|
||||
if (!output.has_parens() && first_in_statement(output)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -704,7 +705,9 @@ function OutputStream(options) {
|
||||
|
||||
// same goes for an object literal, because otherwise it would be
|
||||
// interpreted as a block of code.
|
||||
PARENS(AST_Object, first_in_statement);
|
||||
PARENS(AST_Object, function(output){
|
||||
return !output.has_parens() && first_in_statement(output);
|
||||
});
|
||||
|
||||
PARENS(AST_Unary, function(output){
|
||||
var p = output.parent();
|
||||
|
||||
165
lib/scope.js
165
lib/scope.js
@@ -74,17 +74,13 @@ SymbolDef.prototype = {
|
||||
var cache = options.cache && options.cache.props;
|
||||
if (this.global && cache && cache.has(this.name)) {
|
||||
this.mangled_name = cache.get(this.name);
|
||||
}
|
||||
else if (!this.mangled_name && !this.unmangleable(options)) {
|
||||
var s = this.scope;
|
||||
var sym = this.orig[0];
|
||||
if (options.ie8 && sym instanceof AST_SymbolLambda)
|
||||
s = s.parent_scope;
|
||||
} else if (!this.mangled_name && !this.unmangleable(options)) {
|
||||
var def;
|
||||
if (def = this.redefined()) {
|
||||
this.mangled_name = def.mangled_name || def.name;
|
||||
} else
|
||||
this.mangled_name = s.next_mangled(options, this);
|
||||
} else {
|
||||
this.mangled_name = next_mangled_name(this.scope, options, this);
|
||||
}
|
||||
if (this.global && cache) {
|
||||
cache.set(this.name, this.mangled_name);
|
||||
}
|
||||
@@ -168,8 +164,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
var def = scope.find_variable(node);
|
||||
if (node.thedef !== def) {
|
||||
node.thedef = def;
|
||||
node.reference(options);
|
||||
}
|
||||
node.reference(options);
|
||||
}
|
||||
}
|
||||
else if (node instanceof AST_SymbolCatch) {
|
||||
@@ -325,56 +321,62 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init){
|
||||
return symbol.thedef = def;
|
||||
});
|
||||
|
||||
function next_mangled(scope, options) {
|
||||
var ext = scope.enclosed;
|
||||
out: while (true) {
|
||||
var m = base54(++scope.cname);
|
||||
if (!is_identifier(m)) continue; // skip over "do"
|
||||
|
||||
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
|
||||
// shadow a name reserved from mangling.
|
||||
if (member(m, options.reserved)) continue;
|
||||
|
||||
// we must ensure that the mangled name does not shadow a name
|
||||
// from some parent scope that is referenced in this or in
|
||||
// inner scopes.
|
||||
for (var i = ext.length; --i >= 0;) {
|
||||
var sym = ext[i];
|
||||
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
|
||||
if (m == name) continue out;
|
||||
}
|
||||
return m;
|
||||
function names_in_use(scope, options) {
|
||||
var names = scope.names_in_use;
|
||||
if (!names) {
|
||||
scope.names_in_use = names = Object.create(scope.mangled_names || null);
|
||||
scope.cname_holes = [];
|
||||
scope.enclosed.forEach(function(def) {
|
||||
if (def.unmangleable(options)) names[def.name] = true;
|
||||
});
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
AST_Scope.DEFMETHOD("next_mangled", function(options){
|
||||
return next_mangled(this, options);
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("next_mangled", function(options){
|
||||
var name;
|
||||
do {
|
||||
name = next_mangled(this, options);
|
||||
} while (member(name, this.mangled_names));
|
||||
return name;
|
||||
});
|
||||
|
||||
AST_Function.DEFMETHOD("next_mangled", function(options, def){
|
||||
function next_mangled_name(scope, options, def) {
|
||||
var in_use = names_in_use(scope, options);
|
||||
var holes = scope.cname_holes;
|
||||
var names = Object.create(null);
|
||||
// #179, #326
|
||||
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
|
||||
// a function expression's argument cannot shadow the function expression's name
|
||||
|
||||
var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition();
|
||||
|
||||
// the function's mangled_name is null when keep_fnames is true
|
||||
var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null;
|
||||
|
||||
while (true) {
|
||||
var name = next_mangled(this, options);
|
||||
if (!tricky_name || tricky_name != name)
|
||||
return name;
|
||||
if (scope instanceof AST_Function && scope.name && def.orig[0] instanceof AST_SymbolFunarg) {
|
||||
var tricky_def = scope.name.definition();
|
||||
// the function's mangled_name is null when keep_fnames is true
|
||||
names[tricky_def.mangled_name || tricky_def.name] = true;
|
||||
}
|
||||
});
|
||||
var scopes = [ scope ];
|
||||
def.references.forEach(function(sym) {
|
||||
var scope = sym.scope;
|
||||
do {
|
||||
if (scopes.indexOf(scope) < 0) {
|
||||
for (var name in names_in_use(scope, options)) {
|
||||
names[name] = true;
|
||||
}
|
||||
scopes.push(scope);
|
||||
} else break;
|
||||
} while (scope = scope.parent_scope);
|
||||
});
|
||||
var name;
|
||||
for (var i = 0, len = holes.length; i < len; i++) {
|
||||
name = base54(holes[i]);
|
||||
if (names[name]) continue;
|
||||
holes.splice(i, 1);
|
||||
scope.names_in_use[name] = true;
|
||||
return name;
|
||||
}
|
||||
while (true) {
|
||||
name = base54(++scope.cname);
|
||||
if (in_use[name] || !is_identifier(name) || member(name, options.reserved)) continue;
|
||||
if (!names[name]) break;
|
||||
holes.push(scope.cname);
|
||||
}
|
||||
scope.names_in_use[name] = true;
|
||||
if (options.ie8 && def.orig[0] instanceof AST_SymbolLambda) {
|
||||
names_in_use(scope.parent_scope, options)[name] = true;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||
var def = this.definition();
|
||||
@@ -397,7 +399,7 @@ AST_Symbol.DEFMETHOD("global", function(){
|
||||
return this.definition().global;
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
|
||||
function _default_mangler_options(options) {
|
||||
options = defaults(options, {
|
||||
eval : false,
|
||||
ie8 : false,
|
||||
@@ -409,28 +411,25 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
|
||||
// Never mangle arguments
|
||||
push_uniq(options.reserved, "arguments");
|
||||
return options;
|
||||
});
|
||||
}
|
||||
|
||||
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
options = this._default_mangler_options(options);
|
||||
options = _default_mangler_options(options);
|
||||
|
||||
// We only need to mangle declaration nodes. Special logic wired
|
||||
// into the code generator will display the mangled name if it's
|
||||
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
||||
// the AST_SymbolDeclaration that it points to).
|
||||
var lname = -1;
|
||||
var to_mangle = [];
|
||||
|
||||
var mangled_names = this.mangled_names = [];
|
||||
if (options.cache) {
|
||||
this.globals.each(collect);
|
||||
if (options.cache.props) {
|
||||
options.cache.props.each(function(mangled_name) {
|
||||
push_uniq(mangled_names, mangled_name);
|
||||
});
|
||||
}
|
||||
if (options.cache && options.cache.props) {
|
||||
var mangled_names = this.mangled_names = Object.create(null);
|
||||
options.cache.props.each(function(mangled_name) {
|
||||
mangled_names[mangled_name] = true;
|
||||
});
|
||||
}
|
||||
|
||||
var redefined = [];
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (node instanceof AST_LabeledStatement) {
|
||||
// lname is incremented when we get to the AST_Label
|
||||
@@ -440,8 +439,12 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
return true; // don't descend again in TreeWalker
|
||||
}
|
||||
if (node instanceof AST_Scope) {
|
||||
node.variables.each(collect);
|
||||
return;
|
||||
descend();
|
||||
if (options.cache && node instanceof AST_Toplevel) {
|
||||
node.globals.each(mangle);
|
||||
}
|
||||
node.variables.each(mangle);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Label) {
|
||||
var name;
|
||||
@@ -449,17 +452,28 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||
node.mangled_name = name;
|
||||
return true;
|
||||
}
|
||||
if (!options.ie8 && node instanceof AST_SymbolCatch) {
|
||||
to_mangle.push(node.definition());
|
||||
return;
|
||||
if (!options.ie8 && node instanceof AST_Catch) {
|
||||
var def = node.argname.definition();
|
||||
var redef = def.redefined();
|
||||
if (redef) {
|
||||
redefined.push(def);
|
||||
def.references.forEach(function(ref) {
|
||||
ref.thedef = redef;
|
||||
ref.reference(options);
|
||||
ref.thedef = def;
|
||||
});
|
||||
}
|
||||
descend();
|
||||
if (!redef) mangle(def);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.walk(tw);
|
||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
||||
redefined.forEach(mangle);
|
||||
|
||||
function collect(symbol) {
|
||||
if (!member(symbol.name, options.reserved)) {
|
||||
to_mangle.push(symbol);
|
||||
function mangle(def) {
|
||||
if (!member(def.name, options.reserved)) {
|
||||
def.mangle(options);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -490,7 +504,7 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
|
||||
AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
||||
base54.reset();
|
||||
base54.sort();
|
||||
options = this._default_mangler_options(options);
|
||||
options = _default_mangler_options(options);
|
||||
var avoid = this.find_colliding_names(options);
|
||||
var cname = 0;
|
||||
this.globals.each(rename);
|
||||
@@ -528,7 +542,8 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
||||
options = this._default_mangler_options(options);
|
||||
options = _default_mangler_options(options);
|
||||
base54.reset();
|
||||
try {
|
||||
AST_Node.prototype.print = function(stream, force_parens) {
|
||||
this._print(stream, force_parens);
|
||||
|
||||
@@ -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.3.10",
|
||||
"version": "3.3.14",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -28,6 +28,7 @@ var remaining = 2 * urls.length;
|
||||
function done() {
|
||||
if (!--remaining) {
|
||||
var failures = [];
|
||||
var sum = { input: 0, output: 0, gzip: 0 };
|
||||
urls.forEach(function(url) {
|
||||
var info = results[url];
|
||||
console.log();
|
||||
@@ -40,6 +41,9 @@ function done() {
|
||||
if (info.code) {
|
||||
failures.push(url);
|
||||
}
|
||||
sum.input += info.input;
|
||||
sum.output += info.output;
|
||||
sum.gzip += info.gzip;
|
||||
});
|
||||
if (failures.length) {
|
||||
console.error("Benchmark failed:");
|
||||
@@ -47,6 +51,13 @@ function done() {
|
||||
console.error(url);
|
||||
});
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log();
|
||||
console.log("Subtotal");
|
||||
console.log();
|
||||
console.log("Original:", sum.input, "bytes");
|
||||
console.log("Uglified:", sum.output, "bytes");
|
||||
console.log("GZipped: ", sum.gzip, "bytes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
119
test/compress/arguments.js
Normal file
119
test/compress/arguments.js
Normal file
@@ -0,0 +1,119 @@
|
||||
replace_index: {
|
||||
options = {
|
||||
arguments: true,
|
||||
evaluate: true,
|
||||
properties: true,
|
||||
}
|
||||
input: {
|
||||
console.log(arguments && arguments[0]);
|
||||
(function() {
|
||||
console.log(arguments[1], arguments["1"], arguments["foo"]);
|
||||
})("bar", 42);
|
||||
(function(a, b) {
|
||||
console.log(arguments[1], arguments["1"], arguments["foo"]);
|
||||
})("bar", 42);
|
||||
(function(arguments) {
|
||||
console.log(arguments[1], arguments["1"], arguments["foo"]);
|
||||
})("bar", 42);
|
||||
(function() {
|
||||
var arguments;
|
||||
console.log(arguments[1], arguments["1"], arguments["foo"]);
|
||||
})("bar", 42);
|
||||
}
|
||||
expect: {
|
||||
console.log(arguments && arguments[0]);
|
||||
(function() {
|
||||
console.log(arguments[1], arguments[1], arguments.foo);
|
||||
})("bar", 42);
|
||||
(function(a, b) {
|
||||
console.log(b, b, arguments.foo);
|
||||
})("bar", 42);
|
||||
(function(arguments) {
|
||||
console.log(arguments[1], arguments[1], arguments.foo);
|
||||
})("bar", 42);
|
||||
(function() {
|
||||
var arguments;
|
||||
console.log(arguments[1], arguments[1], arguments.foo);
|
||||
})("bar", 42);
|
||||
}
|
||||
expect_stdout: [
|
||||
"undefined",
|
||||
"42 42 undefined",
|
||||
"42 42 undefined",
|
||||
"a a undefined",
|
||||
"42 42 undefined",
|
||||
]
|
||||
}
|
||||
|
||||
replace_index_keep_fargs: {
|
||||
options = {
|
||||
arguments: true,
|
||||
evaluate: true,
|
||||
keep_fargs: false,
|
||||
properties: true,
|
||||
}
|
||||
input: {
|
||||
console.log(arguments && arguments[0]);
|
||||
(function() {
|
||||
console.log(arguments[1], arguments["1"], arguments["foo"]);
|
||||
})("bar", 42);
|
||||
(function(a, b) {
|
||||
console.log(arguments[1], arguments["1"], arguments["foo"]);
|
||||
})("bar", 42);
|
||||
(function(arguments) {
|
||||
console.log(arguments[1], arguments["1"], arguments["foo"]);
|
||||
})("bar", 42);
|
||||
(function() {
|
||||
var arguments;
|
||||
console.log(arguments[1], arguments["1"], arguments["foo"]);
|
||||
})("bar", 42);
|
||||
}
|
||||
expect: {
|
||||
console.log(arguments && arguments[0]);
|
||||
(function(argument_0, argument_1) {
|
||||
console.log(argument_1, argument_1, arguments.foo);
|
||||
})("bar", 42);
|
||||
(function(a, b) {
|
||||
console.log(b, b, arguments.foo);
|
||||
})("bar", 42);
|
||||
(function(arguments) {
|
||||
console.log(arguments[1], arguments[1], arguments.foo);
|
||||
})("bar", 42);
|
||||
(function() {
|
||||
var arguments;
|
||||
console.log(arguments[1], arguments[1], arguments.foo);
|
||||
})("bar", 42);
|
||||
}
|
||||
expect_stdout: [
|
||||
"undefined",
|
||||
"42 42 undefined",
|
||||
"42 42 undefined",
|
||||
"a a undefined",
|
||||
"42 42 undefined",
|
||||
]
|
||||
}
|
||||
|
||||
modified: {
|
||||
options = {
|
||||
arguments: true,
|
||||
}
|
||||
input: {
|
||||
(function(a, b) {
|
||||
var c = arguments[0];
|
||||
var d = arguments[1];
|
||||
a = "foo";
|
||||
b++;
|
||||
console.log(a, b, c, d, arguments[0], arguments[1]);
|
||||
})("bar", 42);
|
||||
}
|
||||
expect: {
|
||||
(function(a, b) {
|
||||
var c = a;
|
||||
var d = b;
|
||||
a = "foo";
|
||||
b++;
|
||||
console.log(a, b, c, d, a, b);
|
||||
})("bar", 42);
|
||||
}
|
||||
expect_stdout: "foo 43 bar 42 foo 43"
|
||||
}
|
||||
@@ -913,15 +913,15 @@ collapse_vars_unary: {
|
||||
return delete x;
|
||||
}
|
||||
function f1(n) {
|
||||
return n > +!!n
|
||||
return +!!n < n;
|
||||
}
|
||||
function f2(n) {
|
||||
var k = 7;
|
||||
return k--
|
||||
return k--;
|
||||
}
|
||||
function f3(n) {
|
||||
var k = 7;
|
||||
return ++k
|
||||
return ++k;
|
||||
}
|
||||
function f4(n) {
|
||||
var k = 8 - n;
|
||||
@@ -2219,8 +2219,8 @@ unused_orig: {
|
||||
console.log(function(b) {
|
||||
var c = b;
|
||||
for (var d in c) {
|
||||
var a = c[0];
|
||||
return --b + a;
|
||||
var a;
|
||||
return --b + c[0];
|
||||
}
|
||||
a && a.NaN;
|
||||
}([2]), a);
|
||||
@@ -3124,7 +3124,57 @@ issue_2425_3: {
|
||||
expect_stdout: "15"
|
||||
}
|
||||
|
||||
issue_2437: {
|
||||
issue_2437_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
conditionals: true,
|
||||
inline: true,
|
||||
join_vars: true,
|
||||
passes: 2,
|
||||
reduce_funcs: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
sequences: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function foo() {
|
||||
return bar();
|
||||
}
|
||||
function bar() {
|
||||
if (xhrDesc) {
|
||||
var req = new XMLHttpRequest();
|
||||
var result = !!req.onreadystatechange;
|
||||
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {});
|
||||
return result;
|
||||
} else {
|
||||
var req = new XMLHttpRequest();
|
||||
var detectFunc = function () {};
|
||||
req.onreadystatechange = detectFunc;
|
||||
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
|
||||
req.onreadystatechange = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
console.log(foo());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
if (xhrDesc) {
|
||||
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
|
||||
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
|
||||
result;
|
||||
}
|
||||
var req, detectFunc = function() {};
|
||||
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
|
||||
return req.onreadystatechange = null, result;
|
||||
}());
|
||||
}
|
||||
}
|
||||
|
||||
issue_2437_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
conditionals: true,
|
||||
@@ -3161,14 +3211,12 @@ issue_2437: {
|
||||
}
|
||||
expect: {
|
||||
!function() {
|
||||
if (xhrDesc) {
|
||||
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
|
||||
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
|
||||
result;
|
||||
}
|
||||
var req, detectFunc = function() {};
|
||||
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
|
||||
if (xhrDesc)
|
||||
return (req = new XMLHttpRequest()).onreadystatechange,
|
||||
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {});
|
||||
var req;
|
||||
(req = new XMLHttpRequest).onreadystatechange = function(){},
|
||||
req[SYMBOL_FAKE_ONREADYSTATECHANGE_1],
|
||||
req.onreadystatechange = null;
|
||||
}();
|
||||
}
|
||||
@@ -4517,3 +4565,681 @@ issue_2891_2: {
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_2908: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 0, b = 0;
|
||||
function f(c) {
|
||||
if (1 == c) return;
|
||||
a++;
|
||||
if (2 == c) b = a;
|
||||
}
|
||||
f(0);
|
||||
f(2);
|
||||
console.log(b);
|
||||
}
|
||||
expect: {
|
||||
var a = 0, b = 0;
|
||||
function f(c) {
|
||||
if (1 == c) return;
|
||||
a++;
|
||||
if (2 == c) b = a;
|
||||
}
|
||||
f(0);
|
||||
f(2);
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
issue_2914_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
function read(input) {
|
||||
var i = 0;
|
||||
var e = 0;
|
||||
var t = 0;
|
||||
while (e < 32) {
|
||||
var n = input[i++];
|
||||
t |= (127 & n) << e;
|
||||
if (0 === (128 & n))
|
||||
return t;
|
||||
e += 7;
|
||||
}
|
||||
}
|
||||
console.log(read([129]));
|
||||
}
|
||||
expect: {
|
||||
function read(input) {
|
||||
var i = 0;
|
||||
var e = 0;
|
||||
var t = 0;
|
||||
while (e < 32) {
|
||||
var n = input[i++];
|
||||
t |= (127 & n) << e;
|
||||
if (0 === (128 & n))
|
||||
return t;
|
||||
e += 7;
|
||||
}
|
||||
}
|
||||
console.log(read([129]));
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_2914_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
function read(input) {
|
||||
var i = 0;
|
||||
var e = 0;
|
||||
var t = 0;
|
||||
while (e < 32) {
|
||||
var n = input[i++];
|
||||
t = (127 & n) << e;
|
||||
if (0 === (128 & n))
|
||||
return t;
|
||||
e += 7;
|
||||
}
|
||||
}
|
||||
console.log(read([129]));
|
||||
}
|
||||
expect: {
|
||||
function read(input) {
|
||||
var i = 0;
|
||||
var e = 0;
|
||||
var t = 0;
|
||||
while (e < 32) {
|
||||
var n = input[i++];
|
||||
if (0 === (128 & n))
|
||||
return t = (127 & n) << e;
|
||||
e += 7;
|
||||
}
|
||||
}
|
||||
console.log(read([129]));
|
||||
}
|
||||
expect_stdout: "0"
|
||||
}
|
||||
|
||||
issue_805: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
function Foo(){}
|
||||
Foo.prototype = {};
|
||||
Foo.prototype.bar = 42;
|
||||
return Foo;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
function Foo(){}
|
||||
(Foo.prototype = {}).bar = 42;
|
||||
return Foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
issue_2931: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var a = function() {
|
||||
return;
|
||||
}();
|
||||
return a;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
return function() {
|
||||
return;
|
||||
}();
|
||||
}());
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
issue_2954_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = "PASS", b;
|
||||
try {
|
||||
do {
|
||||
b = function() {
|
||||
throw 0;
|
||||
}();
|
||||
a = "FAIL";
|
||||
b && b.c;
|
||||
} while (0);
|
||||
} catch (e) {
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = "PASS", b;
|
||||
try {
|
||||
do {
|
||||
b = function() {
|
||||
throw 0;
|
||||
}();
|
||||
a = "FAIL";
|
||||
b && b.c;
|
||||
} while (0);
|
||||
} catch (e) {
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2954_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = "FAIL_1", b;
|
||||
try {
|
||||
throw 0;
|
||||
} catch (e) {
|
||||
do {
|
||||
b = function() {
|
||||
throw new Error("PASS");
|
||||
}();
|
||||
a = "FAIL_2";
|
||||
b && b.c;
|
||||
} while (0);
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL_1", b;
|
||||
try {
|
||||
throw 0;
|
||||
} catch (e) {
|
||||
do {
|
||||
a = "FAIL_2";
|
||||
(b = function() {
|
||||
throw new Error("PASS");
|
||||
}()) && b.c;
|
||||
} while (0);
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: Error("PASS")
|
||||
}
|
||||
|
||||
issue_2954_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = "FAIL_1", b;
|
||||
try {
|
||||
} finally {
|
||||
do {
|
||||
b = function() {
|
||||
throw new Error("PASS");
|
||||
}();
|
||||
a = "FAIL_2";
|
||||
b && b.c;
|
||||
} while (0);
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL_1", b;
|
||||
try {
|
||||
} finally {
|
||||
do {
|
||||
a = "FAIL_2";
|
||||
(b = function() {
|
||||
throw new Error("PASS");
|
||||
}()) && b.c;
|
||||
} while (0);
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: Error("PASS")
|
||||
}
|
||||
|
||||
collapse_rhs_conditional_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = "PASS", b = "FAIL";
|
||||
b = a;
|
||||
"function" == typeof f && f(a);
|
||||
console.log(a, b);
|
||||
}
|
||||
expect: {
|
||||
var a = "PASS", b = "FAIL";
|
||||
b = a;
|
||||
"function" == typeof f && f(a);
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: "PASS PASS"
|
||||
}
|
||||
|
||||
collapse_rhs_conditional_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = "FAIL", b;
|
||||
while ((a = "PASS", --b) && "PASS" == b);
|
||||
console.log(a, b);
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL", b;
|
||||
while ((a = "PASS", --b) && "PASS" == b);
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: "PASS NaN"
|
||||
}
|
||||
|
||||
collapse_rhs_lhs_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
new function() {
|
||||
this[c++] = 1;
|
||||
c += 1;
|
||||
}();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
new function() {
|
||||
this[c++] = 1;
|
||||
c += 1;
|
||||
}();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
collapse_rhs_lhs_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var b = 1;
|
||||
(function f(f) {
|
||||
f = b;
|
||||
f[b] = 0;
|
||||
})();
|
||||
console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
var b = 1;
|
||||
(function f(f) {
|
||||
f = b;
|
||||
f[b] = 0;
|
||||
})();
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
collapse_rhs_side_effects: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1, c = 0;
|
||||
new function f() {
|
||||
this[a-- && f()] = 1;
|
||||
c += 1;
|
||||
}();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var a = 1, c = 0;
|
||||
new function f() {
|
||||
this[a-- && f()] = 1;
|
||||
c += 1;
|
||||
}();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
collapse_rhs_vardef: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b = 1;
|
||||
a = --b + function c() {
|
||||
var b;
|
||||
c[--b] = 1;
|
||||
}();
|
||||
b |= a;
|
||||
console.log(a, b);
|
||||
}
|
||||
expect: {
|
||||
var a, b = 1;
|
||||
a = --b + function c() {
|
||||
var b;
|
||||
c[--b] = 1;
|
||||
}();
|
||||
b |= a;
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: "NaN 0"
|
||||
}
|
||||
|
||||
collapse_rhs_array: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = [];
|
||||
b = [];
|
||||
return [];
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = [];
|
||||
b = [];
|
||||
return [];
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "false false false"
|
||||
}
|
||||
|
||||
collapse_rhs_boolean: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = !0;
|
||||
b = !0;
|
||||
return !0;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
return b = a = !0;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "true true true"
|
||||
}
|
||||
|
||||
collapse_rhs_function: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = function() {};
|
||||
b = function() {};
|
||||
return function() {};
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = function() {};
|
||||
b = function() {};
|
||||
return function() {};
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "false false false"
|
||||
}
|
||||
|
||||
collapse_rhs_number: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = 42;
|
||||
b = 42;
|
||||
return 42;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
return b = a = 42;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "true true true"
|
||||
}
|
||||
|
||||
collapse_rhs_object: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = {};
|
||||
b = {};
|
||||
return {};
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = {};
|
||||
b = {};
|
||||
return {};
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "false false false"
|
||||
}
|
||||
|
||||
collapse_rhs_regexp: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = /bar/;
|
||||
b = /bar/;
|
||||
return /bar/;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = /bar/;
|
||||
b = /bar/;
|
||||
return /bar/;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "false false false"
|
||||
}
|
||||
|
||||
collapse_rhs_string: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = "foo";
|
||||
b = "foo";
|
||||
return "foo";
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
return b = a = "foo";
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "true true true"
|
||||
}
|
||||
|
||||
collapse_rhs_var: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = f;
|
||||
b = f;
|
||||
return f;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
return b = a = f;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "true true true"
|
||||
}
|
||||
|
||||
collapse_rhs_this: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = this;
|
||||
b = this;
|
||||
return this;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
return b = a = this;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "true true true"
|
||||
}
|
||||
|
||||
collapse_rhs_undefined: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
function f() {
|
||||
a = void 0;
|
||||
b = void 0;
|
||||
return void 0;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect: {
|
||||
var a, b;
|
||||
function f() {
|
||||
b = a = void 0;
|
||||
return;
|
||||
}
|
||||
var c = f();
|
||||
console.log(a === b, b === c, c === a);
|
||||
}
|
||||
expect_stdout: "true true true"
|
||||
}
|
||||
|
||||
issue_2974: {
|
||||
options = {
|
||||
booleans: true,
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
loops: true,
|
||||
passes: 2,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
(function f(b) {
|
||||
var a = 2;
|
||||
do {
|
||||
b && b[b];
|
||||
b && (b.null = -4);
|
||||
c++;
|
||||
} while (b.null && --a > 0);
|
||||
})(true);
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
(function(b) {
|
||||
var a = 2;
|
||||
for (; b.null = -4, c++, b.null && --a > 0;);
|
||||
})(!0),
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
@@ -1,62 +1,42 @@
|
||||
keep_comparisons: {
|
||||
comparisons: {
|
||||
options = {
|
||||
comparisons: true,
|
||||
unsafe_comps: false
|
||||
}
|
||||
input: {
|
||||
var obj1 = {
|
||||
valueOf: function() {triggeredFirst();}
|
||||
}
|
||||
var obj2 = {
|
||||
valueOf: function() {triggeredSecond();}
|
||||
}
|
||||
var obj1, obj2;
|
||||
var result1 = obj1 <= obj2;
|
||||
var result2 = obj1 < obj2;
|
||||
var result3 = obj1 >= obj2;
|
||||
var result4 = obj1 > obj2;
|
||||
}
|
||||
expect: {
|
||||
var obj1 = {
|
||||
valueOf: function() {triggeredFirst();}
|
||||
}
|
||||
var obj2 = {
|
||||
valueOf: function() {triggeredSecond();}
|
||||
}
|
||||
var obj1, obj2;
|
||||
var result1 = obj1 <= obj2;
|
||||
var result2 = obj1 < obj2;
|
||||
var result3 = obj1 >= obj2;
|
||||
var result4 = obj1 > obj2;
|
||||
var result3 = obj2 <= obj1;
|
||||
var result4 = obj2 < obj1;
|
||||
}
|
||||
}
|
||||
|
||||
keep_comparisons_with_unsafe_comps: {
|
||||
unsafe_comps: {
|
||||
options = {
|
||||
comparisons: true,
|
||||
unsafe_comps: true
|
||||
conditionals: true,
|
||||
unsafe_comps: true,
|
||||
}
|
||||
input: {
|
||||
var obj1 = {
|
||||
valueOf: function() {triggeredFirst();}
|
||||
}
|
||||
var obj2 = {
|
||||
valueOf: function() {triggeredSecond();}
|
||||
}
|
||||
var result1 = obj1 <= obj2;
|
||||
var result2 = obj1 < obj2;
|
||||
var result3 = obj1 >= obj2;
|
||||
var result4 = obj1 > obj2;
|
||||
var obj1, obj2;
|
||||
obj1 <= obj2 ? f1() : g1();
|
||||
obj1 < obj2 ? f2() : g2();
|
||||
obj1 >= obj2 ? f3() : g3();
|
||||
obj1 > obj2 ? f4() : g4();
|
||||
}
|
||||
expect: {
|
||||
var obj1 = {
|
||||
valueOf: function() {triggeredFirst();}
|
||||
}
|
||||
var obj2 = {
|
||||
valueOf: function() {triggeredSecond();}
|
||||
}
|
||||
var result1 = obj2 >= obj1;
|
||||
var result2 = obj2 > obj1;
|
||||
var result3 = obj1 >= obj2;
|
||||
var result4 = obj1 > obj2;
|
||||
var obj1, obj2;
|
||||
obj2 < obj1 ? g1() : f1();
|
||||
obj1 < obj2 ? f2() : g2();
|
||||
obj1 < obj2 ? g3() : f3();
|
||||
obj2 < obj1 ? f4() : g4();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ cond_7: {
|
||||
x = 'foo';
|
||||
x = 'foo';
|
||||
x = (condition(), 20);
|
||||
x = z ? 'fuji' : (condition(), 'fuji');
|
||||
x = (z || condition(), 'fuji');
|
||||
x = (condition(), 'foobar');
|
||||
x = y ? a : b;
|
||||
x = y ? 'foo' : 'fo';
|
||||
|
||||
@@ -917,3 +917,28 @@ issue_2860_2: {
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_2929: {
|
||||
options = {
|
||||
dead_code: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a) {
|
||||
try {
|
||||
return null.p = a = 1;
|
||||
} catch (e) {
|
||||
return a ? "PASS" : "FAIL";
|
||||
}
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a) {
|
||||
try {
|
||||
return null.p = a = 1;
|
||||
} catch (e) {
|
||||
return a ? "PASS" : "FAIL";
|
||||
}
|
||||
}());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -1719,3 +1719,69 @@ issue_2846: {
|
||||
}
|
||||
expect_stdout: "0"
|
||||
}
|
||||
|
||||
issue_805_1: {
|
||||
options = {
|
||||
inline: true,
|
||||
passes: 2,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
var unused = function() {};
|
||||
unused.prototype[a()] = 42;
|
||||
(unused.prototype.bar = function() {
|
||||
console.log("bar");
|
||||
})();
|
||||
return unused;
|
||||
})(function() {
|
||||
console.log("foo");
|
||||
return "foo";
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
console.log("foo"),
|
||||
console.log("bar");
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"bar",
|
||||
]
|
||||
}
|
||||
|
||||
issue_805_2: {
|
||||
options = {
|
||||
inline: true,
|
||||
passes: 2,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
function unused() {}
|
||||
unused.prototype[a()] = 42;
|
||||
(unused.prototype.bar = function() {
|
||||
console.log("bar");
|
||||
})();
|
||||
return unused;
|
||||
})(function() {
|
||||
console.log("foo");
|
||||
return "foo";
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
console.log("foo"),
|
||||
console.log("bar");
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"bar",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1429,3 +1429,140 @@ string_case: {
|
||||
"199",
|
||||
]
|
||||
}
|
||||
|
||||
issue_2916_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
var c = "PASS";
|
||||
(function(a, b) {
|
||||
(function(d) {
|
||||
d[0] = 1;
|
||||
})(b);
|
||||
a == b && (c = "FAIL");
|
||||
})("", []);
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = "PASS";
|
||||
(function(a, b) {
|
||||
(function(d) {
|
||||
d[0] = 1;
|
||||
})(b);
|
||||
a == b && (c = "FAIL");
|
||||
})("", []);
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2916_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unsafe: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = "FAIL";
|
||||
(function(b) {
|
||||
(function(d) {
|
||||
d[0] = 1;
|
||||
})(b);
|
||||
+b && (c = "PASS");
|
||||
})([]);
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = "FAIL";
|
||||
(function(b) {
|
||||
b[0] = 1;
|
||||
+b && (c = "PASS");
|
||||
})([]);
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2919: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log([ function() {} ].toString());
|
||||
}
|
||||
expect: {
|
||||
console.log("function(){}");
|
||||
}
|
||||
}
|
||||
|
||||
issue_2926_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
(function f(a) {
|
||||
console.log(f.name.length, f.length);
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function f(a) {
|
||||
console.log(1, 1);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "1 1"
|
||||
}
|
||||
|
||||
issue_2926_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof function() {}.valueOf());
|
||||
}
|
||||
expect: {
|
||||
console.log("function");
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_2968: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = "FAIL";
|
||||
(function() {
|
||||
(function(a, b) {
|
||||
a <<= 0;
|
||||
a && (a[(c = "PASS", 0 >>> (b += 1))] = 0);
|
||||
})(42, -42);
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = "FAIL";
|
||||
(function() {
|
||||
b = -(a = 42),
|
||||
void ((a <<= 0) && (a[(c = "PASS", 0 >>> (b += 1))] = 0));
|
||||
var a, b;
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -292,11 +292,12 @@ issue_2084: {
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
!function(c) {
|
||||
c = 1 + c,
|
||||
!function() {
|
||||
var c;
|
||||
c = 1 + (c = -1),
|
||||
c = 1 + (c = 0),
|
||||
0 !== 23..toString() && (c = 1 + c);
|
||||
}(-1),
|
||||
}(),
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "0"
|
||||
@@ -1051,11 +1052,9 @@ issue_2616: {
|
||||
}
|
||||
expect: {
|
||||
var c = "FAIL";
|
||||
(function() {
|
||||
!function(NaN) {
|
||||
(true << NaN) - 0/0 || (c = "PASS");
|
||||
}([]);
|
||||
})();
|
||||
!function(NaN) {
|
||||
(true << NaN) - 0/0 || (c = "PASS");
|
||||
}([]);
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
@@ -1086,13 +1085,11 @@ issue_2620_1: {
|
||||
}
|
||||
expect: {
|
||||
var c = "FAIL";
|
||||
(function() {
|
||||
(function(a) {
|
||||
if (function(a) {
|
||||
a && a();
|
||||
}(), a) c = "PASS";
|
||||
})(1);
|
||||
})(),
|
||||
!function(a) {
|
||||
if (function(a) {
|
||||
a && a();
|
||||
}(), a) c = "PASS";
|
||||
}(1),
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
@@ -1160,18 +1157,16 @@ issue_2620_3: {
|
||||
}
|
||||
expect: {
|
||||
var c = "FAIL";
|
||||
(function() {
|
||||
(function(a, NaN) {
|
||||
(function() {
|
||||
switch (a) {
|
||||
case a:
|
||||
break;
|
||||
case c = "PASS", NaN:
|
||||
break;
|
||||
}
|
||||
})();
|
||||
})(NaN);
|
||||
})();
|
||||
!function(a, NaN) {
|
||||
(function() {
|
||||
switch (a) {
|
||||
case a:
|
||||
break;
|
||||
case c = "PASS", NaN:
|
||||
break;
|
||||
}
|
||||
})();
|
||||
}(NaN);
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
@@ -1341,11 +1336,9 @@ issue_2630_4: {
|
||||
}
|
||||
expect: {
|
||||
var x = 3, a = 1, b = 2;
|
||||
(function() {
|
||||
(function() {
|
||||
while (--x >= 0 && void (a++, b += a));
|
||||
})();
|
||||
})();
|
||||
!function() {
|
||||
while (--x >= 0 && void (b += ++a));
|
||||
}();
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
@@ -1989,3 +1982,72 @@ issue_2783: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2898: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
(function() {
|
||||
while (f());
|
||||
function f() {
|
||||
var b = (c = 1 + c, void (c = 1 + c));
|
||||
b && b[0];
|
||||
}
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
(function() {
|
||||
while (b = void 0, void ((b = void (c = 1 + (c = 1 + c))) && b[0]));
|
||||
var b;
|
||||
})(),
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
deduplicate_parenthesis: {
|
||||
input: {
|
||||
({}).a = b;
|
||||
(({}).a = b)();
|
||||
(function() {}).a = b;
|
||||
((function() {}).a = b)();
|
||||
}
|
||||
expect_exact: "({}).a=b;({}.a=b)();(function(){}).a=b;(function(){}.a=b)();"
|
||||
}
|
||||
|
||||
drop_lone_use_strict: {
|
||||
options = {
|
||||
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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,14 +239,14 @@ name_collision_2: {
|
||||
input: {
|
||||
var o = {
|
||||
p: 1,
|
||||
0: function(x) {
|
||||
"+": function(x) {
|
||||
return x;
|
||||
},
|
||||
1: function(x) {
|
||||
"-": function(x) {
|
||||
return x + 1;
|
||||
}
|
||||
}, o__$0 = 2, o__$1 = 3;
|
||||
console.log(o.p === o.p, o[0](4), o[1](5), o__$0, o__$1);
|
||||
console.log(o.p === o.p, o["+"](4), o["-"](5), o__$0, o__$1);
|
||||
}
|
||||
expect: {
|
||||
var o_p = 1,
|
||||
@@ -273,14 +273,14 @@ name_collision_3: {
|
||||
input: {
|
||||
var o = {
|
||||
p: 1,
|
||||
0: function(x) {
|
||||
"+": function(x) {
|
||||
return x;
|
||||
},
|
||||
1: function(x) {
|
||||
"-": function(x) {
|
||||
return x + 1;
|
||||
}
|
||||
}, o__$0 = 2, o__$1 = 3;
|
||||
console.log(o.p === o.p, o[0](4), o[1](5));
|
||||
console.log(o.p === o.p, o["+"](4), o["-"](5));
|
||||
}
|
||||
expect: {
|
||||
var o_p = 1,
|
||||
|
||||
@@ -243,7 +243,7 @@ issue_1089: {
|
||||
expect: {
|
||||
function x() {
|
||||
var f = document.getElementById("fname");
|
||||
if (f.files[0].size > 12345)
|
||||
if (12345 < f.files[0].size)
|
||||
return alert("alert"), f.focus(), !1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ mangle_catch_toplevel: {
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
|
||||
expect_exact: 'var c="FAIL";try{throw 1}catch(o){c="PASS"}console.log(c);'
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ mangle_catch_var_toplevel: {
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
|
||||
expect_exact: 'var r="FAIL";try{throw 1}catch(o){var r="PASS"}console.log(r);'
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -345,3 +345,95 @@ mangle_catch_redef_2_ie8_toplevel: {
|
||||
expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);'
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
mangle_catch_redef_3: {
|
||||
mangle = {
|
||||
ie8: false,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
var o = "PASS";
|
||||
try {
|
||||
throw 0;
|
||||
} catch (o) {
|
||||
(function() {
|
||||
function f() {
|
||||
o = "FAIL";
|
||||
}
|
||||
f(), f();
|
||||
})();
|
||||
}
|
||||
console.log(o);
|
||||
}
|
||||
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
mangle_catch_redef_3_toplevel: {
|
||||
mangle = {
|
||||
ie8: false,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
var o = "PASS";
|
||||
try {
|
||||
throw 0;
|
||||
} catch (o) {
|
||||
(function() {
|
||||
function f() {
|
||||
o = "FAIL";
|
||||
}
|
||||
f(), f();
|
||||
})();
|
||||
}
|
||||
console.log(o);
|
||||
}
|
||||
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
mangle_catch_redef_ie8_3: {
|
||||
mangle = {
|
||||
ie8: true,
|
||||
toplevel: false,
|
||||
}
|
||||
input: {
|
||||
var o = "PASS";
|
||||
try {
|
||||
throw 0;
|
||||
} catch (o) {
|
||||
(function() {
|
||||
function f() {
|
||||
o = "FAIL";
|
||||
}
|
||||
f(), f();
|
||||
})();
|
||||
}
|
||||
console.log(o);
|
||||
}
|
||||
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
mangle_catch_redef_3_ie8_toplevel: {
|
||||
mangle = {
|
||||
ie8: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
var o = "PASS";
|
||||
try {
|
||||
throw 0;
|
||||
} catch (o) {
|
||||
(function() {
|
||||
function f() {
|
||||
o = "FAIL";
|
||||
}
|
||||
f(), f();
|
||||
})();
|
||||
}
|
||||
console.log(o);
|
||||
}
|
||||
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ function_iife_catch: {
|
||||
}
|
||||
f();
|
||||
}
|
||||
expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
|
||||
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
|
||||
expect_stdout: "0 1"
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function_iife_catch_ie8: {
|
||||
}
|
||||
f();
|
||||
}
|
||||
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
|
||||
expect_exact: "function f(c){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
|
||||
expect_stdout: "0 1"
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ function_catch_catch: {
|
||||
}
|
||||
f();
|
||||
}
|
||||
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
|
||||
expect_exact: "var o=0;function f(){try{throw 1}catch(o){try{throw 2}catch(c){var c=3;console.log(c)}}console.log(c)}f();"
|
||||
expect_stdout: [
|
||||
"3",
|
||||
"undefined",
|
||||
|
||||
@@ -605,3 +605,20 @@ issue_2740_5: {
|
||||
}
|
||||
expect_stdout: "0 undefined"
|
||||
}
|
||||
|
||||
issue_2904: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
loops: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1;
|
||||
do {
|
||||
console.log(a);
|
||||
} while (--a);
|
||||
}
|
||||
expect: {
|
||||
for (var a = 1; console.log(a), --a;);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
@@ -1588,3 +1588,55 @@ issue_2816: {
|
||||
}
|
||||
expect_stdout: "3 2 4"
|
||||
}
|
||||
|
||||
issue_2893_1: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
var o = {
|
||||
get a() {
|
||||
return "PASS";
|
||||
},
|
||||
};
|
||||
o.a = "FAIL";
|
||||
console.log(o.a);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
get a() {
|
||||
return "PASS";
|
||||
},
|
||||
};
|
||||
o.a = "FAIL";
|
||||
console.log(o.a);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2893_2: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
var o = {
|
||||
set a(v) {
|
||||
this.b = v;
|
||||
},
|
||||
b: "FAIL",
|
||||
};
|
||||
o.a = "PASS";
|
||||
console.log(o.b);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
set a(v) {
|
||||
this.b = v;
|
||||
},
|
||||
b: "FAIL",
|
||||
};
|
||||
o.a = "PASS";
|
||||
console.log(o.b);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -721,3 +721,443 @@ issue_2838: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2938_1: {
|
||||
options = {
|
||||
pure_getters: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
a.b = "PASS";
|
||||
}
|
||||
var o = {};
|
||||
f(o);
|
||||
console.log(o.b);
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
a.b = "PASS";
|
||||
}
|
||||
var o = {};
|
||||
f(o);
|
||||
console.log(o.b);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2938_2: {
|
||||
options = {
|
||||
pure_getters: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var Parser = function Parser() {};
|
||||
var p = Parser.prototype;
|
||||
p.initialContext = function initialContext() {
|
||||
console.log("PASS");
|
||||
};
|
||||
p.braceIsBlock = function() {};
|
||||
(new Parser).initialContext();
|
||||
}
|
||||
expect: {
|
||||
var Parser = function() {};
|
||||
var p = Parser.prototype;
|
||||
p.initialContext = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
p.braceIsBlock = function() {};
|
||||
(new Parser).initialContext();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2938_3: {
|
||||
options = {
|
||||
pure_getters: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var unused = a.a;
|
||||
a.b = "PASS";
|
||||
a.c;
|
||||
}
|
||||
var o = {};
|
||||
o.d;
|
||||
f(o);
|
||||
console.log(o.b);
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
a.b = "PASS";
|
||||
}
|
||||
var o = {};
|
||||
f(o);
|
||||
console.log(o.b);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2938_4: {
|
||||
options = {
|
||||
pure_getters: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var Parser = function Parser() {};
|
||||
var p = Parser.prototype;
|
||||
var unused = p.x;
|
||||
p.initialContext = function initialContext() {
|
||||
p.y;
|
||||
console.log("PASS");
|
||||
};
|
||||
p.braceIsBlock = function() {};
|
||||
(new Parser).initialContext();
|
||||
}
|
||||
expect: {
|
||||
var Parser = function() {};
|
||||
var p = Parser.prototype;
|
||||
p.initialContext = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
p.braceIsBlock = function() {};
|
||||
(new Parser).initialContext();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
collapse_vars_1_true: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a, b) {
|
||||
for (;;) {
|
||||
var c = a.g();
|
||||
var d = b.p;
|
||||
if (c || d) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f(a, b) {
|
||||
for (;;) {
|
||||
if (a.g() || b.p) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collapse_vars_1_false: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: false,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a, b) {
|
||||
for (;;) {
|
||||
var c = a.g();
|
||||
var d = b.p;
|
||||
if (c || d) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f(a, b) {
|
||||
for (;;) {
|
||||
var c = a.g();
|
||||
var d = b.p;
|
||||
if (c || d) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collapse_vars_1_strict: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a, b) {
|
||||
for (;;) {
|
||||
var c = a.g();
|
||||
var d = b.p;
|
||||
if (c || d) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f(a, b) {
|
||||
for (;;) {
|
||||
var c = a.g();
|
||||
var d = b.p;
|
||||
if (c || d) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collapse_vars_2_true: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
function g() {}
|
||||
g.a = function() {};
|
||||
g.b = g.a;
|
||||
return g;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
function g() {}
|
||||
g.b = g.a = function() {};
|
||||
return g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collapse_vars_2_false: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: false,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
function g() {}
|
||||
g.a = function() {};
|
||||
g.b = g.a;
|
||||
return g;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
function g() {}
|
||||
g.a = function() {};
|
||||
g.b = g.a;
|
||||
return g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collapse_vars_2_strict: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
function g() {}
|
||||
g.a = function() {};
|
||||
g.b = g.a;
|
||||
return g;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
function g() {}
|
||||
g.b = g.a = function() {};
|
||||
return g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collapse_rhs_true: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: true,
|
||||
}
|
||||
input: {
|
||||
console.log((42..length = "PASS", "PASS"));
|
||||
console.log(("foo".length = "PASS", "PASS"));
|
||||
console.log((false.length = "PASS", "PASS"));
|
||||
console.log((function() {}.length = "PASS", "PASS"));
|
||||
console.log(({
|
||||
get length() {
|
||||
return "FAIL";
|
||||
}
|
||||
}.length = "PASS", "PASS"));
|
||||
}
|
||||
expect: {
|
||||
console.log(42..length = "PASS");
|
||||
console.log("foo".length = "PASS");
|
||||
console.log(false.length = "PASS");
|
||||
console.log(function() {}.length = "PASS");
|
||||
console.log({
|
||||
get length() {
|
||||
return "FAIL";
|
||||
}
|
||||
}.length = "PASS");
|
||||
}
|
||||
expect_stdout: [
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
]
|
||||
}
|
||||
|
||||
collapse_rhs_false: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: false,
|
||||
}
|
||||
input: {
|
||||
console.log((42..length = "PASS", "PASS"));
|
||||
console.log(("foo".length = "PASS", "PASS"));
|
||||
console.log((false.length = "PASS", "PASS"));
|
||||
console.log((function() {}.length = "PASS", "PASS"));
|
||||
console.log(({
|
||||
get length() {
|
||||
return "FAIL";
|
||||
}
|
||||
}.length = "PASS", "PASS"));
|
||||
}
|
||||
expect: {
|
||||
console.log(42..length = "PASS");
|
||||
console.log("foo".length = "PASS");
|
||||
console.log(false.length = "PASS");
|
||||
console.log(function() {}.length = "PASS");
|
||||
console.log({
|
||||
get length() {
|
||||
return "FAIL";
|
||||
}
|
||||
}.length = "PASS");
|
||||
}
|
||||
expect_stdout: [
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
]
|
||||
}
|
||||
|
||||
collapse_rhs_strict: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
}
|
||||
input: {
|
||||
console.log((42..length = "PASS", "PASS"));
|
||||
console.log(("foo".length = "PASS", "PASS"));
|
||||
console.log((false.length = "PASS", "PASS"));
|
||||
console.log((function() {}.length = "PASS", "PASS"));
|
||||
console.log(({
|
||||
get length() {
|
||||
return "FAIL";
|
||||
}
|
||||
}.length = "PASS", "PASS"));
|
||||
}
|
||||
expect: {
|
||||
console.log(42..length = "PASS");
|
||||
console.log("foo".length = "PASS");
|
||||
console.log(false.length = "PASS");
|
||||
console.log(function() {}.length = "PASS");
|
||||
console.log({
|
||||
get length() {
|
||||
return "FAIL";
|
||||
}
|
||||
}.length = "PASS");
|
||||
}
|
||||
expect_stdout: [
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
]
|
||||
}
|
||||
|
||||
collapse_rhs_setter: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
}
|
||||
input: {
|
||||
try {
|
||||
console.log(({
|
||||
set length(v) {
|
||||
throw "PASS";
|
||||
}
|
||||
}.length = "FAIL", "FAIL"));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
try {
|
||||
console.log({
|
||||
set length(v) {
|
||||
throw "PASS";
|
||||
}
|
||||
}.length = "FAIL");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
collapse_rhs_call: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
passes: 2,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
function f() {
|
||||
console.log("PASS");
|
||||
}
|
||||
o.f = f;
|
||||
f();
|
||||
}
|
||||
expect: {
|
||||
({}.f = function() {
|
||||
console.log("PASS");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
collapse_rhs_lhs: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: true,
|
||||
}
|
||||
input: {
|
||||
function f(a, b) {
|
||||
a.b = b, b += 2;
|
||||
console.log(a.b, b);
|
||||
}
|
||||
f({}, 1);
|
||||
}
|
||||
expect: {
|
||||
function f(a, b) {
|
||||
a.b = b, b += 2;
|
||||
console.log(a.b, b);
|
||||
}
|
||||
f({}, 1);
|
||||
}
|
||||
expect_stdout: "1 3"
|
||||
}
|
||||
|
||||
@@ -1178,9 +1178,7 @@ toplevel_on_loops_1: {
|
||||
console.log("bar:", --x);
|
||||
}
|
||||
var x = 3;
|
||||
do
|
||||
bar();
|
||||
while (x);
|
||||
for (;bar(), x;);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
@@ -1208,9 +1206,7 @@ toplevel_off_loops_1: {
|
||||
console.log("bar:", --x);
|
||||
}
|
||||
var x = 3;
|
||||
do
|
||||
bar();
|
||||
while (x);
|
||||
for (;bar(), x;);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
@@ -1265,9 +1261,7 @@ toplevel_off_loops_2: {
|
||||
console.log("bar:");
|
||||
}
|
||||
var x = 3;
|
||||
do
|
||||
bar();
|
||||
while (x);
|
||||
for (;bar(), x;);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5533,3 +5527,21 @@ issue_2869: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2919: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unsafe: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var arr = [ function() {} ];
|
||||
console.log(typeof arr[0]);
|
||||
}
|
||||
expect: {
|
||||
console.log("function");
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ mangle_catch_toplevel: {
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);'
|
||||
expect_exact: 'var c="FAIL";try{throw 1}catch(o){c="PASS"}console.log(c);'
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ mangle_catch_var_toplevel: {
|
||||
}
|
||||
console.log(a);
|
||||
}
|
||||
expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);'
|
||||
expect_exact: 'var r="FAIL";try{throw 1}catch(o){var r="PASS"}console.log(r);'
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -451,7 +451,7 @@ function_iife_catch: {
|
||||
}
|
||||
f();
|
||||
}
|
||||
expect_exact: "function f(o){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
|
||||
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
|
||||
expect_stdout: "0 1"
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@ function_iife_catch_ie8: {
|
||||
}
|
||||
f();
|
||||
}
|
||||
expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();"
|
||||
expect_exact: "function f(c){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();"
|
||||
expect_stdout: "0 1"
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ function_catch_catch: {
|
||||
}
|
||||
f();
|
||||
}
|
||||
expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
|
||||
expect_exact: "var o=0;function f(){try{throw 1}catch(o){try{throw 2}catch(c){var c=3;console.log(c)}}console.log(c)}f();"
|
||||
expect_stdout: [
|
||||
"3",
|
||||
"undefined",
|
||||
|
||||
@@ -102,12 +102,12 @@ dont_screw_try_catch: {
|
||||
};
|
||||
}
|
||||
expect: {
|
||||
bad = function(n){
|
||||
return function(t){
|
||||
bad = function(t){
|
||||
return function(n){
|
||||
try{
|
||||
n()
|
||||
} catch(n) {
|
||||
t(n)
|
||||
t()
|
||||
} catch(t) {
|
||||
n(t)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -349,11 +349,11 @@ issue_2254_1: {
|
||||
try {
|
||||
console.log(f("PASS"));
|
||||
} catch (e) {}
|
||||
function f(e) {
|
||||
function f(t) {
|
||||
try {
|
||||
throw "FAIL";
|
||||
} catch (t) {
|
||||
return e;
|
||||
} catch (e) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,3 +392,75 @@ issue_2254_2: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_24_1: {
|
||||
mangle = {
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
console.log(typeof function f(){} === typeof a ? "FAIL" : "PASS");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(o) {
|
||||
console.log(typeof function o(){} === typeof o ? "FAIL" : "PASS");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_24_2: {
|
||||
mangle = {
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
console.log(typeof function f(){} === typeof a ? "FAIL" : "PASS");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(n) {
|
||||
console.log(typeof function o(){} === typeof n ? "FAIL" : "PASS");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2976_1: {
|
||||
mangle = {
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
console.log(function f() {
|
||||
var a;
|
||||
return a === f ? "FAIL" : "PASS";
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function n() {
|
||||
var o;
|
||||
return o === n ? "FAIL" : "PASS";
|
||||
}());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2976_2: {
|
||||
mangle = {
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function f() {
|
||||
var a;
|
||||
return a === f ? "FAIL" : "PASS";
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function n() {
|
||||
var o;
|
||||
return o === n ? "FAIL" : "PASS";
|
||||
}());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -668,8 +668,7 @@ side_effects_cascade_2: {
|
||||
}
|
||||
expect: {
|
||||
function f(a, b) {
|
||||
b = a,
|
||||
!a + (b += a) || (b += a),
|
||||
!(b = a) + (b += a) || (b += a),
|
||||
b = a;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ if_return: {
|
||||
conditionals: true,
|
||||
if_return: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
function f(w, x, y, z) {
|
||||
@@ -124,7 +125,7 @@ if_return: {
|
||||
if (w) {
|
||||
if (y) return;
|
||||
} else if (z) return;
|
||||
return x == y || (x && w(), y && z(), !0);
|
||||
return x == y || (x && w(), y && z()), !0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +46,10 @@ describe("minify", function() {
|
||||
assert.strictEqual(compressed, [
|
||||
"function n(n){return 3*n}",
|
||||
"function r(n){return n/2}",
|
||||
"var o=console.log.bind(console);",
|
||||
'function c(n){o("Foo:",2*n)}',
|
||||
"var c=console.log.bind(console);",
|
||||
'function o(o){c("Foo:",2*o)}',
|
||||
"var a=n(3),b=r(12);",
|
||||
'o("qux",a,b),c(11);',
|
||||
'c("qux",a,b),o(11);',
|
||||
].join(""));
|
||||
assert.strictEqual(run_code(compressed), run_code(original));
|
||||
});
|
||||
@@ -79,10 +79,10 @@ describe("minify", function() {
|
||||
assert.strictEqual(compressed, [
|
||||
"function n(n){return 3*n}",
|
||||
"function r(n){return n/2}",
|
||||
"var o=console.log.bind(console);",
|
||||
'function c(n){o("Foo:",2*n)}',
|
||||
"var c=console.log.bind(console);",
|
||||
'function o(o){c("Foo:",2*o)}',
|
||||
"var a=n(3),b=r(12);",
|
||||
'o("qux",a,b),c(11);',
|
||||
'c("qux",a,b),o(11);',
|
||||
].join(""));
|
||||
assert.strictEqual(run_code(compressed), run_code(original));
|
||||
});
|
||||
@@ -226,7 +226,7 @@ describe("minify", function() {
|
||||
content: "inline"
|
||||
}
|
||||
});
|
||||
assert.strictEqual(result.code, "var bar=function(){return function(bar){return bar}}();");
|
||||
assert.strictEqual(result.code, "var bar=function(bar){return bar};");
|
||||
assert.strictEqual(warnings.length, 1);
|
||||
assert.strictEqual(warnings[0], "inline source map not found");
|
||||
} finally {
|
||||
|
||||
@@ -135,7 +135,6 @@ function run_compress_tests() {
|
||||
var output = cmp.compress(input);
|
||||
output.figure_out_scope(test.mangle);
|
||||
if (test.mangle) {
|
||||
U.base54.reset();
|
||||
output.compute_char_frequency(test.mangle);
|
||||
output.mangle_names(test.mangle);
|
||||
if (test.mangle.properties) {
|
||||
@@ -183,7 +182,7 @@ function run_compress_tests() {
|
||||
}
|
||||
if (test.expect_stdout
|
||||
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
|
||||
var stdout = sandbox.run_code(input_code);
|
||||
var stdout = sandbox.run_code(input_code, true);
|
||||
if (test.expect_stdout === true) {
|
||||
test.expect_stdout = stdout;
|
||||
}
|
||||
@@ -197,7 +196,7 @@ function run_compress_tests() {
|
||||
});
|
||||
return false;
|
||||
}
|
||||
stdout = sandbox.run_code(output);
|
||||
stdout = sandbox.run_code(output, true);
|
||||
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
|
||||
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
||||
input: input_formatted,
|
||||
@@ -372,13 +371,14 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
|
||||
var options_formatted = JSON.stringify(options, null, 4);
|
||||
var result = U.minify(input_code, options);
|
||||
if (result.error) {
|
||||
log("!!! failed input reminify\n---INPUT---\n{input}\n--ERROR---\n{error}\n\n", {
|
||||
log("!!! failed input reminify\n---INPUT---\n{input}\n---OPTIONS---\n{options}\n--ERROR---\n{error}\n\n", {
|
||||
input: input_formatted,
|
||||
options: options_formatted,
|
||||
error: result.error,
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
var stdout = sandbox.run_code(result.code);
|
||||
var stdout = sandbox.run_code(result.code, true);
|
||||
if (typeof expect_stdout != "string" && typeof stdout != "string" && expect_stdout.name == stdout.name) {
|
||||
stdout = expect_stdout;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,27 @@
|
||||
var semver = require("semver");
|
||||
var vm = require("vm");
|
||||
|
||||
function createContext() {
|
||||
var context = Object.create(null);
|
||||
Object.defineProperty(context, "console", {
|
||||
value: function() {
|
||||
var con = Object.create(null);
|
||||
Object.defineProperty(con, "log", {
|
||||
value: function(msg) {
|
||||
if (arguments.length == 1 && typeof msg == "string") {
|
||||
return console.log("%s", msg);
|
||||
}
|
||||
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
||||
return safe_log(arg, 3);
|
||||
}));
|
||||
}
|
||||
});
|
||||
return con;
|
||||
}()
|
||||
});
|
||||
return vm.createContext(context);
|
||||
}
|
||||
|
||||
function safe_log(arg, level) {
|
||||
if (arg) switch (typeof arg) {
|
||||
case "function":
|
||||
@@ -9,7 +30,8 @@ function safe_log(arg, level) {
|
||||
if (/Error$/.test(arg.name)) return arg.toString();
|
||||
arg.constructor.toString();
|
||||
if (level--) for (var key in arg) {
|
||||
if (!Object.getOwnPropertyDescriptor(arg, key).get) {
|
||||
var desc = Object.getOwnPropertyDescriptor(arg, key);
|
||||
if (!desc || !desc.get) {
|
||||
arg[key] = safe_log(arg[key], level);
|
||||
}
|
||||
}
|
||||
@@ -21,16 +43,16 @@ function strip_func_ids(text) {
|
||||
return text.toString().replace(/F[0-9]{6}N/g, "<F<>N>");
|
||||
}
|
||||
|
||||
var context;
|
||||
var FUNC_TOSTRING = [
|
||||
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String].forEach(function(f) {",
|
||||
" f.toString = Function.prototype.toString;",
|
||||
" f.valueOf = Function.prototype.valueOf;",
|
||||
"});",
|
||||
"Function.prototype.toString = Function.prototype.valueOf = function() {",
|
||||
"Function.prototype.toString = function() {",
|
||||
" var id = 100000;",
|
||||
" return function() {",
|
||||
" var n = this.name;",
|
||||
' if (!/^F[0-9]{6}N$/.test(n)) {',
|
||||
" if (!/^F[0-9]{6}N$/.test(n)) {",
|
||||
' n = "F" + ++id + "N";',
|
||||
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
|
||||
' Object.defineProperty(this, "name", {',
|
||||
@@ -40,40 +62,34 @@ var FUNC_TOSTRING = [
|
||||
" });",
|
||||
] : [], [
|
||||
" }",
|
||||
' return "[Function: " + n + "]";',
|
||||
" }",
|
||||
' return "function(){}";',
|
||||
" };",
|
||||
"}();",
|
||||
'Object.defineProperty(Function.prototype, "valueOf", { enumerable: false });',
|
||||
]).join("\n");
|
||||
exports.run_code = function(code) {
|
||||
exports.run_code = function(code, reuse) {
|
||||
var stdout = "";
|
||||
var original_write = process.stdout.write;
|
||||
process.stdout.write = function(chunk) {
|
||||
stdout += chunk;
|
||||
};
|
||||
try {
|
||||
vm.runInNewContext([
|
||||
if (!reuse || !context) context = createContext();
|
||||
vm.runInContext([
|
||||
FUNC_TOSTRING,
|
||||
"!function() {",
|
||||
code,
|
||||
"}();",
|
||||
].join("\n"), {
|
||||
console: {
|
||||
log: function(msg) {
|
||||
if (arguments.length == 1 && typeof msg == "string") {
|
||||
return console.log("%s", msg);
|
||||
}
|
||||
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
||||
return safe_log(arg, 3);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}, { timeout: 5000 });
|
||||
].join("\n"), context, { timeout: 5000 });
|
||||
return stdout;
|
||||
} catch (ex) {
|
||||
return ex;
|
||||
} finally {
|
||||
process.stdout.write = original_write;
|
||||
if (!reuse || /prototype/.test(code)) {
|
||||
context = null;
|
||||
} else for (var key in context) {
|
||||
delete context[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
|
||||
|
||||
Reference in New Issue
Block a user