Compare commits

...

34 Commits

Author SHA1 Message Date
Alex Lam S.L
f83adcc995 v3.3.24 2018-05-07 20:17:34 +00:00
Alex Lam S.L
df8a99439a fix various corner cases (#3126)
- augment ufuzz/reminify test options

fixes #3125
2018-05-07 07:36:25 +08:00
Alex Lam S.L
6b91d12ec3 fix corner case in reduce_vars (#3124) 2018-05-06 16:42:35 +08:00
Alex Lam S.L
f37b91879f fix various corner cases (#3123) 2018-05-05 13:17:50 +08:00
Alex Lam S.L
d835c72c80 speed up collapse_vars (#3119) 2018-05-04 18:38:13 +08:00
Alex Lam S.L
c4cebb4b01 fix reduce_vars on nested invocations (#3118) 2018-05-04 06:05:38 +08:00
Alex Lam S.L
d51a00a450 compress AST_Sequence within AST_Call (#3117) 2018-05-03 19:14:56 +08:00
Alex Lam S.L
fc0f168a0c better fix for #3113 (#3115) 2018-05-03 15:51:51 +08:00
Alex Lam S.L
a0ca595c2c fix TreeWalker scan order (#3114)
fixes #3113
2018-05-03 00:27:45 +08:00
Alex Lam S.L
1a314e9f60 improve reduce_vars (#3112)
fixes #3110
2018-05-02 15:11:45 +08:00
Alex Lam S.L
6fcbd5e217 v3.3.23 2018-04-28 17:14:52 +00:00
Alex Lam S.L
22cea023d1 improve numeral compression (#3108) 2018-04-28 02:47:49 +08:00
Alex Lam S.L
70d4477e05 workaround vm context issue in node-chakracore (#3106) 2018-04-27 07:40:34 +08:00
Alex Lam S.L
838f837379 improve general performance (#3104) 2018-04-27 04:30:29 +08:00
Alex Lam S.L
82a8b6f612 improve collapse_vars (#3103) 2018-04-26 19:26:01 +08:00
Alex Lam S.L
69fc7ca8da workaround test failures in Node.js 10 (#3102) 2018-04-26 17:44:37 +08:00
Alex Lam S.L
0a79496e0a workaround stack overflow in ChakraCore (#3101) 2018-04-26 15:02:17 +08:00
Alex Lam S.L
9e87edfc2e better fix for #2506 (#3099) 2018-04-25 04:46:07 +08:00
Alex Lam S.L
27211cf2d5 handle RHS side-effects in collapse_vars (#3097)
fixes #3096
2018-04-24 20:31:50 +08:00
Alex Lam S.L
b5ce199711 improve max_line_len (#3095)
fixes #304
2018-04-24 15:19:45 +08:00
Alex Lam S.L
c71ed91e63 update AST documentation (#3094)
fixes #2622
2018-04-24 14:39:12 +08:00
alexlamsl
f7545d0f1c remove unsupported platform 2018-04-24 13:32:54 +08:00
Alex Lam S.L
59eecb6bf5 v3.3.22 2018-04-20 19:50:16 +00:00
Alex Lam S.L
d83c6490ab fix corner case in strip_func_ids() (#3090) 2018-04-19 04:51:42 +08:00
Alex Lam S.L
7362f57966 improve performance when handling unused variables in collapse_vars (#3084)
fixes #3082
2018-04-15 12:38:31 +08:00
Alex Lam S.L
eaa2c1f6af v3.3.21 2018-04-12 07:08:53 +00:00
Alex Lam S.L
6a916523d4 fix inline of catch-scoped variables (#3077)
fixes #3076
2018-04-11 15:44:43 +08:00
Alex Lam S.L
ba7069d52b suppress hoist_props for embedded assignments (#3074) 2018-04-11 05:19:16 +08:00
Alex Lam S.L
4dd7d0e39b extend hoist_props (#3073)
- handle `AST_Assign` the same way as `AST_VarDef`
- inject `AST_Var` as succeeding statement

fixes #3071
2018-04-11 02:48:15 +08:00
Alex Lam S.L
90199d0a96 extend join_vars on object assignments (#3072) 2018-04-11 01:35:42 +08:00
Alex Lam S.L
b82fd0ad41 handle flow control in loops with reduce_vars (#3069)
fixes #3068
2018-04-10 06:51:03 +08:00
Alex Lam S.L
183da16896 handle pure_funcs under inline & reduce_vars correctly (#3066)
fixes #3065
2018-04-10 02:46:38 +08:00
Alex Lam S.L
87857b0f1b v3.3.20 2018-04-08 03:06:15 +08:00
Alex Lam S.L
e5f6a88233 fix corner case in reuse of mangle options (#3062) 2018-04-08 02:29:37 +08:00
24 changed files with 1537 additions and 408 deletions

View File

@@ -1,11 +1,7 @@
**Bug report or feature request?** **Bug report or feature request?**
<!-- Note: sub-optimal but correct code is not a bug --> <!-- Note: sub-optimal but correct code is not a bug -->
**ES5 or ES6+ input?**
<!-- Note: for ES6 see: https://github.com/mishoo/UglifyJS2/tree/harmony#harmony -->
**Uglify version (`uglifyjs -V`)** **Uglify version (`uglifyjs -V`)**
**JavaScript input** **JavaScript input**
@@ -24,6 +20,6 @@
**JavaScript output or error produced.** **JavaScript output or error produced.**
<!-- <!--
Note: `uglify-js` only supports ES5. Note: `uglify-js` only supports JavaScript.
Those wishing to minify ES6 should use `uglify-es`. Those wishing to minify ES6+ should transpile first.
--> -->

View File

@@ -685,7 +685,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
pass `pure_funcs: [ 'Math.floor' ]` to let it know that this pass `pure_funcs: [ 'Math.floor' ]` to let it know that this
function won't produce any side effect, in which case the whole function won't produce any side effect, in which case the whole
statement would get discarded. The current implementation adds some statement would get discarded. The current implementation adds some
overhead (compression will be slower). overhead (compression will be slower). Make sure symbols under `pure_funcs`
are also under `mangle.reserved` to avoid mangling.
- `pure_getters` (default: `"strict"`) -- If you pass `true` for - `pure_getters` (default: `"strict"`) -- If you pass `true` for
this, UglifyJS will assume that object property access this, UglifyJS will assume that object property access
@@ -1040,8 +1041,9 @@ var result = UglifyJS.minify(ast, {
### Working with Uglify AST ### Working with Uglify AST
Transversal and transformation of the native AST can be performed through Transversal and transformation of the native AST can be performed through
[`TreeWalker`](http://lisperator.net/uglifyjs/walk) and [`TreeWalker`](https://github.com/mishoo/UglifyJS2/blob/master/lib/ast.js) and
[`TreeTransformer`](http://lisperator.net/uglifyjs/transform) respectively. [`TreeTransformer`](https://github.com/mishoo/UglifyJS2/blob/master/lib/transform.js)
respectively.
### ESTree / SpiderMonkey AST ### ESTree / SpiderMonkey AST

View File

@@ -543,12 +543,11 @@ var AST_Call = DEFNODE("Call", "expression args", {
args: "[AST_Node*] array of arguments" args: "[AST_Node*] array of arguments"
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function() {
var args = this.args;
for (var i = 0, len = args.length; i < len; i++) {
args[i]._walk(visitor);
}
this.expression._walk(visitor); this.expression._walk(visitor);
this.args.forEach(function(node) {
node._walk(visitor);
});
}); });
} }
}); });

View File

@@ -295,10 +295,55 @@ merge(Compressor.prototype, {
self.transform(tt); self.transform(tt);
}); });
(function(def){ function read_property(obj, key) {
key = get_value(key);
if (key instanceof AST_Node) return;
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) {
key = "" + key;
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(compressor, tw, node, value, level, immutable) {
var parent = tw.parent(level);
var lhs = is_lhs(node, parent);
if (lhs) return lhs;
if (!immutable
&& parent instanceof AST_Call
&& parent.expression === node
&& !parent.is_expr_pure(compressor)
&& (!(value instanceof AST_Function)
|| !(parent instanceof AST_New) && value.contains_this())) {
return true;
}
if (parent instanceof AST_Array) {
return is_modified(compressor, tw, parent, parent, level + 1);
}
if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1);
return is_modified(compressor, tw, obj, obj, level + 2);
}
if (parent instanceof AST_PropAccess && parent.expression === node) {
var prop = read_property(value, parent.property);
return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
}
}
(function(def) {
def(AST_Node, noop); def(AST_Node, noop);
function reset_def(compressor, def) { function reset_def(tw, compressor, def) {
def.assignments = 0; def.assignments = 0;
def.chained = false; def.chained = false;
def.direct_access = false; def.direct_access = false;
@@ -310,15 +355,23 @@ merge(Compressor.prototype, {
} else { } else {
def.fixed = false; def.fixed = false;
} }
if (def.init instanceof AST_Defun && !all(def.references, function(ref) {
var scope = ref.scope;
do {
if (def.scope === scope) return true;
} while (scope instanceof AST_Function && (scope = scope.parent_scope));
})) {
tw.defun_ids[def.id] = undefined;
}
def.recursive_refs = 0; def.recursive_refs = 0;
def.references = []; def.references = [];
def.should_replace = undefined; def.should_replace = undefined;
def.single_use = undefined; def.single_use = undefined;
} }
function reset_variables(tw, compressor, node) { function reset_variables(tw, compressor, scope) {
node.variables.each(function(def) { scope.variables.each(function(def) {
reset_def(compressor, def); reset_def(tw, compressor, def);
if (def.fixed === null) { if (def.fixed === null) {
def.safe_ids = tw.safe_ids; def.safe_ids = tw.safe_ids;
mark(tw, def, true); mark(tw, def, true);
@@ -329,6 +382,33 @@ merge(Compressor.prototype, {
}); });
} }
function mark_defun(tw, def) {
if (def.id in tw.defun_ids) {
var marker = tw.defun_ids[def.id];
if (!marker) return;
if (marker !== tw.safe_ids) {
tw.defun_ids[def.id] = undefined;
return;
}
return def.fixed;
}
if (!tw.in_loop) {
tw.defun_ids[def.id] = tw.safe_ids;
return def.fixed;
} else if (tw.defun_ids[def.id] !== false) {
tw.defun_ids[def.id] = undefined;
}
}
function walk_defuns(tw, scope) {
scope.functions.each(function(def) {
if (def.init instanceof AST_Defun && tw.defun_ids[def.id] === undefined) {
tw.defun_ids[def.id] = tw.safe_ids;
def.init.walk(tw);
}
});
}
function push(tw) { function push(tw) {
tw.safe_ids = Object.create(tw.safe_ids); tw.safe_ids = Object.create(tw.safe_ids);
} }
@@ -353,7 +433,7 @@ merge(Compressor.prototype, {
return def.fixed instanceof AST_Defun; return def.fixed instanceof AST_Defun;
} }
function safe_to_assign(tw, def, value) { function safe_to_assign(tw, def, scope, value) {
if (def.fixed === undefined) return true; if (def.fixed === undefined) return true;
if (def.fixed === null && def.safe_ids) { if (def.fixed === null && def.safe_ids) {
def.safe_ids[def.id] = false; def.safe_ids[def.id] = false;
@@ -364,6 +444,9 @@ merge(Compressor.prototype, {
if (!safe_to_read(tw, def)) return false; if (!safe_to_read(tw, def)) return false;
if (def.fixed === false) return false; if (def.fixed === false) return false;
if (def.fixed != null && (!value || def.references.length > def.assignments)) return false; if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
if (def.fixed instanceof AST_Defun) {
return value instanceof AST_Node && def.fixed.parent_scope === scope;
}
return all(def.orig, function(sym) { return all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolDefun return !(sym instanceof AST_SymbolDefun
|| sym instanceof AST_SymbolLambda); || sym instanceof AST_SymbolLambda);
@@ -385,45 +468,6 @@ merge(Compressor.prototype, {
|| value instanceof AST_This; || value instanceof AST_This;
} }
function read_property(obj, key) {
key = get_value(key);
if (key instanceof AST_Node) return;
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) {
key = "" + key;
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(tw, node, value, level, immutable) {
var parent = tw.parent(level);
if (is_lhs(node, parent)
|| !immutable
&& parent instanceof AST_Call
&& parent.expression === node
&& (!(value instanceof AST_Function)
|| !(parent instanceof AST_New) && value.contains_this())) {
return true;
} else if (parent instanceof AST_Array) {
return is_modified(tw, parent, parent, level + 1);
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1);
return is_modified(tw, obj, obj, level + 2);
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
return !immutable && is_modified(tw, parent, read_property(value, parent.property), level + 1);
}
}
function mark_escaped(tw, d, scope, node, value, level, depth) { function mark_escaped(tw, d, scope, node, value, level, depth) {
var parent = tw.parent(level); var parent = tw.parent(level);
if (value && value.is_constant()) return; if (value && value.is_constant()) return;
@@ -447,7 +491,10 @@ merge(Compressor.prototype, {
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1); mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
if (value) return; if (value) return;
} }
if (level == 0) d.direct_access = true; if (level > 0) return;
if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
if (parent instanceof AST_SimpleStatement) return;
d.direct_access = true;
} }
var suppressor = new TreeWalker(function(node) { var suppressor = new TreeWalker(function(node) {
@@ -462,19 +509,24 @@ merge(Compressor.prototype, {
reset_variables(tw, compressor, this); reset_variables(tw, compressor, this);
descend(); descend();
pop(tw); pop(tw);
walk_defuns(tw, this);
return true; return true;
}); });
def(AST_Assign, function(tw) { def(AST_Assign, function(tw, descend, compressor) {
var node = this; var node = this;
if (!(node.left instanceof AST_SymbolRef)) return; var sym = node.left;
var d = node.left.definition(); if (!(sym instanceof AST_SymbolRef)) return;
var d = sym.definition();
var fixed = d.fixed; var fixed = d.fixed;
if (!fixed && node.operator != "=") return; if (!fixed && node.operator != "=") return;
if (!safe_to_assign(tw, d, node.right)) return; if (!safe_to_assign(tw, d, sym.scope, node.right)) return;
d.references.push(node.left); var eq = node.operator == "=";
var value = eq ? node.right : node;
if (is_modified(compressor, tw, node, value, 0)) return;
d.references.push(sym);
d.assignments++; d.assignments++;
if (node.operator != "=") d.chained = true; if (!eq) d.chained = true;
d.fixed = node.operator == "=" ? function() { d.fixed = eq ? function() {
return node.right; return node.right;
} : function() { } : function() {
return make_node(AST_Binary, node, { return make_node(AST_Binary, node, {
@@ -486,6 +538,7 @@ merge(Compressor.prototype, {
mark(tw, d, false); mark(tw, d, false);
node.right.walk(tw); node.right.walk(tw);
mark(tw, d, true); mark(tw, d, true);
mark_escaped(tw, d, sym.scope, node, value, 0, 1);
return true; return true;
}); });
def(AST_Binary, function(tw) { def(AST_Binary, function(tw) {
@@ -496,6 +549,17 @@ merge(Compressor.prototype, {
pop(tw); pop(tw);
return true; return true;
}); });
def(AST_Call, function(tw, descend) {
var exp = this.expression;
if (!(exp instanceof AST_SymbolRef)) return;
var def = exp.definition();
if (!(def.fixed instanceof AST_Defun)) return;
var defun = mark_defun(tw, def);
if (!defun) return;
descend();
defun.walk(tw);
return true;
});
def(AST_Case, function(tw) { def(AST_Case, function(tw) {
push(tw); push(tw);
this.expression.walk(tw); this.expression.walk(tw);
@@ -522,19 +586,27 @@ merge(Compressor.prototype, {
return true; return true;
}); });
def(AST_Defun, function(tw, descend, compressor) { def(AST_Defun, function(tw, descend, compressor) {
var id = this.name.definition().id;
if (tw.defun_ids[id] !== tw.safe_ids) return true;
tw.defun_ids[id] = false;
this.inlined = false; this.inlined = false;
var save_ids = tw.safe_ids; push(tw);
tw.safe_ids = Object.create(null);
reset_variables(tw, compressor, this); reset_variables(tw, compressor, this);
descend(); descend();
tw.safe_ids = save_ids; pop(tw);
walk_defuns(tw, this);
return true; return true;
}); });
def(AST_DWLoop, function(tw, descend) { def(AST_Do, function(tw) {
var saved_loop = tw.in_loop; var saved_loop = tw.in_loop;
tw.in_loop = this; tw.in_loop = this;
push(tw); push(tw);
descend(); this.body.walk(tw);
if (has_break_or_continue(this)) {
pop(tw);
push(tw);
}
this.condition.walk(tw);
pop(tw); pop(tw);
tw.in_loop = saved_loop; tw.in_loop = saved_loop;
return true; return true;
@@ -543,19 +615,17 @@ merge(Compressor.prototype, {
if (this.init) this.init.walk(tw); if (this.init) this.init.walk(tw);
var saved_loop = tw.in_loop; var saved_loop = tw.in_loop;
tw.in_loop = this; tw.in_loop = this;
if (this.condition) {
push(tw);
this.condition.walk(tw);
pop(tw);
}
push(tw); push(tw);
if (this.condition) this.condition.walk(tw);
this.body.walk(tw); this.body.walk(tw);
pop(tw);
if (this.step) { if (this.step) {
push(tw); if (has_break_or_continue(this)) {
pop(tw);
push(tw);
}
this.step.walk(tw); this.step.walk(tw);
pop(tw);
} }
pop(tw);
tw.in_loop = saved_loop; tw.in_loop = saved_loop;
return true; return true;
}); });
@@ -597,6 +667,7 @@ merge(Compressor.prototype, {
} }
descend(); descend();
pop(tw); pop(tw);
walk_defuns(tw, node);
return true; return true;
}); });
def(AST_If, function(tw) { def(AST_If, function(tw) {
@@ -641,7 +712,7 @@ merge(Compressor.prototype, {
} else { } else {
d.single_use = false; d.single_use = false;
} }
if (is_modified(tw, this, value, 0, is_immutable(value))) { if (is_modified(compressor, tw, this, value, 0, is_immutable(value))) {
if (d.single_use) { if (d.single_use) {
d.single_use = "m"; d.single_use = "m";
} else { } else {
@@ -650,12 +721,23 @@ merge(Compressor.prototype, {
} }
} }
mark_escaped(tw, d, this.scope, this, value, 0, 1); mark_escaped(tw, d, this.scope, this, value, 0, 1);
var parent;
if (d.fixed instanceof AST_Defun
&& !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
var defun = mark_defun(tw, d);
if (defun) defun.walk(tw);
}
}); });
def(AST_Toplevel, function(tw, descend, compressor) { def(AST_Toplevel, function(tw, descend, compressor) {
this.globals.each(function(def) { this.globals.each(function(def) {
reset_def(compressor, def); reset_def(tw, compressor, def);
}); });
push(tw);
reset_variables(tw, compressor, this); reset_variables(tw, compressor, this);
descend();
pop(tw);
walk_defuns(tw, this);
return true;
}); });
def(AST_Try, function(tw) { def(AST_Try, function(tw) {
push(tw); push(tw);
@@ -672,12 +754,13 @@ merge(Compressor.prototype, {
def(AST_Unary, function(tw, descend) { def(AST_Unary, function(tw, descend) {
var node = this; var node = this;
if (node.operator != "++" && node.operator != "--") return; if (node.operator != "++" && node.operator != "--") return;
if (!(node.expression instanceof AST_SymbolRef)) return; var exp = node.expression;
var d = node.expression.definition(); if (!(exp instanceof AST_SymbolRef)) return;
var d = exp.definition();
var fixed = d.fixed; var fixed = d.fixed;
if (!fixed) return; if (!fixed) return;
if (!safe_to_assign(tw, d, true)) return; if (!safe_to_assign(tw, d, exp.scope, true)) return;
d.references.push(node.expression); d.references.push(exp);
d.assignments++; d.assignments++;
d.chained = true; d.chained = true;
d.fixed = function() { d.fixed = function() {
@@ -699,7 +782,7 @@ merge(Compressor.prototype, {
var node = this; var node = this;
var d = node.name.definition(); var d = node.name.definition();
if (node.value) { if (node.value) {
if (safe_to_assign(tw, d, node.value)) { if (safe_to_assign(tw, d, node.name.scope, node.value)) {
d.fixed = function() { d.fixed = function() {
return node.value; return node.value;
}; };
@@ -713,24 +796,38 @@ merge(Compressor.prototype, {
} }
} }
}); });
def(AST_While, function(tw, descend) {
var saved_loop = tw.in_loop;
tw.in_loop = this;
push(tw);
descend();
pop(tw);
tw.in_loop = saved_loop;
return true;
});
})(function(node, func){ })(function(node, func){
node.DEFMETHOD("reduce_vars", func); node.DEFMETHOD("reduce_vars", func);
}); });
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) { AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
var reduce_vars = compressor.option("reduce_vars"); var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
var tw = new TreeWalker(function(node, descend) { node._squeezed = false;
node._optimized = false;
return node.reduce_vars(tw, descend, compressor);
} : function(node) {
node._squeezed = false; node._squeezed = false;
node._optimized = false; node._optimized = false;
if (reduce_vars) return node.reduce_vars(tw, descend, compressor);
}); });
// Flow control for visiting `AST_Defun`s
tw.defun_ids = Object.create(null);
// Record the loop body in which `AST_SymbolDeclaration` is first encountered
tw.in_loop = null;
tw.loop_ids = Object.create(null);
// Stack of look-up tables to keep track of whether a `SymbolDef` has been // Stack of look-up tables to keep track of whether a `SymbolDef` has been
// properly assigned before use: // properly assigned before use:
// - `push()` & `pop()` when visiting conditional branches // - `push()` & `pop()` when visiting conditional branches
// - backup & restore via `save_ids` when visiting out-of-order sections // - backup & restore via `save_ids` when visiting out-of-order sections
tw.safe_ids = Object.create(null); tw.safe_ids = Object.create(null);
tw.in_loop = null;
tw.loop_ids = Object.create(null);
this.walk(tw); this.walk(tw);
}); });
@@ -829,7 +926,7 @@ merge(Compressor.prototype, {
// the func (becomes lexical instead of global). // the func (becomes lexical instead of global).
function maintain_this_binding(parent, orig, val) { function maintain_this_binding(parent, orig, val) {
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete" if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|| parent instanceof AST_Call && parent.expression === orig || parent.TYPE == "Call" && parent.expression === orig
&& (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) { && (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]); return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
} }
@@ -962,24 +1059,12 @@ merge(Compressor.prototype, {
} }
// Stop immediately if these node types are encountered // Stop immediately if these node types are encountered
var parent = scanner.parent(); var parent = scanner.parent();
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left) if (should_stop(node, parent)) {
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|| node instanceof AST_Debugger
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
|| node instanceof AST_LoopControl
|| node instanceof AST_Try
|| node instanceof AST_With
|| parent instanceof AST_For && node !== parent.init
|| !replace_all
&& (node instanceof AST_SymbolRef && !node.is_declared(compressor))) {
abort = true; abort = true;
return node; return node;
} }
// Stop only if candidate is found within conditional branches // Stop only if candidate is found within conditional branches
if (!stop_if_hit if (!stop_if_hit && in_conditional(node, parent)) {
&& (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; stop_if_hit = parent;
} }
// Replace variable with assignment when found // Replace variable with assignment when found
@@ -995,9 +1080,11 @@ merge(Compressor.prototype, {
if (is_lhs(node, parent)) { if (is_lhs(node, parent)) {
if (value_def) replaced++; if (value_def) replaced++;
return node; return node;
} else {
replaced++;
if (value_def && candidate instanceof AST_VarDef) return node;
} }
CHANGED = abort = true; CHANGED = abort = true;
replaced++;
compressor.info("Collapsing {name} [{file}:{line},{col}]", { compressor.info("Collapsing {name} [{file}:{line},{col}]", {
name: node.print_to_string(), name: node.print_to_string(),
file: node.start.file, file: node.start.file,
@@ -1008,24 +1095,15 @@ merge(Compressor.prototype, {
return make_node(AST_UnaryPrefix, candidate, candidate); return make_node(AST_UnaryPrefix, candidate, candidate);
} }
if (candidate instanceof AST_VarDef) { if (candidate instanceof AST_VarDef) {
if (value_def) {
abort = false;
return node;
}
var def = candidate.name.definition(); var def = candidate.name.definition();
var value = candidate.value;
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++; def.replaced++;
if (funarg && is_identifier_atom(value)) { return maintain_this_binding(parent, node, candidate.value);
return value.transform(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
} }
return make_node(AST_Assign, candidate, { return make_node(AST_Assign, candidate, {
operator: "=", operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name), left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: value right: candidate.value
}); });
} }
candidate.write_only = false; candidate.write_only = false;
@@ -1034,20 +1112,7 @@ merge(Compressor.prototype, {
// These node types have child nodes that execute sequentially, // These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them. // but are otherwise not safe to scan into or beyond them.
var sym; var sym;
if (node instanceof AST_Call if (is_last_node(node, parent) || may_throw(node)) {
|| node instanceof AST_Exit
&& (side_effects || lhs instanceof AST_PropAccess || may_modify(lhs))
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (symbol_in_lvalues(node) || side_effects && may_modify(node))
|| node instanceof AST_This && symbol_in_lvalues(node)
|| node instanceof AST_VarDef && node.value
&& (node.name.name in lvalues || side_effects && may_modify(node.name))
|| (sym = is_lhs(node.left, node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| may_throw
&& (in_try ? node.has_side_effects(compressor) : side_effects_external(node))) {
stop_after = node; stop_after = node;
if (node instanceof AST_Scope) abort = true; if (node instanceof AST_Scope) abort = true;
} }
@@ -1074,7 +1139,7 @@ merge(Compressor.prototype, {
if (is_lhs(node, multi_replacer.parent())) return node; if (is_lhs(node, multi_replacer.parent())) return node;
def.replaced++; def.replaced++;
value_def.replaced--; value_def.replaced--;
return candidate.value; return candidate.value.clone();
} }
// Skip (non-executed) functions and (leading) default case in switch statements // Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node; if (node instanceof AST_Default || node instanceof AST_Scope) return node;
@@ -1106,7 +1171,9 @@ merge(Compressor.prototype, {
var lhs_local = is_lhs_local(lhs); var lhs_local = is_lhs_local(lhs);
if (!side_effects) side_effects = value_has_side_effects(candidate); if (!side_effects) side_effects = value_has_side_effects(candidate);
var replace_all = replace_all_symbols(); var replace_all = replace_all_symbols();
var may_throw = candidate.may_throw(compressor); var may_throw = candidate.may_throw(compressor) ? in_try ? function(node) {
return node.has_side_effects(compressor);
} : side_effects_external : return_false;
var funarg = candidate.name instanceof AST_SymbolFunarg; var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg; var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit; var abort = false, replaced = 0, can_replace = !args || !hit;
@@ -1158,6 +1225,51 @@ merge(Compressor.prototype, {
} }
} }
function should_stop(node, parent) {
if (node instanceof AST_Assign) return node.operator != "=" && lhs.equivalent_to(node.left);
if (node instanceof AST_Call) {
return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression);
}
if (node instanceof AST_Debugger) return true;
if (node instanceof AST_IterationStatement) return !(node instanceof AST_For);
if (node instanceof AST_LoopControl) return true;
if (node instanceof AST_Try) return true;
if (node instanceof AST_With) return true;
if (parent instanceof AST_For) return node !== parent.init;
if (replace_all) return false;
return node instanceof AST_SymbolRef && !node.is_declared(compressor);
}
function in_conditional(node, parent) {
if (parent instanceof AST_Binary) return lazy_op[parent.operator] && parent.left !== node;
if (parent instanceof AST_Conditional) return parent.condition !== node;
return parent instanceof AST_If && parent.condition !== node;
}
function is_last_node(node, parent) {
if (node instanceof AST_Call) return true;
if (node instanceof AST_Exit) {
return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
}
if (node instanceof AST_PropAccess) {
return side_effects || node.expression.may_throw_on_access(compressor);
}
if (node instanceof AST_SymbolRef) {
if (symbol_in_lvalues(node, parent)) {
return !parent || parent.operator != "=" || parent.left !== node;
}
return side_effects && may_modify(node);
}
if (node instanceof AST_This) return symbol_in_lvalues(node, parent);
if (node instanceof AST_VarDef) {
if (!node.value) return false;
return node.name.name in lvalues || side_effects && may_modify(node.name);
}
var sym = is_lhs(node.left, node);
if (sym && sym.name in lvalues) return true;
if (sym instanceof AST_PropAccess) return true;
}
function extract_args() { function extract_args() {
var iife, fn = compressor.self(); var iife, fn = compressor.self();
if (fn instanceof AST_Function if (fn instanceof AST_Function
@@ -1336,8 +1448,9 @@ merge(Compressor.prototype, {
if (expr instanceof AST_VarDef) { if (expr instanceof AST_VarDef) {
var def = expr.name.definition(); var def = expr.name.definition();
if (!member(expr.name, def.orig)) return; if (!member(expr.name, def.orig)) return;
var declared = def.orig.length - def.eliminated;
var referenced = def.references.length - def.replaced; var referenced = def.references.length - def.replaced;
if (!referenced) return;
var declared = def.orig.length - def.eliminated;
if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg) if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) { || (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name); return make_node(AST_SymbolRef, expr.name, expr.name);
@@ -1406,7 +1519,7 @@ merge(Compressor.prototype, {
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
var sym = root_expr(node); var sym = root_expr(node);
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) { if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent()); lvalues[sym.name] = lvalues[sym.name] || is_modified(compressor, tw, node, node, 0);
} }
}); });
expr.walk(tw); expr.walk(tw);
@@ -1468,10 +1581,10 @@ merge(Compressor.prototype, {
return false; return false;
} }
function symbol_in_lvalues(sym) { function symbol_in_lvalues(sym, parent) {
var lvalue = lvalues[sym.name]; var lvalue = lvalues[sym.name];
if (!lvalue) return; if (!lvalue) return;
if (lvalue !== lhs) return true; if (lvalue !== lhs) return !(parent instanceof AST_Call);
scan_rhs = false; scan_rhs = false;
} }
@@ -1870,9 +1983,6 @@ merge(Compressor.prototype, {
} }
function join_object_assignments(defn, body) { function join_object_assignments(defn, body) {
if (!(defn instanceof AST_Definitions)) return;
var def = defn.definitions[defn.definitions.length - 1];
if (!(def.value instanceof AST_Object)) return;
var exprs; var exprs;
if (body instanceof AST_Assign) { if (body instanceof AST_Assign) {
exprs = [ body ]; exprs = [ body ];
@@ -1880,6 +1990,23 @@ merge(Compressor.prototype, {
exprs = body.expressions.slice(); exprs = body.expressions.slice();
} }
if (!exprs) return; if (!exprs) return;
if (defn instanceof AST_Definitions) {
var def = defn.definitions[defn.definitions.length - 1];
if (trim_object_assignments(def.name, def.value, exprs)) return exprs;
}
for (var i = exprs.length - 1; --i >= 0;) {
var expr = exprs[i];
if (!(expr instanceof AST_Assign)) continue;
if (expr.operator != "=") continue;
if (!(expr.left instanceof AST_SymbolRef)) continue;
var tail = exprs.slice(i + 1);
if (!trim_object_assignments(expr.left, expr.right, tail)) continue;
return exprs.slice(0, i + 1).concat(tail);
}
}
function trim_object_assignments(name, value, exprs) {
if (!(value instanceof AST_Object)) return;
var trimmed = false; var trimmed = false;
do { do {
var node = exprs[0]; var node = exprs[0];
@@ -1888,7 +2015,7 @@ merge(Compressor.prototype, {
if (!(node.left instanceof AST_PropAccess)) break; if (!(node.left instanceof AST_PropAccess)) break;
var sym = node.left.expression; var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) break; if (!(sym instanceof AST_SymbolRef)) break;
if (def.name.name != sym.name) break; if (name.name != sym.name) break;
if (!node.right.is_constant_expression(scope)) break; if (!node.right.is_constant_expression(scope)) break;
var prop = node.left.property; var prop = node.left.property;
if (prop instanceof AST_Node) { if (prop instanceof AST_Node) {
@@ -1901,15 +2028,15 @@ merge(Compressor.prototype, {
} : function(node) { } : function(node) {
return node.key.name != prop; return node.key.name != prop;
}; };
if (!all(def.value.properties, diff)) break; if (!all(value.properties, diff)) break;
def.value.properties.push(make_node(AST_ObjectKeyVal, node, { value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop, key: prop,
value: node.right value: node.right
})); }));
exprs.shift(); exprs.shift();
trimmed = true; trimmed = true;
} while (exprs.length); } while (exprs.length);
return trimmed && exprs; return trimmed;
} }
function join_consecutive_vars(statements) { function join_consecutive_vars(statements) {
@@ -3613,28 +3740,50 @@ merge(Compressor.prototype, {
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false; var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
var defs_by_id = Object.create(null); var defs_by_id = Object.create(null);
return self.transform(new TreeTransformer(function(node, descend) { return self.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_VarDef) { if (node instanceof AST_Assign
var sym = node.name, def, value; && node.operator == "="
if (sym.scope === self && node.write_only
&& (def = sym.definition()).escaped != 1 && can_hoist(node.left, node.right, 1)) {
&& !def.assignments descend(node, this);
&& !def.direct_access var defs = new Dictionary();
&& !def.single_use var assignments = [];
&& !top_retain(def) var decls = [];
&& (value = sym.fixed_value()) === node.value node.right.properties.forEach(function(prop) {
&& value instanceof AST_Object) { var decl = make_sym(node.left, prop.key);
descend(node, this); decls.push(make_node(AST_VarDef, node, {
var defs = new Dictionary(); name: decl,
var assignments = []; value: null
value.properties.forEach(function(prop) { }));
assignments.push(make_node(AST_VarDef, node, { var sym = make_node(AST_SymbolRef, node, {
name: make_sym(prop.key), name: decl.name,
value: prop.value scope: self,
})); thedef: decl.definition()
}); });
defs_by_id[def.id] = defs; sym.reference({});
return MAP.splice(assignments); assignments.push(make_node(AST_Assign, node, {
} operator: "=",
left: sym,
right: prop.value
}));
});
defs_by_id[node.left.definition().id] = defs;
self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
definitions: decls
}));
return make_sequence(node, assignments);
}
if (node instanceof AST_VarDef && can_hoist(node.name, node.value, 0)) {
descend(node, this);
var defs = new Dictionary();
var var_defs = [];
node.value.properties.forEach(function(prop) {
var_defs.push(make_node(AST_VarDef, node, {
name: make_sym(node.name, prop.key),
value: prop.value
}));
});
defs_by_id[node.name.definition().id] = defs;
return MAP.splice(var_defs);
} }
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) { if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id]; var defs = defs_by_id[node.expression.definition().id];
@@ -3650,8 +3799,20 @@ merge(Compressor.prototype, {
} }
} }
function make_sym(key) { function can_hoist(sym, right, count) {
var new_var = make_node(sym.CTOR, sym, { if (sym.scope !== self) return;
var def = sym.definition();
if (def.assignments != count) return;
if (def.direct_access) return;
if (def.escaped == 1) return;
if (def.single_use) return;
if (top_retain(def)) return;
if (sym.fixed_value() !== right) return;
return right instanceof AST_Object;
}
function make_sym(sym, key) {
var new_var = make_node(AST_SymbolVar, sym, {
name: self.make_var_name(sym.name + "_" + key), name: self.make_var_name(sym.name + "_" + key),
scope: self scope: self
}); });
@@ -3837,6 +3998,20 @@ merge(Compressor.prototype, {
return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self; return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
}); });
function has_break_or_continue(loop, parent) {
var found = false;
var tw = new TreeWalker(function(node) {
if (found || node instanceof AST_Scope) return true;
if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) {
return found = true;
}
});
if (parent instanceof AST_LabeledStatement) tw.push(parent);
tw.push(loop);
loop.body.walk(tw);
return found;
}
OPT(AST_Do, function(self, compressor){ OPT(AST_Do, function(self, compressor){
if (!compressor.option("loops")) return self; if (!compressor.option("loops")) return self;
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor); var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
@@ -3851,22 +4026,16 @@ merge(Compressor.prototype, {
] ]
}) })
}).optimize(compressor); }).optimize(compressor);
var has_loop_control = false; if (!has_break_or_continue(self, compressor.parent())) {
var tw = new TreeWalker(function(node) { return make_node(AST_BlockStatement, self.body, {
if (node instanceof AST_Scope || has_loop_control) return true; body: [
if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === self) self.body,
return has_loop_control = true; make_node(AST_SimpleStatement, self.condition, {
}); body: self.condition
var parent = compressor.parent(); })
(parent instanceof AST_LabeledStatement ? parent : self).walk(tw); ]
if (!has_loop_control) return make_node(AST_BlockStatement, self.body, { }).optimize(compressor);
body: [ }
self.body,
make_node(AST_SimpleStatement, self.condition, {
body: self.condition
})
]
}).optimize(compressor);
} }
if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, { if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
condition: make_sequence(self.condition, [ condition: make_sequence(self.condition, [
@@ -4303,7 +4472,24 @@ merge(Compressor.prototype, {
return self; return self;
}); });
OPT(AST_Call, function(self, compressor){ AST_Call.DEFMETHOD("lift_sequences", function(compressor) {
if (!compressor.option("sequences")) return this;
var exp = this.expression;
if (!(exp instanceof AST_Sequence)) return this;
var tail = exp.tail_node();
if (tail instanceof AST_PropAccess && !(this instanceof AST_New)) return this;
var expressions = exp.expressions.slice(0, -1);
var node = this.clone();
node.expression = tail;
expressions.push(node);
return make_sequence(this, expressions).optimize(compressor);
});
OPT(AST_Call, function(self, compressor) {
var seq = self.lift_sequences(compressor);
if (seq !== self) {
return seq;
}
var exp = self.expression; var exp = self.expression;
var fn = exp; var fn = exp;
if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) { if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
@@ -4504,13 +4690,16 @@ merge(Compressor.prototype, {
func = func.fixed_value(); func = func.fixed_value();
} }
if (func instanceof AST_Lambda && !func.contains_this()) { if (func instanceof AST_Lambda && !func.contains_this()) {
return make_sequence(this, [ return (self.args.length ? make_sequence(this, [
self.args[0], self.args[0],
make_node(AST_Call, self, { make_node(AST_Call, self, {
expression: exp.expression, expression: exp.expression,
args: self.args.slice(1) args: self.args.slice(1)
}) })
]).optimize(compressor); ]) : make_node(AST_Call, self, {
expression: exp.expression,
args: []
})).optimize(compressor);
} }
break; break;
} }
@@ -4573,7 +4762,8 @@ merge(Compressor.prototype, {
} }
} }
var stat = is_func && fn.body[0]; var stat = is_func && fn.body[0];
if (compressor.option("inline") && stat instanceof AST_Return) { var can_inline = compressor.option("inline") && !self.is_expr_pure(compressor);
if (can_inline && stat instanceof AST_Return) {
var value = stat.value; var value = stat.value;
if (!value || value.is_constant_expression()) { if (!value || value.is_constant_expression()) {
if (value) { if (value) {
@@ -4587,7 +4777,7 @@ merge(Compressor.prototype, {
} }
if (is_func) { if (is_func) {
var def, value, scope, in_loop, level = -1; var def, value, scope, in_loop, level = -1;
if (compressor.option("inline") if (can_inline
&& !fn.uses_arguments && !fn.uses_arguments
&& !fn.uses_eval && !fn.uses_eval
&& !(fn.name && fn instanceof AST_Function) && !(fn.name && fn instanceof AST_Function)
@@ -4772,6 +4962,11 @@ merge(Compressor.prototype, {
for (var j = 0, defs = stat.definitions.length; j < defs; j++) { for (var j = 0, defs = stat.definitions.length; j < defs; j++) {
var var_def = stat.definitions[j]; var var_def = stat.definitions[j];
var name = var_def.name; var name = var_def.name;
var redef = name.definition().redefined();
if (redef) {
name = name.clone();
name.thedef = redef;
}
append_var(decls, expressions, name, var_def.value); append_var(decls, expressions, name, var_def.value);
if (in_loop && all(fn.argnames, function(argname) { if (in_loop && all(fn.argnames, function(argname) {
return argname.name != name.name; return argname.name != name.name;
@@ -4805,7 +5000,11 @@ merge(Compressor.prototype, {
} }
}); });
OPT(AST_New, function(self, compressor){ OPT(AST_New, function(self, compressor) {
var seq = self.lift_sequences(compressor);
if (seq !== self) {
return seq;
}
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
var exp = self.expression; var exp = self.expression;
if (is_undeclared_ref(exp)) { if (is_undeclared_ref(exp)) {
@@ -4861,14 +5060,12 @@ merge(Compressor.prototype, {
}); });
AST_Unary.DEFMETHOD("lift_sequences", function(compressor){ AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
if (compressor.option("sequences")) { if (compressor.option("sequences") && this.expression instanceof AST_Sequence) {
if (this.expression instanceof AST_Sequence) { var x = this.expression.expressions.slice();
var x = this.expression.expressions.slice(); var e = this.clone();
var e = this.clone(); e.expression = x.pop();
e.expression = x.pop(); x.push(e);
x.push(e); return make_sequence(this, x).optimize(compressor);
return make_sequence(this, x).optimize(compressor);
}
} }
return this; return this;
}); });
@@ -5460,11 +5657,12 @@ merge(Compressor.prototype, {
return make_node(AST_Infinity, self).optimize(compressor); return make_node(AST_Infinity, self).optimize(compressor);
} }
} }
if (compressor.option("reduce_vars") var parent = compressor.parent();
&& is_lhs(self, compressor.parent()) !== self) { if (compressor.option("reduce_vars") && is_lhs(self, parent) !== self) {
var d = self.definition(); var d = self.definition();
var fixed = self.fixed_value(); var fixed = self.fixed_value();
var single_use = d.single_use; var single_use = d.single_use
&& !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
if (single_use && fixed instanceof AST_Lambda) { if (single_use && fixed instanceof AST_Lambda) {
if (d.scope !== self.scope if (d.scope !== self.scope
&& (!compressor.option("reduce_funcs") && (!compressor.option("reduce_funcs")

View File

@@ -173,7 +173,7 @@ function OutputStream(options) {
default: default:
return dq > sq ? quote_single() : quote_double(); return dq > sq ? quote_single() : quote_double();
} }
}; }
function encode_string(str, quote) { function encode_string(str, quote) {
var ret = make_string(str, quote); var ret = make_string(str, quote);
@@ -183,17 +183,17 @@ function OutputStream(options) {
ret = ret.replace(/--\x3e/g, "--\\x3e"); ret = ret.replace(/--\x3e/g, "--\\x3e");
} }
return ret; return ret;
}; }
function make_name(name) { function make_name(name) {
name = name.toString(); name = name.toString();
name = to_utf8(name, true); name = to_utf8(name, true);
return name; return name;
}; }
function make_indent(back) { function make_indent(back) {
return repeat_string(" ", options.indent_start + indentation - back * options.indent_level); return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
}; }
/* -----[ beautification/minification ]----- */ /* -----[ beautification/minification ]----- */
@@ -351,7 +351,7 @@ function OutputStream(options) {
current_col = a[n].length; current_col = a[n].length;
} }
last = str; last = str;
}; }
var space = options.beautify ? function() { var space = options.beautify ? function() {
print(" "); print(" ");
@@ -374,6 +374,11 @@ function OutputStream(options) {
return ret; return ret;
} : function(col, cont) { return cont() }; } : function(col, cont) { return cont() };
var may_add_newline = options.max_line_len ? function() {
ensure_line_len();
might_add_newline = OUTPUT.length;
} : noop;
var newline = options.beautify ? function() { var newline = options.beautify ? function() {
if (newline_insert < 0) return print("\n"); if (newline_insert < 0) return print("\n");
if (OUTPUT[newline_insert] != "\n") { if (OUTPUT[newline_insert] != "\n") {
@@ -382,10 +387,7 @@ function OutputStream(options) {
current_line++; current_line++;
} }
newline_insert++; newline_insert++;
} : options.max_line_len ? function() { } : may_add_newline;
ensure_line_len();
might_add_newline = OUTPUT.length;
} : noop;
var semicolon = options.beautify ? function() { var semicolon = options.beautify ? function() {
print(";"); print(";");
@@ -396,11 +398,11 @@ function OutputStream(options) {
function force_semicolon() { function force_semicolon() {
might_need_semicolon = false; might_need_semicolon = false;
print(";"); print(";");
}; }
function next_indent() { function next_indent() {
return indentation + options.indent_level; return indentation + options.indent_level;
}; }
function with_block(cont) { function with_block(cont) {
var ret; var ret;
@@ -412,34 +414,40 @@ function OutputStream(options) {
indent(); indent();
print("}"); print("}");
return ret; return ret;
}; }
function with_parens(cont) { function with_parens(cont) {
print("("); print("(");
may_add_newline();
//XXX: still nice to have that for argument lists //XXX: still nice to have that for argument lists
//var ret = with_indent(current_col, cont); //var ret = with_indent(current_col, cont);
var ret = cont(); var ret = cont();
may_add_newline();
print(")"); print(")");
return ret; return ret;
}; }
function with_square(cont) { function with_square(cont) {
print("["); print("[");
may_add_newline();
//var ret = with_indent(current_col, cont); //var ret = with_indent(current_col, cont);
var ret = cont(); var ret = cont();
may_add_newline();
print("]"); print("]");
return ret; return ret;
}; }
function comma() { function comma() {
may_add_newline();
print(","); print(",");
may_add_newline();
space(); space();
}; }
function colon() { function colon() {
print(":"); print(":");
space(); space();
}; }
var add_mapping = mappings ? function(token, name) { var add_mapping = mappings ? function(token, name) {
mapping_token = token; mapping_token = token;
@@ -451,7 +459,7 @@ function OutputStream(options) {
ensure_line_len(); ensure_line_len();
} }
return OUTPUT; return OUTPUT;
}; }
function has_nlb() { function has_nlb() {
var index = OUTPUT.lastIndexOf("\n"); var index = OUTPUT.lastIndexOf("\n");
@@ -619,8 +627,7 @@ function OutputStream(options) {
return stack[stack.length - 2 - (n || 0)]; return stack[stack.length - 2 - (n || 0)];
} }
}; };
}
};
/* -----[ code generators ]----- */ /* -----[ code generators ]----- */
@@ -630,7 +637,7 @@ function OutputStream(options) {
function DEFPRINT(nodetype, generator) { function DEFPRINT(nodetype, generator) {
nodetype.DEFMETHOD("_codegen", generator); nodetype.DEFMETHOD("_codegen", generator);
}; }
var in_directive = false; var in_directive = false;
var active_scope = null; var active_scope = null;
@@ -679,7 +686,7 @@ function OutputStream(options) {
} else { } else {
nodetype.DEFMETHOD("needs_parens", func); nodetype.DEFMETHOD("needs_parens", func);
} }
}; }
PARENS(AST_Node, return_false); PARENS(AST_Node, return_false);
@@ -865,7 +872,7 @@ function OutputStream(options) {
} }
}); });
in_directive = false; in_directive = false;
}; }
AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){ AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){
force_statement(this.body, output); force_statement(this.body, output);
@@ -901,7 +908,7 @@ function OutputStream(options) {
display_body(self.body, false, output, allow_directives); display_body(self.body, false, output, allow_directives);
}); });
} else print_braced_empty(self, output); } else print_braced_empty(self, output);
}; }
DEFPRINT(AST_BlockStatement, function(self, output){ DEFPRINT(AST_BlockStatement, function(self, output){
print_braced(self, output); print_braced(self, output);
}); });
@@ -1064,7 +1071,7 @@ function OutputStream(options) {
else break; else break;
} }
force_statement(self.body, output); force_statement(self.body, output);
}; }
DEFPRINT(AST_If, function(self, output){ DEFPRINT(AST_If, function(self, output){
output.print("if"); output.print("if");
output.space(); output.space();
@@ -1184,7 +1191,7 @@ function OutputStream(options) {
} }
})); }));
node.print(output, parens); node.print(output, parens);
}; }
DEFPRINT(AST_VarDef, function(self, output){ DEFPRINT(AST_VarDef, function(self, output){
self.name.print(output); self.name.print(output);
@@ -1430,7 +1437,7 @@ function OutputStream(options) {
else else
stat.print(output); stat.print(output);
} }
}; }
// self should be AST_New. decide if we want to show parens or not. // self should be AST_New. decide if we want to show parens or not.
function need_constructor_parens(self, output) { function need_constructor_parens(self, output) {
@@ -1438,7 +1445,7 @@ function OutputStream(options) {
if (self.args.length > 0) return true; if (self.args.length > 0) return true;
return output.option("beautify"); return output.option("beautify");
}; }
function best_of(a) { function best_of(a) {
var best = a[0], len = best.length; var best = a[0], len = best.length;
@@ -1449,27 +1456,31 @@ function OutputStream(options) {
} }
} }
return best; return best;
}; }
function make_num(num) { function make_num(num) {
var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m; var str = num.toString(10).replace(/^0\./, ".").replace("e+", "e");
var candidates = [ str ];
if (Math.floor(num) === num) { if (Math.floor(num) === num) {
if (num >= 0) { if (num < 0) {
a.push("0x" + num.toString(16).toLowerCase(), // probably pointless candidates.push("-0x" + (-num).toString(16).toLowerCase());
"0" + num.toString(8)); // same.
} else { } else {
a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless candidates.push("0x" + num.toString(16).toLowerCase());
"-0" + (-num).toString(8)); // same.
} }
if ((m = /^(.*?)(0+)$/.exec(num))) {
a.push(m[1] + "e" + m[2].length);
}
} else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
a.push(m[2] + "e-" + (m[1].length + m[2].length),
str.substr(str.indexOf(".")));
} }
return best_of(a); var match, len, digits;
}; if (match = /^\.0+/.exec(str)) {
len = match[0].length;
digits = str.slice(len);
candidates.push(digits + "e-" + (digits.length + len - 1));
} else if (match = /0+$/.exec(str)) {
len = match[0].length;
candidates.push(str.slice(0, -len) + "e" + len);
} else if (match = /^(\d)\.(\d+)e(-?\d+)$/.exec(str)) {
candidates.push(match[1] + match[2] + "e" + (match[3] - match[2].length));
}
return best_of(candidates);
}
function make_block(stmt, output) { function make_block(stmt, output) {
if (!stmt || stmt instanceof AST_EmptyStatement) if (!stmt || stmt instanceof AST_EmptyStatement)
@@ -1481,7 +1492,7 @@ function OutputStream(options) {
stmt.print(output); stmt.print(output);
output.newline(); output.newline();
}); });
}; }
/* -----[ source map generators ]----- */ /* -----[ source map generators ]----- */

View File

@@ -55,7 +55,7 @@ function SymbolDef(scope, orig, init) {
this.mangled_name = null; this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
}; }
SymbolDef.next_id = 1; SymbolDef.next_id = 1;
@@ -344,7 +344,7 @@ function next_mangled_name(scope, options, def) {
} }
while (true) { while (true) {
name = base54(++scope.cname); name = base54(++scope.cname);
if (in_use[name] || !is_identifier(name) || options._reserved[name]) continue; if (in_use[name] || !is_identifier(name) || options.reserved.has[name]) continue;
if (!names[name]) break; if (!names[name]) break;
holes.push(scope.cname); holes.push(scope.cname);
} }
@@ -387,7 +387,7 @@ function _default_mangler_options(options) {
if (!Array.isArray(options.reserved)) options.reserved = []; if (!Array.isArray(options.reserved)) options.reserved = [];
// Never mangle arguments // Never mangle arguments
push_uniq(options.reserved, "arguments"); push_uniq(options.reserved, "arguments");
options._reserved = makePredicate(options.reserved); options.reserved.has = makePredicate(options.reserved);
return options; return options;
} }
@@ -453,7 +453,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
redefined.forEach(mangle); redefined.forEach(mangle);
function mangle(def) { function mangle(def) {
if (options._reserved[def.name]) return; if (options.reserved.has[def.name]) return;
def.mangle(options); def.mangle(options);
} }
}); });
@@ -504,7 +504,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
function rename(def) { function rename(def) {
if (def.global && options.cache) return; if (def.global && options.cache) return;
if (def.unmangleable(options)) return; if (def.unmangleable(options)) return;
if (options._reserved[def.name]) return; if (options.reserved.has[def.name]) return;
var d = def.redefined(); var d = def.redefined();
def.name = d ? d.name : next_name(); def.name = d ? d.name : next_name();
def.orig.forEach(function(sym) { def.orig.forEach(function(sym) {
@@ -556,17 +556,21 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
}); });
var base54 = (function() { var base54 = (function() {
var leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split(""); var freq = Object.create(null);
var digits = "0123456789".split(""); function init(chars) {
var array = [];
for (var i = 0, len = chars.length; i < len; i++) {
var ch = chars[i];
array.push(ch);
freq[ch] = -1e-2 * i;
}
return array;
}
var digits = init("0123456789");
var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_");
var chars, frequency; var chars, frequency;
function reset() { function reset() {
frequency = Object.create(null); frequency = Object.create(freq);
leading.forEach(function(ch) {
frequency[ch] = 0;
});
digits.forEach(function(ch) {
frequency[ch] = 0;
});
} }
base54.consider = function(str, delta) { base54.consider = function(str, delta) {
for (var i = str.length; --i >= 0;) { for (var i = str.length; --i >= 0;) {
@@ -577,7 +581,7 @@ var base54 = (function() {
return frequency[b] - frequency[a]; return frequency[b] - frequency[a];
} }
base54.sort = function() { base54.sort = function() {
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare)); chars = leading.sort(compare).concat(digits.sort(compare));
}; };
base54.reset = reset; base54.reset = reset;
reset(); reset();
@@ -591,6 +595,6 @@ var base54 = (function() {
base = 64; base = 64;
} while (num > 0); } while (num > 0);
return ret; return ret;
}; }
return base54; return base54;
})(); })();

View File

@@ -45,27 +45,25 @@
function characters(str) { function characters(str) {
return str.split(""); return str.split("");
}; }
function member(name, array) { function member(name, array) {
return array.indexOf(name) >= 0; return array.indexOf(name) >= 0;
}; }
function find_if(func, array) { function find_if(func, array) {
for (var i = 0, n = array.length; i < n; ++i) { for (var i = 0, n = array.length; i < n; ++i) {
if (func(array[i])) if (func(array[i])) return array[i];
return array[i];
} }
}; }
function repeat_string(str, i) { function repeat_string(str, i) {
if (i <= 0) return ""; if (i <= 0) return "";
if (i == 1) return str; if (i == 1) return str;
var d = repeat_string(str, i >> 1); var d = repeat_string(str, i >> 1);
d += d; d += d;
if (i & 1) d += str; return i & 1 ? d + str : d;
return d; }
};
function configure_error_stack(fn) { function configure_error_stack(fn) {
Object.defineProperty(fn.prototype, "stack", { Object.defineProperty(fn.prototype, "stack", {
@@ -84,27 +82,23 @@ function configure_error_stack(fn) {
function DefaultsError(msg, defs) { function DefaultsError(msg, defs) {
this.message = msg; this.message = msg;
this.defs = defs; this.defs = defs;
}; }
DefaultsError.prototype = Object.create(Error.prototype); DefaultsError.prototype = Object.create(Error.prototype);
DefaultsError.prototype.constructor = DefaultsError; DefaultsError.prototype.constructor = DefaultsError;
DefaultsError.prototype.name = "DefaultsError"; DefaultsError.prototype.name = "DefaultsError";
configure_error_stack(DefaultsError); configure_error_stack(DefaultsError);
DefaultsError.croak = function(msg, defs) {
throw new DefaultsError(msg, defs);
};
function defaults(args, defs, croak) { function defaults(args, defs, croak) {
if (args === true) if (args === true) args = {};
args = {};
var ret = args || {}; var ret = args || {};
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i)) if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i)) {
DefaultsError.croak("`" + i + "` is not a supported option", defs); throw new DefaultsError("`" + i + "` is not a supported option", defs);
}
for (var i in defs) if (HOP(defs, i)) { for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
} }
return ret; return ret;
}; }
function merge(obj, ext) { function merge(obj, ext) {
var count = 0; var count = 0;
@@ -113,7 +107,7 @@ function merge(obj, ext) {
count++; count++;
} }
return count; return count;
}; }
function noop() {} function noop() {}
function return_false() { return false; } function return_false() { return false; }
@@ -172,43 +166,19 @@ var MAP = (function(){
function push_uniq(array, el) { function push_uniq(array, el) {
if (array.indexOf(el) < 0) if (array.indexOf(el) < 0)
array.push(el); array.push(el);
}; }
function string_template(text, props) { function string_template(text, props) {
return text.replace(/\{(.+?)\}/g, function(str, p){ return text.replace(/\{(.+?)\}/g, function(str, p){
return props && props[p]; return props && props[p];
}); });
}; }
function remove(array, el) { function remove(array, el) {
for (var i = array.length; --i >= 0;) { for (var i = array.length; --i >= 0;) {
if (array[i] === el) array.splice(i, 1); if (array[i] === el) array.splice(i, 1);
} }
}; }
function mergeSort(array, cmp) {
if (array.length < 2) return array.slice();
function merge(a, b) {
var r = [], ai = 0, bi = 0, i = 0;
while (ai < a.length && bi < b.length) {
cmp(a[ai], b[bi]) <= 0
? r[i++] = a[ai++]
: r[i++] = b[bi++];
}
if (ai < a.length) r.push.apply(r, a.slice(ai));
if (bi < b.length) r.push.apply(r, b.slice(bi));
return r;
};
function _ms(a) {
if (a.length <= 1)
return a;
var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m);
left = _ms(left);
right = _ms(right);
return merge(left, right);
};
return _ms(array);
};
function makePredicate(words) { function makePredicate(words) {
if (!Array.isArray(words)) words = words.split(" "); if (!Array.isArray(words)) words = words.split(" ");
@@ -224,12 +194,12 @@ function all(array, predicate) {
if (!predicate(array[i])) if (!predicate(array[i]))
return false; return false;
return true; return true;
}; }
function Dictionary() { function Dictionary() {
this._values = Object.create(null); this._values = Object.create(null);
this._size = 0; this._size = 0;
}; }
Dictionary.prototype = { Dictionary.prototype = {
set: function(key, val) { set: function(key, val) {
if (!this.has(key)) ++this._size; if (!this.has(key)) ++this._size;
@@ -290,20 +260,22 @@ function HOP(obj, prop) {
// a statement. // a statement.
function first_in_statement(stack) { function first_in_statement(stack) {
var node = stack.parent(-1); var node = stack.parent(-1);
for (var i = 0, p; p = stack.parent(i); i++) { for (var i = 0, p; p = stack.parent(i++); node = p) {
if (p instanceof AST_Statement && p.body === node) if (p.TYPE == "Call") {
return true; if (p.expression === node) continue;
if ((p instanceof AST_Sequence && p.expressions[0] === node) || } else if (p instanceof AST_Binary) {
(p.TYPE == "Call" && p.expression === node ) || if (p.left === node) continue;
(p instanceof AST_Dot && p.expression === node ) || } else if (p instanceof AST_Conditional) {
(p instanceof AST_Sub && p.expression === node ) || if (p.condition === node) continue;
(p instanceof AST_Conditional && p.condition === node ) || } else if (p instanceof AST_PropAccess) {
(p instanceof AST_Binary && p.left === node ) || if (p.expression === node) continue;
(p instanceof AST_UnaryPostfix && p.expression === node )) } else if (p instanceof AST_Sequence) {
{ if (p.expressions[0] === node) continue;
node = p; } else if (p instanceof AST_Statement) {
} else { return p.body === node;
return false; } else if (p instanceof AST_UnaryPostfix) {
if (p.expression === node) continue;
} }
return false;
} }
} }

View File

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

View File

@@ -4056,6 +4056,36 @@ replace_all_var: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
replace_all_var_scope: {
rename = true;
options = {
collapse_vars: true,
unused: true,
}
mangle = {}
input: {
var a = 100, b = 10;
(function(r, a) {
switch (~a) {
case (b += a):
case a++:
}
})(--b, a);
console.log(a, b);
}
expect: {
var a = 100, b = 10;
(function(c, o) {
switch (~a) {
case (b += a):
case o++:
}
})(--b, a);
console.log(a, b);
}
expect_stdout: "100 109"
}
cascade_statement: { cascade_statement: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -5327,3 +5357,24 @@ issue_3032: {
} }
expect_stdout: "42" expect_stdout: "42"
} }
issue_3096: {
options = {
collapse_vars: true,
}
input: {
console.log(function() {
var ar = ["a", "b"];
var first = ar.pop();
return ar + "" + first;
}());
}
expect: {
console.log(function() {
var ar = ["a", "b"];
var first = ar.pop();
return ar + "" + first;
}());
}
expect_stdout: "ab"
}

View File

@@ -2267,3 +2267,55 @@ issue_3054: {
} }
expect_stdout: "true true" expect_stdout: "true true"
} }
issue_3076: {
options = {
dead_code: true,
inline: true,
sequences: true,
unused: true,
}
input: {
var c = "PASS";
(function(b) {
var n = 2;
while (--b + function() {
e && (c = "FAIL");
e = 5;
return 1;
try {
var a = 5;
} catch (e) {
var e;
}
}().toString() && --n > 0);
})(2);
console.log(c);
}
expect: {
var c = "PASS";
(function(b) {
var n = 2;
while (--b + (e = void 0, e && (c = "FAIL"), e = 5, 1).toString() && --n > 0);
var e;
})(2),
console.log(c);
}
expect_stdout: "PASS"
}
issue_3125: {
options = {
inline: true,
unsafe: true,
}
input: {
console.log(function() {
return "PASS";
}.call());
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -742,3 +742,117 @@ issue_3046: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
issue_3071_1: {
options = {
evaluate: true,
inline: true,
join_vars: true,
hoist_props: true,
passes: 3,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
(function() {
var obj = {};
obj.one = 1;
obj.two = 2;
console.log(obj.one);
})();
}
expect: {
console.log(1);
}
expect_stdout: "1"
}
issue_3071_2: {
options = {
evaluate: true,
inline: true,
join_vars: true,
hoist_props: true,
passes: 3,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
obj = {};
obj.one = 1;
obj.two = 2;
console.log(obj.one);
var obj;
})();
}
expect: {
console.log(1);
}
expect_stdout: "1"
}
issue_3071_2_toplevel: {
options = {
evaluate: true,
inline: true,
join_vars: true,
hoist_props: true,
passes: 3,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
(function() {
obj = {};
obj.one = 1;
obj.two = 2;
console.log(obj.one);
var obj;
})();
}
expect: {
console.log(1);
}
expect_stdout: "1"
}
issue_3071_3: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
var c = 0;
(function(a, b) {
(function f(o) {
var n = 2;
while (--b + (o = {
p: c++,
}) && --n > 0);
})();
})();
console.log(c);
}
expect: {
var c = 0;
(function(a, b) {
(function f(o) {
var n = 2;
while (--b + (o = {
p: c++,
}) && --n > 0);
})();
})();
console.log(c);
}
expect_stdout: "2"
}

View File

@@ -8,14 +8,14 @@ too_short: {
} }
} }
expect_exact: [ expect_exact: [
'function f(a){', "function f(",
'return{', "a){return{",
'c:42,', "c:42,d:a(",
'd:a(),', '),e:"foo"}',
'e:"foo"}}', "}",
] ]
expect_warnings: [ expect_warnings: [
"WARN: Output exceeds 10 characters" "WARN: Output exceeds 10 characters",
] ]
} }
@@ -29,11 +29,25 @@ just_enough: {
} }
} }
expect_exact: [ expect_exact: [
'function f(a){', "function f(a){",
'return{c:42,', "return{c:42,",
'd:a(),e:"foo"}', 'd:a(),e:"foo"}',
'}', "}",
]
expect_warnings: [
] ]
expect_warnings: []
}
issue_304: {
beautify = {
max_line_len: 10,
}
input: {
var a = 0, b = 0, c = 0, d = 0, e = 0;
}
expect_exact: [
"var a=0,",
"b=0,c=0,",
"d=0,e=0;",
]
expect_warnings: []
} }

View File

@@ -1,21 +1,49 @@
hex_numbers_in_parentheses_for_prototype_functions: { hex_numbers_in_parentheses_for_prototype_functions: {
input: { beautify = {
(-2); beautify: true,
(-2).toFixed(0);
(2);
(2).toFixed(0);
(0.2);
(0.2).toFixed(0);
(0.00000002);
(0.00000002).toFixed(0);
(1000000000000000128);
(1000000000000000128).toFixed(0);
} }
expect_exact: "-2;(-2).toFixed(0);2;2..toFixed(0);.2;.2.toFixed(0);2e-8;2e-8.toFixed(0);0xde0b6b3a7640080;(0xde0b6b3a7640080).toFixed(0);" input: {
function f() {
(-2);
(-2).toFixed(0);
(2);
(2).toFixed(0);
(0.2);
(0.2).toFixed(0);
(2.34e20);
(2.34e20).toFixed(0);
(0.00000002);
(0.00000002).toFixed(0);
(1000000000000000128);
(1000000000000000128).toFixed(0);
(-1000000000000000128);
(-1000000000000000128).toFixed(0);
}
}
expect_exact: [
"function f() {",
" -2;",
" (-2).toFixed(0);",
" 2;",
" 2..toFixed(0);",
" .2;",
" .2.toFixed(0);",
" 234e18;",
" 234e18.toFixed(0);",
" 2e-8;",
" 2e-8.toFixed(0);",
" 0xde0b6b3a7640080;",
" (0xde0b6b3a7640080).toFixed(0);",
" -0xde0b6b3a7640080;",
" (-0xde0b6b3a7640080).toFixed(0);",
"}",
]
} }
comparisons: { comparisons: {

View File

@@ -1208,6 +1208,37 @@ join_object_assignments_3: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
join_object_assignments_4: {
options = {
join_vars: true,
sequences: true,
}
input: {
var o;
console.log(o);
o = {};
o.a = "foo";
console.log(o.b);
o.b = "bar";
console.log(o.a);
}
expect: {
var o;
console.log(o),
o = {
a: "foo",
},
console.log(o.b),
o.b = "bar",
console.log(o.a);
}
expect_stdout: [
"undefined",
"undefined",
"foo",
]
}
join_object_assignments_return_1: { join_object_assignments_return_1: {
options = { options = {
join_vars: true, join_vars: true,

View File

@@ -535,3 +535,110 @@ issue_2705_6: {
"/* */new(/* */a()||b())(c(),d());", "/* */new(/* */a()||b())(c(),d());",
] ]
} }
issue_3065_1: {
options = {
inline: true,
pure_funcs: [ "pureFunc" ],
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function modifyWrapper(a, f, wrapper) {
wrapper.a = a;
wrapper.f = f;
return wrapper;
}
function pureFunc(fun) {
return modifyWrapper(1, fun, function(a) {
return fun(a);
});
}
var unused = pureFunc(function(x) {
return x;
});
}
expect: {}
}
issue_3065_2: {
rename = true
options = {
inline: true,
pure_funcs: [ "pureFunc" ],
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
mangle = {
reserved: [ "pureFunc" ],
toplevel: true,
}
input: {
function modifyWrapper(a, f, wrapper) {
wrapper.a = a;
wrapper.f = f;
return wrapper;
}
function pureFunc(fun) {
return modifyWrapper(1, fun, function(a) {
return fun(a);
});
}
var unused = pureFunc(function(x) {
return x;
});
}
expect: {}
}
issue_3065_3: {
options = {
pure_funcs: [ "debug" ],
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function debug(msg) {
console.log(msg);
}
debug(function() {
console.log("PASS");
return "FAIL";
}());
}
expect: {
(function() {
console.log("PASS");
})();
}
}
issue_3065_4: {
options = {
pure_funcs: [ "debug" ],
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var debug = function(msg) {
console.log(msg);
};
debug(function() {
console.log("PASS");
return "FAIL";
}());
}
expect: {
(function() {
console.log("PASS");
})();
}
}

View File

@@ -1476,18 +1476,18 @@ defun_redefine: {
}; };
return g() + h(); return g() + h();
} }
console.log(f());
} }
expect: { expect: {
function f() { function f() {
function g() { (function() {
return 1;
}
g = function() {
return 3; return 3;
}; });
return g() + 2; return 3 + 2;
} }
console.log(f());
} }
expect_stdout: "5"
} }
func_inline: { func_inline: {
@@ -1527,23 +1527,37 @@ func_modified: {
} }
input: { input: {
function f(a) { function f(a) {
function a() { return 1; } function a() {
function b() { return 2; } return 1;
function c() { return 3; } }
function b() {
return 2;
}
function c() {
return 3;
}
b.inject = []; b.inject = [];
c = function() { return 4; }; c = function() {
return 4;
};
return a() + b() + c(); return a() + b() + c();
} }
console.log(f());
} }
expect: { expect: {
function f(a) { function f(a) {
function b() { return 2; } function b() {
function c() { return 3; } return 2;
}
b.inject = []; b.inject = [];
c = function() { return 4; }; (function() {
return 1 + 2 + c(); return 4;
});
return 1 + 2 + 4;
} }
console.log(f());
} }
expect_stdout: "7"
} }
defun_label: { defun_label: {
@@ -5054,9 +5068,7 @@ defun_var_1: {
console.log(typeof a, typeof b); console.log(typeof a, typeof b);
} }
expect: { expect: {
var a = 42; console.log("number", "function");
function a() {}
console.log(typeof a, "function");
} }
expect_stdout: "number function" expect_stdout: "number function"
} }
@@ -5076,9 +5088,7 @@ defun_var_2: {
console.log(typeof a, typeof b); console.log(typeof a, typeof b);
} }
expect: { expect: {
function a() {} console.log("number", "function");
var a = 42;
console.log(typeof a, "function");
} }
expect_stdout: "number function" expect_stdout: "number function"
} }
@@ -5654,3 +5664,485 @@ issue_3042_2: {
"true", "true",
] ]
} }
issue_3068_1: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
(function() {
do {
continue;
var b = "defined";
} while (b && b.c);
})();
}
expect: {
(function() {
do {
continue;
var b = "defined";
} while (b && b.c);
})();
}
expect_stdout: true
}
issue_3068_2: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
(function() {
do {
try {
while ("" == typeof a);
} finally {
continue;
}
var b = "defined";
} while (b && b.c);
})();
}
expect: {
(function() {
do {
try {
while ("" == typeof a);
} finally {
continue;
}
var b = "defined";
} while (b && b.c);
})();
}
expect_stdout: true
}
issue_3110_1: {
options = {
conditionals: true,
evaluate: true,
inline: true,
passes: 3,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
function foo() {
return isDev ? "foo" : "bar";
}
var isDev = true;
var obj = {
foo: foo
};
console.log(foo());
console.log(obj.foo());
})();
}
expect: {
console.log("foo"),
console.log("foo");
}
expect_stdout: [
"foo",
"foo",
]
}
issue_3110_2: {
options = {
conditionals: true,
evaluate: true,
inline: true,
passes: 4,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
function foo() {
return isDev ? "foo" : "bar";
}
var isDev = true;
console.log(foo());
var obj = {
foo: foo
};
console.log(obj.foo());
})();
}
expect: {
console.log("foo"),
console.log("foo");
}
expect_stdout: [
"foo",
"foo",
]
}
issue_3110_3: {
options = {
conditionals: true,
evaluate: true,
inline: true,
properties: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
function foo() {
return isDev ? "foo" : "bar";
}
console.log(foo());
var isDev = true;
var obj = {
foo: foo
};
console.log(obj.foo());
})();
}
expect: {
(function() {
function foo() {
return isDev ? "foo" : "bar";
}
console.log(foo());
var isDev = true;
var obj = {
foo: foo
};
console.log(obj.foo());
})();
}
expect_stdout: [
"bar",
"foo",
]
}
issue_3113_1: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var c = 0;
(function() {
function f() {
while (g());
}
var a = f();
function g() {
a && a[c++];
}
g(a = 1);
})();
console.log(c);
}
expect: {
var c = 0;
(function() {
function f() {
while (g());
}
var a = f();
function g() {
a && a[c++];
}
g(a = 1);
})();
console.log(c);
}
expect_stdout: "1"
}
issue_3113_2: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var c = 0;
(function() {
function f() {
while (g());
}
var a = f();
function g() {
a && a[c++];
}
a = 1;
g();
})();
console.log(c);
}
expect: {
var c = 0;
(function() {
function f() {
while (g());
}
var a = f();
function g() {
a && a[c++];
}
a = 1;
g();
})();
console.log(c);
}
expect_stdout: "1"
}
issue_3113_3: {
options = {
evaluate: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
(function() {
function f() {
while (g());
}
var a;
function g() {
a && a[c++];
}
g(a = 1);
})();
console.log(c);
}
expect: {
var c = 0;
c++;
console.log(c);
}
expect_stdout: "1"
}
issue_3113_4: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 0, b = 0;
function f() {
b += a;
}
f(f(), ++a);
console.log(a, b);
}
expect: {
var a = 0, b = 0;
function f() {
b += a;
}
f(f(), ++a);
console.log(a, b);
}
expect_stdout: "1 1"
}
issue_3113_5: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
function f() {
console.log(a);
}
function g() {
f();
}
while (g());
var a = 1;
f();
}
expect: {
function f() {
console.log(a);
}
function g() {
f();
}
while (g());
var a = 1;
f();
}
expect_stdout: [
"undefined",
"1",
]
}
conditional_nested_1: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var a = 1, b = 0;
(function f(c) {
function g() {
c && (c.a = 0);
c && (c.a = 0);
c && (c[b++] *= 0);
}
g(a-- && f(g(c = 42)));
})();
console.log(b);
}
expect: {
var a = 1, b = 0;
(function f(c) {
function g() {
c && (c.a = 0);
c && (c.a = 0);
c && (c[b++] *= 0);
}
g(a-- && f(g(c = 42)));
})();
console.log(b);
}
expect_stdout: "2"
}
conditional_nested_2: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var c = 0;
(function(a) {
function f() {
a && c++;
}
f(!c && f(), a = 1);
})();
console.log(c);
}
expect: {
var c = 0;
(function(a) {
function f() {
a && c++;
}
f(!c && f(), a = 1);
})();
console.log(c);
}
expect_stdout: "1"
}
issue_2436: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var c;
console.log(((c = {
a: 1,
b: 2
}).a = 3, {
x: c.a,
y: c.b
}));
}
expect: {
var c;
console.log(((c = {
a: 1,
b: 2
}).a = 3, {
x: c.a,
y: c.b
}));
}
expect_stdout: true
}
issue_2916: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
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_3125: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var o;
console.log((function() {
this.p++;
}.call(o = {
p: 6
}), o.p));
}
expect: {
var o;
console.log((function() {
this.p++;
}.call(o = {
p: 6
}), o.p));
}
expect_stdout: "7"
}

View File

@@ -876,3 +876,59 @@ forin: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
call: {
options = {
sequences: true,
}
input: {
var a = function() {
return this;
}();
function b() {
console.log("foo");
}
b.c = function() {
console.log(this === b ? "bar" : "baz");
};
(a, b)();
(a, b.c)();
(a, function() {
console.log(this === a);
})();
new (a, b)();
new (a, b.c)();
new (a, function() {
console.log(this === a);
})();
}
expect: {
var a = function() {
return this;
}();
function b() {
console.log("foo");
}
b.c = function() {
console.log(this === b ? "bar" : "baz");
},
a, b(),
(a, b.c)(),
a, function() {
console.log(this === a);
}(),
a, new b(),
a, new b.c(),
a, new function() {
console.log(this === a);
}();
}
expect_stdout: [
"foo",
"baz",
"true",
"foo",
"baz",
"false",
]
}

View File

@@ -90,17 +90,11 @@ typeof_defun_1: {
"function" == typeof h && h(); "function" == typeof h && h();
} }
expect: { expect: {
function g() {
h = 42;
console.log("NOPE");
}
function h() { function h() {
console.log("YUP"); console.log("YUP");
} }
g = 42;
console.log("YES"); console.log("YES");
"function" == typeof g && g(); h();
"function" == typeof h && h();
} }
expect_stdout: [ expect_stdout: [
"YES", "YES",

View File

@@ -1,5 +1,5 @@
function test(a){ function test(a){
"aaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaa"
;a(err,data),a(err,data) ;a(err,data),a(err,
} data)}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQyxJQUFLQyJ9 //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQztBQUFLQyJ9

View File

@@ -100,8 +100,8 @@ describe("bin/uglifyjs", function () {
exec(command, function (err, stdout, stderr) { exec(command, function (err, stdout, stderr) {
if (err) throw err; if (err) throw err;
var stderrLines = stderr.split('\n'); var stderrLines = stderr.split("\n");
assert.strictEqual(stderrLines[0], 'INFO: Using input source map: test/input/issue-2082/sample.js.map'); assert.strictEqual(stderrLines[0], "INFO: Using input source map: test/input/issue-2082/sample.js.map");
assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}'); assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}');
done(); done();
}); });
@@ -244,7 +244,8 @@ describe("bin/uglifyjs", function () {
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==",
"", "",
].join("\n")); ].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found: test/input/issue-1323/sample.js\n"); var stderrLines = stderr.split("\n");
assert.strictEqual(stderrLines[0], "WARN: inline source map not found: test/input/issue-1323/sample.js");
done(); done();
}); });
}); });
@@ -264,7 +265,8 @@ describe("bin/uglifyjs", function () {
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwidGVzdC9pbnB1dC9pc3N1ZS0xMzIzL3NhbXBsZS5qcyJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFNBQUFBLE1BQWdCQyxRQUFRQyxJQUFJLEVBQUUsSUFBTyxJQUFJRixJQ0FuRCxJQUFJRyxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwidGVzdC9pbnB1dC9pc3N1ZS0xMzIzL3NhbXBsZS5qcyJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFNBQUFBLE1BQWdCQyxRQUFRQyxJQUFJLEVBQUUsSUFBTyxJQUFJRixJQ0FuRCxJQUFJRyxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==",
"", "",
].join("\n")); ].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found: test/input/issue-1323/sample.js\n"); var stderrLines = stderr.split("\n");
assert.strictEqual(stderrLines[0], "WARN: inline source map not found: test/input/issue-1323/sample.js");
done(); done();
}); });
}); });

View File

@@ -91,7 +91,7 @@ describe("parentheses", function() {
"(function(){}).name;", "(function(){}).name;",
]; ];
for (var i = 16; --i >= 0;) { for (var i = 16; --i >= 0;) {
[].push.apply(code, code); code = code.concat(code);
} }
code = code.join(""); code = code.join("");
var result = uglify.minify(code, { var result = uglify.minify(code, {

View File

@@ -172,7 +172,7 @@ function run_compress_tests() {
} }
if (test.expect_stdout if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) { && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = sandbox.run_code(input_code, true); var stdout = run_code(input_code);
if (test.expect_stdout === true) { if (test.expect_stdout === true) {
test.expect_stdout = stdout; test.expect_stdout = stdout;
} }
@@ -186,7 +186,7 @@ function run_compress_tests() {
}); });
return false; return false;
} }
stdout = sandbox.run_code(output, true); stdout = run_code(output);
if (!sandbox.same_stdout(test.expect_stdout, stdout)) { 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", { log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
input: input_formatted, input: input_formatted,
@@ -344,6 +344,11 @@ function evaluate(code) {
return new Function("return(" + code + ")")(); return new Function("return(" + code + ")")();
} }
function run_code(code) {
var result = sandbox.run_code(code, true);
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
}
// Try to reminify original input with standard options // Try to reminify original input with standard options
// to see if it matches expect_stdout. // to see if it matches expect_stdout.
function reminify(orig_options, input_code, input_formatted, expect_stdout) { function reminify(orig_options, input_code, input_formatted, expect_stdout) {
@@ -367,7 +372,7 @@ function reminify(orig_options, input_code, input_formatted, expect_stdout) {
}); });
return false; return false;
} else { } else {
var stdout = sandbox.run_code(result.code, true); var stdout = run_code(result.code);
if (typeof expect_stdout != "string" && typeof stdout != "string" && expect_stdout.name == stdout.name) { if (typeof expect_stdout != "string" && typeof stdout != "string" && expect_stdout.name == stdout.name) {
stdout = expect_stdout; stdout = expect_stdout;
} }

View File

@@ -2,24 +2,18 @@ var semver = require("semver");
var vm = require("vm"); var vm = require("vm");
function createContext() { function createContext() {
var context = Object.create(null); return vm.createContext(Object.defineProperty({}, "console", {
Object.defineProperty(context, "console", { value: {
value: function() { log: function(msg) {
var con = Object.create(null); if (arguments.length == 1 && typeof msg == "string") {
Object.defineProperty(con, "log", { return console.log("%s", msg);
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 console.log.apply(console, [].map.call(arguments, function(arg) {
return con; return safe_log(arg, 3);
}() }));
}); }
return vm.createContext(context); }
}));
} }
function safe_log(arg, level) { function safe_log(arg, level) {
@@ -40,7 +34,7 @@ function safe_log(arg, level) {
} }
function strip_func_ids(text) { function strip_func_ids(text) {
return text.toString().replace(/F[0-9]{6}N/g, "<F<>N>"); return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
} }
var context; var context;

View File

@@ -18,6 +18,13 @@
{ {
"toplevel": true "toplevel": true
}, },
{
"compress": {
"passes": 1e6,
"unsafe": true
},
"toplevel": true
},
{ {
"compress": { "compress": {
"keep_fargs": false, "keep_fargs": false,