Compare commits

...

29 Commits

Author SHA1 Message Date
Alex Lam S.L
38bfb73f06 v3.1.8 2017-11-07 03:55:16 +08:00
Alex Lam S.L
bbedbf4ea0 handle circular function reference gracefully (#2446)
fixes #2442
2017-11-07 02:37:23 +08:00
Alex Lam S.L
2cfb5aa7da account for eval & with in reduce_vars (#2441)
fixes #2440
2017-11-06 16:10:57 +08:00
Alex Lam S.L
6c45101870 consolidate & enhance unused (#2439)
- defer declaration removal in `collapse_vars`
- account for `AST_SymbolFunarg` in deduplication
- private accounting for `collapse_vars`
- avoid issues with identity reference due to deep cloning

fixes #2437
2017-11-06 14:25:10 +08:00
Alex Lam S.L
2c2fd89e34 inline single-use functions that are not constant expressions (#2434)
fixes #2428
2017-11-05 22:14:11 +08:00
Alex Lam S.L
f46281e2b7 v3.1.7 2017-11-05 15:03:19 +08:00
Alex Lam S.L
25a18883f5 tweak #2424 (#2432) 2017-11-05 12:49:14 +08:00
Alex Lam S.L
5b4b07e9a7 extend function inlining safety checks (#2430) 2017-11-05 06:18:45 +08:00
Alex Lam S.L
a8aa28a7a6 consolidate single-use function reduction (#2427)
fixes #2423
2017-11-05 04:27:01 +08:00
Alex Lam S.L
fe5a68f9d5 maintain call argument order in collapse_vars (#2426)
fixes #2425
2017-11-05 00:00:18 +08:00
Alex Lam S.L
71e61153b1 improve variations on call arguments for ufuzz (#2424) 2017-11-04 16:29:42 +08:00
Alex Lam S.L
c8b6f4733d reduce this within functions (#2421)
- only replace same-scope usages
- augment `test/ufuzz.js` to test for `this`


fixes #2420
2017-11-04 00:31:37 +08:00
Alex Lam S.L
a48f87abf2 compress new function containing this (#2417) 2017-10-30 23:19:27 +08:00
Alex Lam S.L
2fd927a7cc v3.1.6 2017-10-29 12:38:10 +08:00
Alex Lam S.L
8428326ea1 enhance properties (#2412)
- trim array items only if `side_effects`
- extend to non-identifier properties
2017-10-29 04:11:26 +08:00
Alex Lam S.L
31f8209193 remove dead code (#2405) 2017-10-27 14:28:09 +08:00
Alex Lam S.L
9b0f86f5a1 fix reduce_vars on AST_Array.length (#2404) 2017-10-27 02:33:37 +08:00
Alex Lam S.L
ee082ace1b compress self comparisons (#2398) 2017-10-26 01:16:12 +08:00
kzc
ae67a49850 document compress option hoist_props (#2399) 2017-10-25 14:03:43 +08:00
Alex Lam S.L
4178289c38 implement hoist_props (#2396)
fixes #2377
2017-10-25 03:38:11 +08:00
Alex Lam S.L
74ae16f9f8 fix unsafe reduce_vars on arrays & objects (#2397) 2017-10-24 22:10:36 +08:00
Tom MacWright
1968203d83 docs: Fix spelling and style (#2395) 2017-10-24 04:59:12 +08:00
Alex Lam S.L
86ea38a259 enhance unsafe evaluate of arrays & objects (#2394) 2017-10-24 02:58:30 +08:00
Alex Lam S.L
8a713e449f deduplicate declarations regardless of toplevel (#2393) 2017-10-23 01:00:50 +08:00
Alex Lam S.L
24aa07855b safer properties transform (#2391)
`{ a: x, b: y }.a` => `[ x, y ][0]`
- `x` cannot be function containing `this`

`[ x, y, z ][1]` => `(x, z, y)`
- only if `z` is side-effect-free
2017-10-22 20:10:13 +08:00
Alex Lam S.L
5fd723f143 fix unsafe expansion of object literals (#2390) 2017-10-22 15:00:36 +08:00
Alex Lam S.L
516eaef50c fix unsafe evaluation of AST_Sub (#2389) 2017-10-22 13:14:15 +08:00
Alex Lam S.L
4ae1fb3ed8 fix unsafe evaluation of objects (#2388) 2017-10-22 04:19:40 +08:00
Alex Lam S.L
011123223b fix unsafe escape analysis in reduce_vars (#2387) 2017-10-22 03:23:31 +08:00
16 changed files with 2387 additions and 385 deletions

View File

@@ -221,7 +221,7 @@ to prevent the `require`, `exports` and `$` names from being changed.
is a separate step, different from variable name mangling. Pass
`--mangle-props` to enable it. It will mangle all properties in the
input code with the exception of built in DOM properties and properties
in core javascript classes. For example:
in core JavaScript classes. For example:
```javascript
// example.js
@@ -236,7 +236,7 @@ x.bar_ = 2;
x["baz_"] = 3;
console.log(x.calc());
```
Mangle all properties (except for javascript `builtins`):
Mangle all properties (except for JavaScript `builtins`):
```bash
$ uglifyjs example.js -c -m --mangle-props
```
@@ -632,6 +632,12 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `hoist_funs` (default: `true`) -- hoist function declarations
- `hoist_props` (default: `false`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher,
and the `compress` option `toplevel` enabled.
- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false`
by default because it seems to increase the size of the output in general)
@@ -1060,7 +1066,7 @@ in total it's a bit more than just using UglifyJS's own parser.
### Uglify Fast Minify Mode
It's not well known, but whitespace removal and symbol mangling accounts
for 95% of the size reduction in minified code for most javascript - not
for 95% of the size reduction in minified code for most JavaScript - not
elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to

View File

@@ -60,6 +60,7 @@ function Compressor(options, false_by_default) {
expression : false,
global_defs : {},
hoist_funs : !false_by_default,
hoist_props : false,
hoist_vars : false,
ie8 : false,
if_return : !false_by_default,
@@ -149,7 +150,9 @@ merge(Compressor.prototype, {
}
var passes = +this.options.passes || 1;
var last_count = 1 / 0;
var mangle = { ie8: this.option("ie8") };
for (var pass = 0; pass < passes; pass++) {
node.figure_out_scope(mangle);
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this);
node = node.transform(this);
@@ -190,6 +193,7 @@ merge(Compressor.prototype, {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
node = node.hoist_properties(this);
node = node.hoist_declarations(this);
was_scope = true;
}
@@ -283,7 +287,7 @@ merge(Compressor.prototype, {
self.transform(tt);
});
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor) {
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
var reduce_vars = compressor.option("reduce_vars");
var unused = compressor.option("unused");
// Stack of look-up tables to keep track of whether a `SymbolDef` has been
@@ -311,29 +315,29 @@ merge(Compressor.prototype, {
d.references.push(node);
if (d.fixed === undefined || !safe_to_read(d) || d.single_use == "m") {
d.fixed = false;
} else {
} else if (d.fixed) {
var value = node.fixed_value();
if (unused) {
d.single_use = value
&& d.references.length == 1
&& loop_ids[d.id] === in_loop
&& d.scope === node.scope
&& value.is_constant_expression();
if (value && ref_once(d)) {
if (value instanceof AST_Lambda) {
d.single_use = d.scope === node.scope
&& !(d.orig[0] instanceof AST_SymbolFunarg)
|| value.is_constant_expression(node.scope);
} else {
d.single_use = d.scope === node.scope
&& loop_ids[d.id] === in_loop
&& value.is_constant_expression();
}
} else {
d.single_use = false;
}
if (is_modified(node, 0, is_immutable(value))) {
if (is_modified(node, value, 0, is_immutable(value))) {
if (d.single_use) {
d.single_use = "m";
} else {
d.fixed = false;
}
} else {
var parent = tw.parent();
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped = true;
}
mark_escaped(d, node, value, 0);
}
}
}
@@ -381,6 +385,10 @@ merge(Compressor.prototype, {
} else {
d.fixed = node;
mark(d, true);
if (ref_once(d)) {
d.single_use = d.scope === d.references[0].scope
|| node.is_constant_expression(d.references[0].scope);
}
}
var save_ids = safe_ids;
safe_ids = Object.create(null);
@@ -531,6 +539,7 @@ merge(Compressor.prototype, {
}
return true;
}
return def.fixed instanceof AST_Defun;
}
function safe_to_assign(def, value) {
@@ -553,8 +562,9 @@ merge(Compressor.prototype, {
}
function reset_def(def) {
def.direct_access = false;
def.escaped = false;
if (def.scope.uses_eval) {
if (def.scope.uses_eval || def.scope.uses_with) {
def.fixed = false;
} else if (!compressor.exposed(def)) {
def.fixed = undefined;
@@ -566,22 +576,72 @@ merge(Compressor.prototype, {
def.single_use = undefined;
}
function is_immutable(value) {
return value && (value.is_constant() || value instanceof AST_Lambda);
function ref_once(def) {
return unused && !def.scope.uses_eval && !def.scope.uses_with && def.references.length == 1;
}
function is_modified(node, level, immutable) {
function is_immutable(value) {
if (!value) return false;
return value.is_constant()
|| value instanceof AST_Lambda
|| value instanceof AST_This;
}
function read_property(obj, key) {
if (key instanceof AST_Constant) key = key.getValue();
if (key instanceof AST_Node) return null;
var value;
if (obj instanceof AST_Array) {
var elements = obj.elements;
if (key == "length") return make_node_from_constant(elements.length, obj);
if (typeof key == "number" && key in elements) value = elements[key];
} else if (obj instanceof AST_Object) {
var props = obj.properties;
for (var i = props.length; --i >= 0;) {
var prop = props[i];
if (!(prop instanceof AST_ObjectKeyVal)) return;
if (!value && props[i].key === key) value = props[i].value;
}
}
return value instanceof AST_SymbolRef && value.fixed_value() || value;
}
function is_modified(node, value, level, immutable) {
var parent = tw.parent(level);
if (is_lhs(node, parent)
|| !immutable && parent instanceof AST_Call && parent.expression === node) {
|| !immutable
&& parent instanceof AST_Call
&& parent.expression === node
&& (!(value instanceof AST_Function) || value.contains_this(parent))) {
return true;
} else if (parent instanceof AST_Array || parent instanceof AST_Object) {
return is_modified(parent, parent, level + 1);
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
return !immutable && is_modified(parent, level + 1);
return !immutable && is_modified(parent, read_property(value, parent.property), level + 1);
}
}
function mark_escaped(d, node, value, level) {
var parent = tw.parent(level);
if (value instanceof AST_Constant || value instanceof AST_Function) return;
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped = true;
return;
} else if (parent instanceof AST_Array || parent instanceof AST_Object) {
mark_escaped(d, parent, parent, level + 1);
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
value = read_property(value, parent.property);
mark_escaped(d, parent, value, level + 1);
if (value) return;
}
if (level == 0) d.direct_access = true;
}
});
AST_SymbolRef.DEFMETHOD("fixed_value", function() {
AST_Symbol.DEFMETHOD("fixed_value", function() {
var fixed = this.definition().fixed;
if (!fixed || fixed instanceof AST_Node) return fixed;
return fixed();
@@ -732,6 +792,14 @@ merge(Compressor.prototype, {
|| compressor.option("unsafe") && global_names(this.name);
});
function drop_decl(def) {
def._eliminiated = (def._eliminiated || 0) + 1;
if (def.orig.length == def._eliminiated) {
def.scope.functions.del(def.name);
def.scope.variables.del(def.name);
}
}
function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10;
do {
@@ -765,6 +833,7 @@ merge(Compressor.prototype, {
function collapse(statements, compressor) {
var scope = compressor.find_parent(AST_Scope);
if (scope.uses_eval || scope.uses_with) return statements;
var args;
var candidates = [];
var stat_index = statements.length;
while (--stat_index >= 0) {
@@ -785,7 +854,7 @@ merge(Compressor.prototype, {
var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1;
var side_effects = value_has_side_effects(candidate);
var hit = candidate.name instanceof AST_SymbolFunarg;
var abort = false, replaced = false;
var abort = false, replaced = false, can_replace = !args || !hit;
var tt = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
@@ -810,7 +879,8 @@ merge(Compressor.prototype, {
return node;
}
// Replace variable with assignment when found
if (!(node instanceof AST_SymbolDeclaration)
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& !is_lhs(node, parent)
&& lhs.equivalent_to(node)) {
CHANGED = replaced = abort = true;
@@ -861,6 +931,12 @@ merge(Compressor.prototype, {
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
});
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; j < args.length; j++) {
args[j].transform(tt);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(tt);
}
@@ -878,12 +954,18 @@ merge(Compressor.prototype, {
&& iife.expression === fn) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && fn.body.indexOf(fn_strict) < 0) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null);
for (var i = fn.argnames.length; --i >= 0;) {
for (var i = len; --i >= 0;) {
var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue;
names[sym.name] = true;
var arg = iife.args[i];
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else {
var tw = new TreeWalker(function(node) {
@@ -930,7 +1012,8 @@ merge(Compressor.prototype, {
function get_lhs(expr) {
if (expr instanceof AST_VarDef) {
var def = expr.name.definition();
if (def.orig.length > 1 && !(expr.name instanceof AST_SymbolFunarg)
if (def.orig.length - (def._eliminiated || 0) > 1
&& !(expr.name instanceof AST_SymbolFunarg)
|| def.references.length == 1 && !compressor.exposed(def)) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
@@ -939,6 +1022,10 @@ merge(Compressor.prototype, {
}
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
@@ -949,7 +1036,7 @@ merge(Compressor.prototype, {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw);
get_rvalue(expr).walk(tw);
return lvalues;
}
@@ -973,7 +1060,9 @@ merge(Compressor.prototype, {
if (node === expr) {
found = true;
if (node instanceof AST_VarDef) {
remove(node.name.definition().orig, node.name);
drop_decl(node.name.definition());
node.value = null;
return node;
}
return in_list ? MAP.skip : null;
}
@@ -982,16 +1071,13 @@ merge(Compressor.prototype, {
case 0: return null;
case 1: return node.expressions[0];
}
if (node instanceof AST_Definitions && node.definitions.length == 0
|| node instanceof AST_SimpleStatement && !node.body) {
return null;
}
if (node instanceof AST_SimpleStatement && !node.body) return null;
}));
}
function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false;
return expr[expr instanceof AST_Assign ? "right" : "value"].has_side_effects(compressor);
return get_rvalue(expr).has_side_effects(compressor);
}
function references_in_scope(def) {
@@ -1607,35 +1693,6 @@ merge(Compressor.prototype, {
&& unaryPrefix(this.operator);
}
});
// Obtain the constant value of an expression already known to be constant.
// Result only valid iff this.is_constant() is true.
AST_Node.DEFMETHOD("constant_value", function(compressor){
// Accomodate when option evaluate=false.
if (this instanceof AST_Constant && !(this instanceof AST_RegExp)) {
return this.value;
}
// Accomodate the common constant expressions !0 and -1 when option evaluate=false.
if (this instanceof AST_UnaryPrefix
&& this.expression instanceof AST_Constant) switch (this.operator) {
case "!":
return !this.expression.value;
case "~":
return ~this.expression.value;
case "-":
return -this.expression.value;
case "+":
return +this.expression.value;
default:
throw new Error(string_template("Cannot evaluate unary expression {value}", {
value: this.print_to_string()
}));
}
var result = this.evaluate(compressor);
if (result !== this) {
return result;
}
throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
});
def(AST_Statement, function(){
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
});
@@ -1654,6 +1711,7 @@ merge(Compressor.prototype, {
var elements = [];
for (var i = 0, len = this.elements.length; i < len; i++) {
var element = this.elements[i];
if (element instanceof AST_Function) continue;
var value = ev(element, compressor);
if (element === value) return this;
elements.push(value);
@@ -1677,6 +1735,7 @@ merge(Compressor.prototype, {
if (typeof Object.prototype[key] === 'function') {
return this;
}
if (prop.value instanceof AST_Function) continue;
val[key] = ev(prop.value, compressor);
if (val[key] === prop.value) return this;
}
@@ -2135,18 +2194,22 @@ merge(Compressor.prototype, {
}
def(AST_Node, return_false);
def(AST_Constant, return_true);
def(AST_Function, function(){
def(AST_Lambda, function(scope){
var self = this;
var result = true;
self.walk(new TreeWalker(function(node) {
if (!result) return true;
if (node instanceof AST_SymbolRef) {
var def = node.definition();
if (self.enclosed.indexOf(def) >= 0
&& self.variables.get(def.name) !== def) {
if (member(def, self.enclosed)
&& !self.variables.has(def.name)) {
if (scope) {
var scope_def = scope.find_variable(node);
if (def.undeclared ? !scope_def : scope_def === def) return true;
}
result = false;
return true;
}
return true;
}
}));
return result;
@@ -2234,7 +2297,6 @@ 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;
if (!drop_funcs && !drop_vars) return;
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node) {
if (node instanceof AST_Assign && (node.write_only || node.operator == "=")) {
return node.left;
@@ -2257,58 +2319,63 @@ merge(Compressor.prototype, {
// this scope (not in nested scopes).
var scope = this;
var tw = new TreeWalker(function(node, descend){
if (node !== self) {
if (node instanceof AST_Defun) {
if (!drop_funcs && scope === self) {
var node_def = node.name.definition();
if (node === self) return;
if (node instanceof AST_Defun) {
if (!drop_funcs && scope === self) {
var node_def = node.name.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
initializations.add(node.name.name, node);
return true; // don't go in nested scopes
}
if (node instanceof AST_SymbolFunarg && scope === self) {
var_defs_by_id.add(node.definition().id, node);
}
if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def){
var node_def = def.name.definition();
if (def.name instanceof AST_SymbolVar) {
var_defs_by_id.add(node_def.id, def);
}
if (!drop_vars) {
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
initializations.add(node.name.name, node);
return true; // don't go in nested scopes
}
if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def){
var node_def = def.name.definition();
if (def.name instanceof AST_SymbolVar) {
var_defs_by_id.add(node_def.id, def);
if (def.value) {
initializations.add(def.name.name, def.value);
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
if (!drop_vars) {
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
}
if (def.value) {
initializations.add(def.name.name, def.value);
if (def.value.has_side_effects(compressor)) {
def.value.walk(tw);
}
}
});
return true;
}
if (assign_as_unused(node) instanceof AST_SymbolRef && scope === self) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
});
return true;
}
var sym;
if (scope === self
&& (sym = assign_as_unused(node)) instanceof AST_SymbolRef
&& self.variables.get(sym.name) === sym.definition()) {
if (node instanceof AST_Assign) node.right.walk(tw);
return true;
}
if (node instanceof AST_SymbolRef) {
var node_def = node.definition();
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
}
return true;
}
if (node instanceof AST_Scope) {
var save_scope = scope;
scope = node;
descend();
scope = save_scope;
return true;
}
});
self.walk(tw);
@@ -2363,13 +2430,16 @@ merge(Compressor.prototype, {
}
}
if (drop_funcs && node instanceof AST_Defun && node !== self) {
if (!(node.name.definition().id in in_use_ids)) {
var def = node.name.definition();
if (!(def.id in in_use_ids)) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
drop_decl(def);
return make_node(AST_EmptyStatement, node);
}
return node;
}
if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) {
var parent = tt.parent();
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
// place uninitialized names at the start
var body = [], head = [], tail = [];
// for unused names whose initialization has
@@ -2379,13 +2449,13 @@ merge(Compressor.prototype, {
node.definitions.forEach(function(def) {
if (def.value) def.value = def.value.transform(tt);
var sym = def.name.definition();
if (sym.id in in_use_ids) {
if (!drop_vars || sym.id in in_use_ids) {
if (def.name instanceof AST_SymbolVar) {
var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && !def.value) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
remove(var_defs, def);
remove(sym.orig, def.name);
drop_decl(sym);
return;
}
}
@@ -2418,7 +2488,7 @@ merge(Compressor.prototype, {
} else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
}
remove(sym.orig, def.name);
drop_decl(sym);
}
});
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
@@ -2427,7 +2497,7 @@ merge(Compressor.prototype, {
var def = tail.pop();
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
remove(var_defs, def);
remove(def.name.definition().orig, def.name);
drop_decl(def.name.definition());
side_effects.unshift(make_node(AST_Assign, def, {
operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name),
@@ -2445,23 +2515,22 @@ merge(Compressor.prototype, {
}));
}
switch (body.length) {
case 0:
case 0:
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
case 1:
case 1:
return body[0];
default:
default:
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
body: body
});
}
}
if (drop_vars) {
var def = assign_as_unused(node);
if (def instanceof AST_SymbolRef
&& !((def = def.definition()).id in in_use_ids)
&& self.variables.get(def.name) === def) {
var sym = assign_as_unused(node);
if (sym instanceof AST_SymbolRef
&& !(sym.definition().id in in_use_ids)) {
if (node instanceof AST_Assign) {
return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
return maintain_this_binding(parent, node, node.right.transform(tt));
}
return make_node(AST_Number, node, {
value: 0
@@ -2645,6 +2714,71 @@ merge(Compressor.prototype, {
return self;
});
AST_Scope.DEFMETHOD("hoist_properties", function(compressor){
var self = this;
if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return self;
var defs_by_id = Object.create(null);
var var_names = Object.create(null);
self.enclosed.forEach(function(def) {
var_names[def.name] = true;
});
self.variables.each(function(def, name) {
var_names[name] = true;
});
return self.transform(new TreeTransformer(function(node) {
if (node instanceof AST_VarDef) {
var sym = node.name, def, value;
if (sym.scope === self
&& !(def = sym.definition()).escaped
&& !def.single_use
&& !def.direct_access
&& (value = sym.fixed_value()) === node.value
&& value instanceof AST_Object) {
var defs = new Dictionary();
var assignments = [];
value.properties.forEach(function(prop) {
assignments.push(make_node(AST_VarDef, node, {
name: make_sym(prop.key),
value: prop.value
}));
});
defs_by_id[def.id] = defs;
return MAP.splice(assignments);
}
}
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var key = node.property;
if (key instanceof AST_Node) key = key.getValue();
var def = defs.get(key);
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
thedef: def
});
sym.reference({});
return sym;
}
}
function make_sym(key) {
var prefix = sym.name + "_" + key.toString().replace(/[^a-z_$]+/ig, "_");
var name = prefix;
for (var i = 0; var_names[name]; i++) name = prefix + "$" + i;
var new_var = make_node(sym.CTOR, sym, {
name: name,
scope: self
});
var def = self.def_variable(new_var);
defs.set(key, def);
self.enclosed.push(def);
var_names[name] = true;
return new_var;
}
}));
});
// drop_side_effect_free()
// remove side-effect-free parts which only affects return value
(function(def){
@@ -3164,7 +3298,7 @@ merge(Compressor.prototype, {
}));
if (reduce_vars) name.definition().fixed = false;
}
remove(def.name.definition().orig, def.name);
drop_decl(def.name.definition());
return a;
}, []);
if (assignments.length == 0) return null;
@@ -3406,6 +3540,7 @@ merge(Compressor.prototype, {
&& !exp.uses_arguments
&& !exp.uses_eval
&& exp.body.length == 1
&& !exp.contains_this()
&& all(exp.argnames, function(arg) {
return arg.__unused;
})
@@ -3419,23 +3554,6 @@ merge(Compressor.prototype, {
expression: stat.body
});
}
if (value) {
var tw = new TreeWalker(function(node) {
if (!value) return true;
if (node instanceof AST_SymbolRef) {
var ref = node.scope.find_variable(node);
if (ref && ref.scope.parent_scope === fn.parent_scope) {
value = null;
return true;
}
}
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
value = null;
return true;
}
});
value.walk(tw);
}
if (value) {
var args = self.args.concat(value);
return make_sequence(self, args).optimize(compressor);
@@ -3719,6 +3837,11 @@ merge(Compressor.prototype, {
});
var commutativeOperators = makePredicate("== === != !== * & | ^");
function is_object(node) {
return node instanceof AST_Array
|| node instanceof AST_Lambda
|| node instanceof AST_Object;
}
OPT(AST_Binary, function(self, compressor){
function reversible() {
@@ -3754,7 +3877,8 @@ merge(Compressor.prototype, {
case "!==":
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
(self.left.is_number(compressor) && self.right.is_number(compressor)) ||
(self.left.is_boolean() && self.right.is_boolean())) {
(self.left.is_boolean() && self.right.is_boolean()) ||
self.left.equivalent_to(self.right)) {
self.operator = self.operator.substr(0, 2);
}
// XXX: intentionally falling down to the next case
@@ -3774,6 +3898,13 @@ merge(Compressor.prototype, {
if (self.operator.length == 2) self.operator += "=";
}
}
// obj !== obj => false
else if (self.left instanceof AST_SymbolRef
&& self.right instanceof AST_SymbolRef
&& self.left.definition() === self.right.definition()
&& is_object(self.left.fixed_value())) {
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
}
break;
}
if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
@@ -4110,49 +4241,64 @@ merge(Compressor.prototype, {
if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed);
}
if (compressor.option("unused")
&& fixed
&& d.references.length == 1
&& (d.single_use || fixed instanceof AST_Function
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
&& !d.scope.uses_eval
&& compressor.find_parent(AST_Scope) === fixed.parent_scope)) {
var value = fixed.optimize(compressor);
return value === fixed ? fixed.clone(true) : value;
}
if (compressor.option("evaluate") && fixed) {
if (d.should_replace === undefined) {
var init = fixed.evaluate(compressor);
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
init = make_node_from_constant(init, fixed);
var value_length = init.optimize(compressor).print_to_string().length;
var fn;
if (has_symbol_ref(fixed)) {
fn = function() {
var result = init.optimize(compressor);
return result === init ? result.clone(true) : result;
};
} else {
value_length = Math.min(value_length, fixed.print_to_string().length);
fn = function() {
var result = best_of_expression(init.optimize(compressor), fixed);
return result === init || result === fixed ? result.clone(true) : result;
};
if (fixed && d.single_use) {
var recurse;
if (fixed instanceof AST_Function) {
for (var i = 0; recurse = compressor.parent(i); i++) {
if (recurse instanceof AST_Lambda) {
var name = recurse.name;
if (name && name.definition() === d) break;
}
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
d.should_replace = false;
}
}
if (d.should_replace) {
return d.should_replace();
if (!recurse) {
var value = fixed.optimize(compressor);
return value === fixed ? fixed.clone(true) : value;
}
}
if (fixed && d.should_replace === undefined) {
var init;
if (fixed instanceof AST_This) {
if (!(d.orig[0] instanceof AST_SymbolFunarg)
&& all(d.references, function(ref) {
return d.scope === ref.scope;
})) {
init = fixed;
}
} else {
var ev = fixed.evaluate(compressor);
if (ev !== fixed && (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))) {
init = make_node_from_constant(ev, fixed);
}
}
if (init) {
var value_length = init.optimize(compressor).print_to_string().length;
var fn;
if (has_symbol_ref(fixed)) {
fn = function() {
var result = init.optimize(compressor);
return result === init ? result.clone(true) : result;
};
} else {
value_length = Math.min(value_length, fixed.print_to_string().length);
fn = function() {
var result = best_of_expression(init.optimize(compressor), fixed);
return result === init || result === fixed ? result.clone(true) : result;
};
}
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
d.should_replace = false;
}
}
if (d.should_replace) {
return d.should_replace();
}
}
return self;
@@ -4446,19 +4592,69 @@ merge(Compressor.prototype, {
});
OPT(AST_Sub, function(self, compressor){
var expr = self.expression;
var prop = self.property;
if (prop instanceof AST_String && compressor.option("properties")) {
prop = prop.getValue();
if (is_identifier_string(prop)) {
return make_node(AST_Dot, self, {
expression : self.expression,
property : prop
}).optimize(compressor);
if (compressor.option("properties")) {
var key = prop.evaluate(compressor);
if (key !== prop) {
var property = "" + key;
if (is_identifier_string(property)
&& property.length <= prop.print_to_string().length + 1) {
return make_node(AST_Dot, self, {
expression: expr,
property: property
}).optimize(compressor);
}
if (!(prop instanceof AST_Number)) {
var value = parseFloat(property);
if (value.toString() == property) {
prop = self.property = make_node(AST_Number, prop, {
value: value
});
}
}
}
var v = parseFloat(prop);
if (!isNaN(v) && v.toString() == prop) {
self.property = make_node(AST_Number, self.property, {
value: v
}
if (is_lhs(self, compressor.parent())) return self;
if (key !== prop) {
var sub = self.flatten_object(property, compressor);
if (sub) {
expr = self.expression = sub.expression;
prop = self.property = sub.property;
}
}
if (compressor.option("properties") && compressor.option("side_effects")
&& prop instanceof AST_Number && expr instanceof AST_Array) {
var index = prop.getValue();
var elements = expr.elements;
if (index in elements) {
var flatten = true;
var values = [];
for (var i = elements.length; --i > index;) {
var value = elements[i].drop_side_effect_free(compressor);
if (value) {
values.unshift(value);
if (flatten && value.has_side_effects(compressor)) flatten = false;
}
}
var retValue = elements[index];
retValue = retValue instanceof AST_Hole ? make_node(AST_Undefined, retValue) : retValue;
if (!flatten) values.unshift(retValue);
while (--i >= 0) {
var value = elements[i].drop_side_effect_free(compressor);
if (value) values.unshift(value);
else index--;
}
if (flatten) {
values.push(retValue);
return make_sequence(self, values).optimize(compressor);
} else return make_node(AST_Sub, self, {
expression: make_node(AST_Array, expr, {
elements: values
}),
property: make_node(AST_Number, prop, {
value: index
})
});
}
}
@@ -4470,7 +4666,8 @@ merge(Compressor.prototype, {
return self;
});
AST_Lambda.DEFMETHOD("contains_this", function() {
AST_Lambda.DEFMETHOD("contains_this", function(grandparent) {
if (grandparent instanceof AST_New) return false;
var result;
var self = this;
self.walk(new TreeWalker(function(node) {
@@ -4481,25 +4678,40 @@ merge(Compressor.prototype, {
return result;
});
AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
if (!compressor.option("properties")) return;
var expr = this.expression;
if (expr instanceof AST_Object) {
var props = expr.properties;
for (var i = props.length; --i >= 0;) {
var prop = props[i];
if ("" + prop.key == key) {
if (!all(props, function(prop) {
return prop instanceof AST_ObjectKeyVal;
})) break;
var value = prop.value;
if (value instanceof AST_Function
&& value.contains_this(compressor.parent())) break;
return make_node(AST_Sub, this, {
expression: make_node(AST_Array, expr, {
elements: props.map(function(prop) {
return prop.value;
})
}),
property: make_node(AST_Number, this, {
value: i
})
});
}
}
}
});
OPT(AST_Dot, function(self, compressor){
var def = self.resolve_defines(compressor);
if (def) {
return def.optimize(compressor);
}
if (compressor.option("unsafe") && self.expression instanceof AST_Object) {
var values = self.expression.properties;
for (var i = values.length; --i >= 0;) {
if (values[i].key === self.property) {
var value = values[i].value;
if (value instanceof AST_Function ? !value.contains_this() : !value.has_side_effects(compressor)) {
var obj = self.expression.clone();
obj.properties = obj.properties.slice();
obj.properties.splice(i, 1);
return make_sequence(self, [ obj, value ]).optimize(compressor);
}
}
}
}
if (compressor.option("unsafe_proto")
&& self.expression instanceof AST_Dot
&& self.expression.property == "prototype") {
@@ -4522,6 +4734,9 @@ merge(Compressor.prototype, {
break;
}
}
if (is_lhs(self, compressor.parent())) return self;
var sub = self.flatten_object(self.property, compressor);
if (sub) return sub.optimize(compressor);
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);

View File

@@ -137,11 +137,9 @@ function minify(files, options) {
if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap);
}
if (timings) timings.scope1 = Date.now();
if (options.compress) toplevel.figure_out_scope(options.mangle);
if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now();
if (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) {
@@ -199,9 +197,9 @@ function minify(files, options) {
if (timings) {
timings.end = Date.now();
result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2),
compress: 1e-3 * (timings.scope2 - timings.compress),
parse: 1e-3 * (timings.compress - timings.parse),
compress: 1e-3 * (timings.scope - timings.compress),
scope: 1e-3 * (timings.mangle - timings.scope),
mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),

View File

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

View File

@@ -128,50 +128,110 @@ constant_join_3: {
for_loop: {
options = {
unsafe : true,
unused : true,
evaluate : true,
reduce_vars : true
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
};
input: {
function f0() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
console.log(a[i]);
}
var b = 0;
for (var i = 0; i < a.length; i++)
b += a[i];
return b;
}
function f1() {
var a = [1, 2, 3];
for (var i = 0, len = a.length; i < len; i++) {
console.log(a[i]);
}
var b = 0;
for (var i = 0, len = a.length; i < len; i++)
b += a[i];
return b;
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++) {
a[i]++;
}
}
}
expect: {
function f0() {
var a = [1, 2, 3];
for (var i = 0; i < 3; i++)
console.log(a[i]);
}
function f1() {
var a = [1, 2, 3];
for (var i = 0; i < 3; i++)
console.log(a[i]);
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++)
a[i]++;
return a[2];
}
console.log(f0(), f1(), f2());
}
expect: {
function f0() {
var a = [1, 2, 3];
var b = 0;
for (var i = 0; i < 3; i++)
b += a[i];
return b;
}
function f1() {
var a = [1, 2, 3];
var b = 0;
for (var i = 0; i < 3; i++)
b += a[i];
return b;
}
function f2() {
var a = [1, 2, 3];
for (var i = 0; i < a.length; i++)
a[i]++;
return a[2];
}
console.log(f0(), f1(), f2());
}
expect_stdout: "6 6 4"
}
index: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a[0], a[1]);
}
expect: {
console.log(1, 2);
}
expect_stdout: "1 2"
}
length: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a.length);
}
expect: {
console.log(2);
}
expect_stdout: "2"
}
index_length: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var a = [ 1, 2 ];
console.log(a[0], a.length);
}
expect: {
console.log(1, 2);
}
expect_stdout: "1 2"
}

View File

@@ -1388,6 +1388,7 @@ issue_1605_1: {
options = {
collapse_vars: true,
toplevel: false,
unused: true,
}
input: {
function foo(x) {
@@ -1410,6 +1411,7 @@ issue_1605_2: {
options = {
collapse_vars: true,
toplevel: "vars",
unused: true,
}
input: {
function foo(x) {
@@ -1537,6 +1539,7 @@ issue_1631_3: {
var_side_effects_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var print = console.log.bind(console);
@@ -1559,6 +1562,7 @@ var_side_effects_1: {
var_side_effects_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var print = console.log.bind(console);
@@ -1584,6 +1588,7 @@ var_side_effects_3: {
collapse_vars: true,
pure_getters: true,
unsafe: true,
unused: true,
}
input: {
var print = console.log.bind(console);
@@ -1659,6 +1664,7 @@ iife_2: {
}(foo);
}
expect: {
var foo;
!function(x) {
console.log(x);
}(bar());
@@ -1945,6 +1951,7 @@ ref_scope: {
chained_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 2;
@@ -1961,6 +1968,7 @@ chained_1: {
chained_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a;
@@ -2051,7 +2059,7 @@ inner_lvalues: {
console.log(null, a, b);
}
expect: {
var a, b = 10;
var b = 10;
var a = (--b || a || 3).toString(), c = --b + -a;
console.log(null, a, b);
}
@@ -2061,6 +2069,7 @@ inner_lvalues: {
double_def: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = x, a = a && y;
@@ -2075,6 +2084,7 @@ double_def: {
toplevel_single_reference: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a;
@@ -2084,9 +2094,10 @@ toplevel_single_reference: {
}
}
expect: {
var a;
for (var b in x)
for (var b in x) {
var a;
b(a = b);
}
}
}
@@ -2534,73 +2545,6 @@ issue_2319_3: {
expect_stdout: "true"
}
prop_side_effects_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log({
bar: function() {
return 2;
}
}.bar());
}
expect_stdout: [
"1",
"2",
]
}
prop_side_effects_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log(2);
}
expect_stdout: [
"1",
"2",
]
}
issue_2365: {
options = {
collapse_vars: true,
@@ -2956,6 +2900,7 @@ pure_getters_chain: {
options = {
collapse_vars: true,
pure_getters: true,
unused: true,
}
input: {
function o(t, r) {
@@ -2976,6 +2921,7 @@ pure_getters_chain: {
conditional_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(a, b) {
@@ -3000,6 +2946,7 @@ conditional_1: {
conditional_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f(a, b) {
@@ -3016,3 +2963,117 @@ conditional_2: {
}
expect_stdout: "5 5"
}
issue_2425_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect_stdout: "15"
}
issue_2425_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b, c) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b, c) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect_stdout: "15"
}
issue_2425_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b, b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b, b) {
(a |= 10).toString();
})(--a);
console.log(a);
}
expect_stdout: "15"
}
issue_2437: {
options = {
collapse_vars: true,
conditionals: true,
inline: true,
join_vars: true,
reduce_vars: true,
side_effects: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
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;
}
}
foo();
}
expect: {
!function() {
if (xhrDesc)
return result = !!(req = new XMLHttpRequest()).onreadystatechange,
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result;
var req = new XMLHttpRequest(), detectFunc = function() {};
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
}();
}
}

View File

@@ -73,4 +73,41 @@ dont_change_in_or_instanceof_expressions: {
1 instanceof 1;
null instanceof null;
}
}
}
self_comparison_1: {
options = {
comparisons: true,
}
input: {
a === a;
a !== b;
b.c === a.c;
b.c !== b.c;
}
expect: {
a == a;
a !== b;
b.c === a.c;
b.c != b.c;
}
}
self_comparison_2: {
options = {
comparisons: true,
reduce_vars: true,
toplevel: true,
}
input: {
function f() {}
var o = {};
console.log(f != f, o === o);
}
expect: {
function f() {}
var o = {};
console.log(false, true);
}
expect_stdout: "false true"
}

View File

@@ -1109,11 +1109,11 @@ var_catch_toplevel: {
}
}
issue_2105: {
issue_2105_1: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -1139,17 +1139,50 @@ issue_2105: {
});
}
expect: {
(function() {
var quux = function() {
({
prop: function() {
console.log;
console.log("PASS");
};
return {
prop: function() {
console.log;
quux();
}
}).prop();
}
expect_stdout: "PASS"
}
issue_2105_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
properties: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
};
})().prop();
return bar;
} );
});
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -386,10 +386,11 @@ unsafe_object_accessor: {
}
}
unsafe_function: {
prop_function: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
properties: true,
side_effects: true,
}
input: {
console.log(
@@ -402,9 +403,9 @@ unsafe_function: {
expect: {
console.log(
({a:{b:1},b:function(){}}) + 1,
({b:function(){}}, {b:1}) + 1,
({a:{b:1}}, function(){}) + 1,
({b:function(){}}, {b:1}).b + 1
({b:1}) + 1,
function(){} + 1,
2
);
}
expect_stdout: true
@@ -630,10 +631,11 @@ unsafe_string_bad_index: {
expect_stdout: true
}
unsafe_prototype_function: {
prototype_function: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
properties: true,
side_effects: true,
}
input: {
var a = ({valueOf: 0}) < 1;
@@ -652,8 +654,8 @@ unsafe_prototype_function: {
var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2];
var g = ({}, 0)();
var h = ({}, 0)();
var g = 0();
var h = 0();
}
}
@@ -1195,3 +1197,40 @@ issue_2231_2: {
}
expect_stdout: true
}
self_comparison_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var o = { n: NaN };
console.log(o.n == o.n, o.n === o.n, o.n != o.n, o.n !== o.n, typeof o.n);
}
expect: {
console.log(false, false, true, true, "number");
}
expect_stdout: "false false true true 'number'"
}
self_comparison_2: {
options = {
evaluate: true,
hoist_props: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { n: NaN };
console.log(o.n == o.n, o.n === o.n, o.n != o.n, o.n !== o.n, typeof o.n);
}
expect: {
console.log(false, false, true, true, "number");
}
expect_stdout: "false false true true 'number'"
}

View File

@@ -151,13 +151,13 @@ issue_1841_2: {
function_returning_constant_literal: {
options = {
reduce_vars: true,
unsafe: true,
toplevel: true,
evaluate: true,
cascade: true,
unused: true,
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function greeter() {
@@ -508,3 +508,41 @@ issue_2114_2: {
}
expect_stdout: "2"
}
issue_2428: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function bar(k) {
console.log(k);
}
function foo(x) {
return bar(x);
}
function baz(a) {
foo(a);
}
baz(42);
baz("PASS");
}
expect: {
function baz(a) {
console.log(a);
}
baz(42);
baz("PASS");
}
expect_stdout: [
"42",
"PASS",
]
}

View File

@@ -0,0 +1,399 @@
issue_2377_1: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
var obj_foo = 1, obj_cube = function(x) {
return x * x * x;
};
console.log(obj_foo, obj_cube(3));
}
expect_stdout: "1 27"
}
issue_2377_2: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
console.log(1, function(x) {
return x * x * x;
}(3));
}
expect_stdout: "1 27"
}
issue_2377_3: {
options = {
evaluate: true,
inline: true,
hoist_props: true,
passes: 3,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.cube(3));
}
expect: {
console.log(1, 27);
}
expect_stdout: "1 27"
}
direct_access_1: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 0;
var obj = {
a: 1,
b: 2,
};
for (var k in obj) a++;
console.log(a, obj.a);
}
expect: {
var a = 0;
var obj = {
a: 1,
b: 2,
};
for (var k in obj) a++;
console.log(a, obj.a);
}
expect_stdout: "2 1"
}
direct_access_2: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { a: 1 };
var f = function(k) {
if (o[k]) return "PASS";
};
console.log(f("a"));
}
expect: {
var o = { a: 1 };
console.log(function(k) {
if (o[k]) return "PASS";
}("a"));
}
expect_stdout: "PASS"
}
direct_access_3: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = { a: 1 };
o.b;
console.log(o.a);
}
expect: {
var o = { a: 1 };
o.b;
console.log(o.a);
}
expect_stdout: "1"
}
single_use: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var obj = {
bar: function() {
return 42;
},
};
console.log(obj.bar());
}
expect: {
console.log({
bar: function() {
return 42;
},
}.bar());
}
}
name_collision_1: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var obj_foo = 1;
var obj_bar = 2;
function f() {
var obj = {
foo: 3,
bar: 4,
"b-r": 5,
"b+r": 6,
"b!r": 7,
};
console.log(obj_foo, obj.foo, obj.bar, obj["b-r"], obj["b+r"], obj["b!r"]);
}
f();
}
expect: {
var obj_foo = 1;
var obj_bar = 2;
function f() {
var obj_foo$0 = 3,
obj_bar = 4,
obj_b_r = 5,
obj_b_r$0 = 6,
obj_b_r$1 = 7;
console.log(obj_foo, obj_foo$0, obj_bar, obj_b_r, obj_b_r$0, obj_b_r$1);
}
f();
}
expect_stdout: "1 3 4 5 6 7"
}
name_collision_2: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
p: 1,
0: function(x) {
return x;
},
1: 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);
}
expect: {
var o_p = 1,
o__ = function(x) {
return x;
},
o__$2 = function(x) {
return x + 1;
},
o__$0 = 2,
o__$1 = 3;
console.log(o_p === o_p, o__(4), o__$2(5), o__$0, o__$1);
}
expect_stdout: "true 4 6 2 3"
}
name_collision_3: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
p: 1,
0: function(x) {
return x;
},
1: function(x) {
return x + 1;
}
}, o__$0 = 2, o__$1 = 3;
console.log(o.p === o.p, o[0](4), o[1](5));
}
expect: {
var o_p = 1,
o__ = function(x) {
return x;
},
o__$2 = function(x) {
return x + 1;
},
o__$0 = 2,
o__$1 = 3;
console.log(o_p === o_p, o__(4), o__$2(5));
}
expect_stdout: "true 4 6"
}
contains_this_1: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p);
}
expect: {
console.log(1, 1);
}
expect_stdout: "1 1"
}
contains_this_2: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u);
}
expect: {
console.log(1, 1, function() {
return this === this;
});
}
expect_stdout: true
}
contains_this_3: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u());
}
expect: {
var o = {
u: function() {
return this === this;
},
p: 1
};
console.log(o.p, o.p, o.u());
}
expect_stdout: "1 1 true"
}
new_this: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: 1,
b: 2,
f: function(a) {
this.b = a;
}
};
console.log(new o.f(o.a).b, o.b);
}
expect: {
console.log(new function(a) {
this.b = a;
}(1).b, 2);
}
expect_stdout: "1 2"
}

View File

@@ -51,6 +51,7 @@ this_binding_collapse_vars: {
options = {
collapse_vars: true,
toplevel: true,
unused: true,
};
input: {
var c = a; c();

View File

@@ -1,7 +1,8 @@
keep_properties: {
options = {
properties: false
};
evaluate: true,
properties: false,
}
input: {
a["foo"] = "bar";
}
@@ -12,6 +13,7 @@ keep_properties: {
dot_properties: {
options = {
evaluate: true,
properties: true,
}
beautify = {
@@ -37,6 +39,7 @@ dot_properties: {
dot_properties_es5: {
options = {
evaluate: true,
properties: true,
}
beautify = {
@@ -61,8 +64,8 @@ dot_properties_es5: {
sub_properties: {
options = {
evaluate: true,
properties: true
};
properties: true,
}
input: {
a[0] = 0;
a["0"] = 1;
@@ -81,18 +84,18 @@ sub_properties: {
a[3.14] = 3;
a.if = 4;
a["foo bar"] = 5;
a[NaN] = 6;
a[null] = 7;
a.NaN = 6;
a.null = 7;
a[void 0] = 8;
}
}
evaluate_array_length: {
options = {
evaluate: true,
properties: true,
unsafe: true,
evaluate: true
};
}
input: {
a = [1, 2, 3].length;
a = [1, 2, 3].join()["len" + "gth"];
@@ -109,10 +112,10 @@ evaluate_array_length: {
evaluate_string_length: {
options = {
evaluate: true,
properties: true,
unsafe: true,
evaluate: true
};
}
input: {
a = "foo".length;
a = ("foo" + "bar")["len" + "gth"];
@@ -151,7 +154,8 @@ mangle_properties: {
mangle_unquoted_properties: {
options = {
properties: false
evaluate: true,
properties: false,
}
mangle = {
properties: {
@@ -249,7 +253,8 @@ mangle_debug_suffix: {
mangle_debug_suffix_keep_quoted: {
options = {
properties: false
evaluate: true,
properties: false,
}
mangle = {
properties: {
@@ -677,8 +682,8 @@ accessor_this: {
issue_2208_1: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
@@ -696,8 +701,8 @@ issue_2208_1: {
issue_2208_2: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
@@ -721,8 +726,8 @@ issue_2208_2: {
issue_2208_3: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
a = 42;
@@ -746,8 +751,8 @@ issue_2208_3: {
issue_2208_4: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
function foo() {}
@@ -770,8 +775,8 @@ issue_2208_4: {
issue_2208_5: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
@@ -804,3 +809,219 @@ issue_2256: {
g.keep = g.g;
}
}
lhs_prop_1: {
options = {
evaluate: true,
properties: true,
}
input: {
console.log(++{
a: 1
}.a);
}
expect: {
console.log(++{
a: 1
}.a);
}
expect_stdout: "2"
}
lhs_prop_2: {
options = {
evaluate: true,
inline: true,
properties: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
[1][0] = 42;
(function(a) {
a.b = "g";
})("abc");
(function(a) {
a[2] = "g";
})("def");
(function(a) {
a[""] = "g";
})("ghi");
}
expect: {
[1][0] = 42;
"abc".b = "g";
"def"[2] = "g";
"ghi"[""] = "g";
}
}
literal_duplicate_key_side_effects: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log({
a: "FAIL",
a: console.log ? "PASS" : "FAIL"
}.a);
}
expect: {
console.log(console.log ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
prop_side_effects_1: {
options = {
evaluate: true,
inline: true,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
var obj = {
bar: function() {
return 2;
}
};
console.log(obj.bar());
}
expect_stdout: [
"1",
"2",
]
}
prop_side_effects_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
"": function() {
return C + C;
}
};
console.log(obj[""]());
}
expect: {
console.log(1);
console.log(2);
}
expect_stdout: [
"1",
"2",
]
}
accessor_1: {
options = {
properties: true,
}
input: {
console.log({
a: "FAIL",
get a() {
return "PASS";
}
}.a);
}
expect: {
console.log({
a: "FAIL",
get a() {
return "PASS";
}
}.a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
accessor_2: {
options = {
properties: true,
}
input: {
console.log({
get a() {
return "PASS";
},
set a(v) {},
a: "FAIL"
}.a);
}
expect: {
console.log({
get a() {
return "PASS";
},
set a(v) {},
a: "FAIL"
}.a);
}
expect_stdout: true
}
array_hole: {
options = {
properties: true,
side_effects: true,
}
input: {
console.log(
[ 1, 2, , 3][1],
[ 1, 2, , 3][2],
[ 1, 2, , 3][3]
);
}
expect: {
console.log(2, void 0, 3);
}
expect_stdout: "2 undefined 3"
}
new_this: {
options = {
properties: true,
side_effects: true,
}
input: {
new {
f: function(a) {
this.a = a;
}
}.f(42);
}
expect: {
new function(a) {
this.a = a;
}(42);
}
}

View File

@@ -211,7 +211,116 @@ unsafe_evaluate: {
}
}
unsafe_evaluate_object: {
unsafe_evaluate_side_effect_free_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.p); return o; }());
console.log(function(){ var o={p:3}; console.log([o][0].p); return o.p; }());
}
expect: {
console.log(function(){ console.log(1); return 1; }());
console.log(function(){ var o={p:2}; console.log(2); return o; }());
console.log(function(){ console.log(3); return 3; }());
}
expect_stdout: true
}
unsafe_evaluate_side_effect_free_2: {
options = {
collapse_vars: true,
evaluate: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1},a=[o]; console.log(a[0].p); return o.p; }());
}
expect: {
console.log(function(){ console.log(1); return 1; }());
}
expect_stdout: true
}
unsafe_evaluate_escaped: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1}; console.log(o, o.p); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.p, o); return o.p; }());
console.log(function(){ var o={p:3},a=[o]; console.log(a[0].p++); return o.p; }());
}
expect: {
console.log(function(){ var o={p:1}; console.log(o, o.p); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.p, o); return o.p; }());
console.log(function(){ var o={p:3},a=[o]; console.log(a[0].p++); return o.p; }());
}
expect_stdout: true
}
unsafe_evaluate_modified: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:4}; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }());
function inc() { this.p++; }
console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }());
console.log(function(){ var o={p:7}; console.log([o][0].p++); return o.p; }());
}
expect: {
console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:4}; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }());
function inc() { this.p++; }
console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }());
console.log(function(){ var o={p:7}; console.log([o][0].p++); return o.p; }());
}
expect_stdout: true
}
unsafe_evaluate_unknown: {
options = {
evaluate: true,
reduce_vars: true,
unsafe: true,
unused: true,
}
input: {
console.log(function(){ var o={p:1}; console.log(o.not_present); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.prototype); return o.p; }());
console.log(function(){ var o={p:3}; console.log(o.hasOwnProperty); return o.p; }());
}
expect: {
console.log(function(){ var o={p:1}; console.log(o.not_present); return o.p; }());
console.log(function(){ var o={p:2}; console.log(o.prototype); return o.p; }());
console.log(function(){ var o={p:3}; console.log(o.hasOwnProperty); return o.p; }());
}
expect_stdout: true
}
unsafe_evaluate_object_1: {
options = {
evaluate : true,
reduce_vars : true,
@@ -251,7 +360,83 @@ unsafe_evaluate_object: {
}
}
unsafe_evaluate_array: {
unsafe_evaluate_object_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.bar, obj.square(2), obj.cube);
}
expect: {
var obj = {
foo: 1,
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(1, 2, obj.square(2), obj.cube);
}
expect_stdout: true
}
unsafe_evaluate_object_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var obj = {
get foo() {
return 1;
},
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.bar, obj.square(2), obj.cube);
}
expect: {
var obj = {
get foo() {
return 1;
},
bar: 2,
square: function(x) {
return x * x;
},
cube: function(x) {
return x * x * x;
},
};
console.log(obj.foo, obj.bar, obj.square(2), obj.cube);
}
expect_stdout: true
}
unsafe_evaluate_array_1: {
options = {
evaluate : true,
reduce_vars : true,
@@ -299,6 +484,132 @@ unsafe_evaluate_array: {
}
}
unsafe_evaluate_array_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var arr = [
1,
2,
function(x) {
return x * x;
},
function(x) {
return x * x * x;
},
];
console.log(arr[0], arr[1], arr[2](2), arr[3]);
}
expect: {
var arr = [
1,
2,
function(x) {
return x * x;
},
function(x) {
return x * x * x;
},
];
console.log(1, 2, arr[2](2), arr[3]);
}
expect_stdout: true
}
unsafe_evaluate_array_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var arr = [
1,
2,
function() {
return ++arr[0];
},
];
console.log(arr[0], arr[1], arr[2](), arr[0]);
}
expect: {
var arr = [
1,
2,
function() {
return ++arr[0];
},
];
console.log(arr[0], arr[1], arr[2](), arr[0]);
}
expect_stdout: "1 2 2 2"
}
unsafe_evaluate_array_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var arr = [
1,
2,
function() {
return ++this[0];
},
];
console.log(arr[0], arr[1], arr[2], arr[0]);
}
expect: {
var arr = [
1,
2,
function() {
return ++this[0];
},
];
console.log(1, 2, arr[2], 1);
}
expect_stdout: true
}
unsafe_evaluate_array_5: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var arr = [
1,
2,
function() {
return ++this[0];
},
];
console.log(arr[0], arr[1], arr[2](), arr[0]);
}
expect: {
var arr = [
1,
2,
function() {
return ++this[0];
},
];
console.log(arr[0], arr[1], arr[2](), arr[0]);
}
expect_stdout: "1 2 2 2"
}
unsafe_evaluate_equality_1: {
options = {
evaluate : true,
@@ -1820,14 +2131,13 @@ redefine_farg_1: {
}
expect: {
function f(a) {
var a;
return typeof a;
}
function g() {
return"number";
return "number";
}
function h(a, b) {
var a = b;
a = b;
return typeof a;
}
console.log(f([]), g([]), h([]));
@@ -1862,10 +2172,9 @@ redefine_farg_2: {
}
expect: {
console.log(function(a) {
var a;
return typeof a;
}([]), "number",function(a, b) {
var a = b;
a = b;
return typeof a;
}([]));
}
@@ -1874,11 +2183,13 @@ redefine_farg_2: {
redefine_farg_3: {
options = {
cascade: true,
evaluate: true,
inline: true,
keep_fargs: false,
passes: 3,
passes: 2,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
@@ -2660,6 +2971,7 @@ obj_var_2: {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@@ -2716,10 +3028,10 @@ obj_arg_2: {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
@@ -2954,3 +3266,582 @@ const_expr_2: {
}
expect_stdout: "2 2"
}
escaped_prop: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var obj = { o: { a: 1 } };
(function(o) {
o.a++;
})(obj.o);
(function(o) {
console.log(o.a);
})(obj.o);
}
expect: {
var obj = { o: { a: 1 } };
obj.o.a++;
console.log(obj.o.a);
}
expect_stdout: "2"
}
issue_2420_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function run() {
var self = this;
if (self.count++)
self.foo();
else
self.bar();
}
var o = {
count: 0,
foo: function() { console.log("foo"); },
bar: function() { console.log("bar"); },
};
run.call(o);
run.call(o);
}
expect: {
function run() {
if (this.count++)
this.foo();
else
this.bar();
}
var o = {
count: 0,
foo: function() { console.log("foo"); },
bar: function() { console.log("bar"); },
};
run.call(o);
run.call(o);
}
expect_stdout: [
"bar",
"foo",
]
}
issue_2420_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function f() {
var that = this;
if (that.bar)
that.foo();
else
!function(that, self) {
console.log(this === that, self === this, that === self);
}(that, this);
}
f.call({
bar: 1,
foo: function() { console.log("foo", this.bar); },
});
f.call({});
}
expect: {
function f() {
if (this.bar)
this.foo();
else
!function(that, self) {
console.log(this === that, self === this, that === self);
}(this, this);
}
f.call({
bar: 1,
foo: function() { console.log("foo", this.bar); },
});
f.call({});
}
expect_stdout: [
"foo 1",
"false false true",
]
}
issue_2423_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
p();
}
expect: {
function p() { console.log(function() { return 1; }()); }
p();
p();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_2: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
p();
}
expect: {
function p() { console.log(1); }
p();
p();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
}
expect: {
(function() { console.log(function() { return 1; }()); })();
}
expect_stdout: "1"
}
issue_2423_4: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
}
expect: {
console.log(1);
}
expect_stdout: "1"
}
issue_2423_5: {
options = {
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function x() {
y();
}
function y() {
console.log(1);
}
function z() {
function y() {
console.log(2);
}
x();
}
z();
z();
}
expect: {
function z() {
console.log(1);
}
z();
z();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_6: {
options = {
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function x() {
y();
}
function y() {
console.log(1);
}
function z() {
function y() {
console.log(2);
}
x();
y();
}
z();
z();
}
expect: {
function z(){
console.log(1);
console.log(2);
}
z();
z();
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2440_eval_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
}
expect: {
function foo() {
return bar();
}
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
}
}
issue_2440_eval_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
function foo() {
return bar();
}
}
expect: {
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
function foo() {
return bar();
}
}
}
issue_2440_with_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
baz = {
quux: foo
};
with (o) whatever();
}
expect: {
function foo() {
return bar();
}
baz = {
quux: foo
};
with (o) whatever();
}
}
issue_2440_with_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
baz = {
quux: foo
};
with (o) whatever();
function foo() {
return bar();
}
}
expect: {
baz = {
quux: foo
};
with (o) whatever();
function foo() {
return bar();
}
}
}
issue_2442: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
foo();
}
}
expect: {}
}
recursive_inlining_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo() { bar(); }
function bar() { foo(); }
console.log("PASS");
}();
}
expect: {
!function() {
console.log("PASS");
}();
}
expect_stdout: "PASS"
}
recursive_inlining_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo() { qux(); }
function bar() { foo(); }
function qux() { bar(); }
console.log("PASS");
}();
}
expect: {
!function() {
console.log("PASS");
}();
}
expect_stdout: "PASS"
}
recursive_inlining_3: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
}();
}
expect: {
!function() {
function qux(x) {
console.log("qux", x);
if (x) (function(x) {
console.log("foo", x);
if (x) (function(x) {
console.log("bar", x);
if (x) qux(x - 1);
})(x - 1);
})(x - 1);
}
qux(4);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}
recursive_inlining_4: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
bar(5);
}();
}
expect: {
!function() {
function bar(x) {
console.log("bar", x);
if (x) qux(x - 1);
}
function qux(x) {
console.log("qux", x);
if (x) (function(x) {
console.log("foo", x);
if (x) bar(x - 1);
})(x - 1);
}
qux(4);
bar(5);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"bar 5",
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}
recursive_inlining_5: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
bar(5);
foo(3);
}();
}
expect: {
!function() {
function foo(x) {
console.log("foo", x);
if (x) bar(x - 1);
}
function bar(x) {
console.log("bar", x);
if (x) qux(x - 1);
}
function qux(x) {
console.log("qux", x);
if (x) foo(x - 1);
}
qux(4);
bar(5);
foo(3);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"bar 5",
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}

View File

@@ -162,6 +162,7 @@ var VALUES = [
'"object"',
'"number"',
'"function"',
'this',
];
var BINARY_OPS_NO_COMMA = [
@@ -349,10 +350,10 @@ function createParams() {
return params.join(', ');
}
function createArgs() {
function createArgs(recurmax, stmtDepth, canThrow) {
var args = [];
for (var n = rng(4); --n >= 0;) {
args.push(createValue());
args.push(rng(2) ? createValue() : createExpression(recurmax - 1, COMMA_OK, stmtDepth, canThrow));
}
return args.join(', ');
}
@@ -390,9 +391,10 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
VAR_NAMES.length = namesLenBefore;
if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s + '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ');';
if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s;
// avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name;
s += '(' + createArgs(recurmax, stmtDepth, canThrow) + ');';
return s;
}
@@ -626,6 +628,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
case p++:
return createValue();
case p++:
case p++:
return getVarName();
case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++:

View File

@@ -16,11 +16,9 @@
{},
{
"compress": {
"toplevel": true
"hoist_props": true
},
"mangle": {
"toplevel": true
}
"toplevel": true
},
{
"compress": {