Compare commits

..

30 Commits

Author SHA1 Message Date
Alex Lam S.L
f72d3029dd v3.3.25 2018-05-12 23:50:40 +00:00
Alex Lam S.L
1a0d6edc81 remove colors dependency (#3133) 2018-05-13 07:50:02 +08:00
Alex Lam S.L
7b59b2f5b2 replace mocha dependency (#3131) 2018-05-11 20:15:34 +08:00
Alex Lam S.L
7bc7704edf fix corner case in reduce_vars (#3129) 2018-05-10 18:45:20 +08:00
Alex Lam S.L
14e712ee80 fix corner case in call binding (#3128)
fixes #3127
2018-05-10 06:16:35 +08:00
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
24 changed files with 1307 additions and 384 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

@@ -1041,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] = false;
}
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,39 @@ 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;
var visited = tw.defun_visited[def.id];
if (marker === tw.safe_ids) {
if (!visited) return def.fixed;
} else if (visited) {
def.init.enclosed.forEach(function(d) {
if (def.init.variables.get(d.name) === d) return;
if (!safe_to_read(tw, d)) d.fixed = false;
});
} else {
tw.defun_ids[def.id] = false;
}
} else {
if (!tw.in_loop) {
tw.defun_ids[def.id] = tw.safe_ids;
return def.fixed;
}
tw.defun_ids[def.id] = false;
}
}
function walk_defuns(tw, scope) {
scope.functions.each(function(def) {
if (def.init instanceof AST_Defun && !tw.defun_visited[def.id]) {
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 +439,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 +450,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 +474,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 +497,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 +515,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 +544,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 +555,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,12 +592,16 @@ 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_visited[id]) return true;
if (tw.defun_ids[id] !== tw.safe_ids) return true;
tw.defun_visited[id] = true;
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_Do, function(tw) { def(AST_Do, function(tw) {
@@ -600,6 +674,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) {
@@ -644,7 +719,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 {
@@ -653,12 +728,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);
@@ -675,12 +761,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() {
@@ -702,7 +789,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;
}; };
@@ -730,19 +817,25 @@ merge(Compressor.prototype, {
}); });
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);
tw.defun_visited = 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);
}); });
@@ -834,15 +927,21 @@ merge(Compressor.prototype, {
type: typeof val type: typeof val
})); }));
} }
}; }
function needs_unbinding(compressor, val) {
return val instanceof AST_PropAccess
|| compressor.has_directive("use strict")
&& is_undeclared_ref(val)
&& val.name == "eval";
}
// we shouldn't compress (1,func)(something) to // we shouldn't compress (1,func)(something) to
// func(something) because that changes the meaning of // func(something) because that changes the meaning of
// 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(compressor, 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 && needs_unbinding(compressor, val)) {
&& (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 ]);
} }
return val; return val;
@@ -974,24 +1073,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
@@ -1007,9 +1094,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,
@@ -1020,24 +1109,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(compressor, 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;
@@ -1046,20 +1126,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;
} }
@@ -1086,7 +1153,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;
@@ -1118,7 +1185,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;
@@ -1170,6 +1239,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
@@ -1348,8 +1462,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);
@@ -1418,7 +1533,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);
@@ -1480,10 +1595,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;
} }
@@ -3245,7 +3360,7 @@ merge(Compressor.prototype, {
} }
if (value) { if (value) {
props.push(value); props.push(value);
return maintain_this_binding(parent, node, make_sequence(node, props.map(function(prop) { return maintain_this_binding(compressor, parent, node, make_sequence(node, props.map(function(prop) {
return prop.transform(tt); return prop.transform(tt);
}))); })));
} }
@@ -4371,7 +4486,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 (needs_unbinding(compressor, tail) && !(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) {
@@ -4572,13 +4704,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;
} }
@@ -4879,7 +5014,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)) {
@@ -4903,7 +5042,7 @@ merge(Compressor.prototype, {
var end = expressions.length - 1; var end = expressions.length - 1;
trim_right_for_undefined(); trim_right_for_undefined();
if (end == 0) { if (end == 0) {
self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]); self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
if (!(self instanceof AST_Sequence)) self = self.optimize(compressor); if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
return self; return self;
} }
@@ -4935,14 +5074,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;
}); });
@@ -5224,7 +5361,7 @@ merge(Compressor.prototype, {
var ll = fuzzy_eval(self.left); var ll = fuzzy_eval(self.left);
if (!ll) { if (!ll) {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start); compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor); return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
} else if (!(ll instanceof AST_Node)) { } else if (!(ll instanceof AST_Node)) {
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start); compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
@@ -5263,7 +5400,7 @@ merge(Compressor.prototype, {
return make_sequence(self, [ self.left, self.right ]).optimize(compressor); return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
} else if (!(ll instanceof AST_Node)) { } else if (!(ll instanceof AST_Node)) {
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start); compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor); return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
} }
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if (!rr) { if (!rr) {

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;
@@ -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.21", "version": "3.3.25",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -28,7 +28,6 @@
}, },
"devDependencies": { "devDependencies": {
"acorn": "~5.5.3", "acorn": "~5.5.3",
"mocha": "~3.5.1",
"semver": "~5.5.0" "semver": "~5.5.0"
}, },
"scripts": { "scripts": {

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

@@ -2303,3 +2303,19 @@ issue_3076: {
} }
expect_stdout: "PASS" 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

@@ -1,11 +1,31 @@
remove_redundant_sequence_items: { remove_sequence: {
options = { side_effects: true }; options = {
side_effects: true,
}
input: { input: {
(0, 1, eval)(); (0, 1, eval)();
(0, 1, logThis)(); (0, 1, logThis)();
(0, 1, _decorators.logThis)(); (0, 1, _decorators.logThis)();
} }
expect: { expect: {
eval();
logThis();
(0, _decorators.logThis)();
}
}
remove_redundant_sequence_items: {
options = {
side_effects: true,
}
input: {
"use strict";
(0, 1, eval)();
(0, 1, logThis)();
(0, 1, _decorators.logThis)();
}
expect: {
"use strict";
(0, eval)(); (0, eval)();
logThis(); logThis();
(0, _decorators.logThis)(); (0, _decorators.logThis)();
@@ -13,13 +33,17 @@ remove_redundant_sequence_items: {
} }
dont_remove_this_binding_sequence: { dont_remove_this_binding_sequence: {
options = { side_effects: true }; options = {
side_effects: true,
}
input: { input: {
"use strict";
(0, eval)(); (0, eval)();
(0, logThis)(); (0, logThis)();
(0, _decorators.logThis)(); (0, _decorators.logThis)();
} }
expect: { expect: {
"use strict";
(0, eval)(); (0, eval)();
logThis(); logThis();
(0, _decorators.logThis)(); (0, _decorators.logThis)();

View File

@@ -3,8 +3,9 @@ this_binding_conditionals: {
conditionals: true, conditionals: true,
evaluate: true, evaluate: true,
side_effects: true, side_effects: true,
}; }
input: { input: {
"use strict";
(1 && a)(); (1 && a)();
(0 || a)(); (0 || a)();
(0 || 1 && a)(); (0 || 1 && a)();
@@ -26,6 +27,7 @@ this_binding_conditionals: {
(1 ? eval : 0)(); (1 ? eval : 0)();
} }
expect: { expect: {
"use strict";
a(); a();
a(); a();
a(); a();
@@ -53,13 +55,15 @@ this_binding_collapse_vars: {
collapse_vars: true, collapse_vars: true,
toplevel: true, toplevel: true,
unused: true, unused: true,
}; }
input: { input: {
"use strict";
var c = a; c(); var c = a; c();
var d = a.b; d(); var d = a.b; d();
var e = eval; e(); var e = eval; e();
} }
expect: { expect: {
"use strict";
a(); a();
(0, a.b)(); (0, a.b)();
(0, eval)(); (0, eval)();
@@ -69,31 +73,88 @@ this_binding_collapse_vars: {
this_binding_side_effects: { this_binding_side_effects: {
options = { options = {
side_effects : true side_effects : true
}; }
input: { input: {
(function (foo) { (function(foo) {
(0, foo)(); (0, foo)();
(0, foo.bar)(); (0, foo.bar)();
(0, eval)('console.log(foo);'); (0, eval)("console.log(foo);");
}()); }());
(function (foo) { (function(foo) {
"use strict";
(0, foo)();
(0, foo.bar)();
(0, eval)("console.log(foo);");
}());
(function(foo) {
var eval = console; var eval = console;
(0, foo)(); (0, foo)();
(0, foo.bar)(); (0, foo.bar)();
(0, eval)('console.log(foo);'); (0, eval)("console.log(foo);");
}()); }());
} }
expect: { expect: {
(function (foo) { (function(foo) {
foo(); foo();
(0, foo.bar)(); (0, foo.bar)();
(0, eval)('console.log(foo);'); eval("console.log(foo);");
}()); }());
(function (foo) { (function(foo) {
"use strict";
foo();
(0, foo.bar)();
(0, eval)("console.log(foo);");
}());
(function(foo) {
var eval = console; var eval = console;
foo(); foo();
(0, foo.bar)(); (0, foo.bar)();
(0, eval)('console.log(foo);'); eval("console.log(foo);");
}()); }());
} }
} }
this_binding_sequences: {
options = {
sequences: true,
side_effects: true,
}
input: {
console.log(typeof function() {
return eval("this");
}());
console.log(typeof function() {
"use strict";
return eval("this");
}());
console.log(typeof function() {
return (0, eval)("this");
}());
console.log(typeof function() {
"use strict";
return (0, eval)("this");
}());
}
expect: {
console.log(typeof function() {
return eval("this");
}()),
console.log(typeof function() {
"use strict";
return eval("this");
}()),
console.log(typeof function() {
return eval("this");
}()),
console.log(typeof function() {
"use strict";
return (0, eval)("this");
}());
}
expect_stdout: [
"object",
"undefined",
"object",
"object",
]
}

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

@@ -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"
} }
@@ -5710,3 +5720,461 @@ issue_3068_2: {
} }
expect_stdout: true 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"
}
conditional_nested_3: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var n = 2, c = 0;
(function f(a) {
0 < n-- && g(a = 1);
function g() {
a && c++;
}
g();
0 < n-- && f();
})();
console.log(c);
}
expect: {
var n = 2, c = 0;
(function f(a) {
0 < n-- && g(a = 1);
function g() {
a && c++;
}
g();
0 < n-- && f();
})();
console.log(c);
}
expect_stdout: "2"
}
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

@@ -1,24 +1,109 @@
var fs = require("fs"); var fs = require("fs");
var Mocha = require("mocha");
var path = require("path");
// Instantiate a Mocha instance var config = {
var mocha = new Mocha({ limit: 5000,
timeout: 5000 timeout: function(limit) {
}); this.limit = limit;
var testDir = __dirname + "/mocha/"; }
};
var tasks = [];
var titles = [];
describe = function(title, fn) {
config = Object.create(config);
titles.push(title);
fn.call(config);
titles.pop();
config = Object.getPrototypeOf(config);
};
it = function(title, fn) {
fn.limit = config.limit;
fn.titles = titles.slice();
fn.titles.push(title);
tasks.push(fn);
};
// Add each .js file to the Mocha instance fs.readdirSync("test/mocha").filter(function(file) {
fs.readdirSync(testDir).filter(function(file) {
return /\.js$/.test(file); return /\.js$/.test(file);
}).forEach(function(file) { }).forEach(function(file) {
mocha.addFile(path.join(testDir, file)); require("./mocha/" + file);
}); });
module.exports = function() { function log_titles(log, current, marker) {
mocha.run(function(failures) { var indent = "";
if (failures) process.on("exit", function() { var writing = false;
process.exit(failures); for (var i = 0; i < current.length; i++, indent += " ") {
if (titles[i] != current[i]) writing = true;
if (writing) log(indent + (i == current.length - 1 && marker || "") + current[i]);
}
titles = current;
}
function red(text) {
return "\u001B[31m" + text + "\u001B[39m";
}
function green(text) {
return "\u001B[32m" + text + "\u001B[39m";
}
var errors = [];
var total = tasks.length;
titles = [];
process.nextTick(function run() {
var task = tasks.shift();
if (task) try {
var elapsed = Date.now();
var timer;
var done = function() {
clearTimeout(timer);
done = function() {};
elapsed = Date.now() - elapsed;
if (elapsed > task.limit) {
throw new Error("Timed out: " + elapsed + "ms > " + task.limit + "ms");
}
log_titles(console.log, task.titles, green('\u221A '));
process.nextTick(run);
};
if (task.length) {
task.timeout = function(limit) {
clearTimeout(timer);
task.limit = limit;
timer = setTimeout(function() {
raise(new Error("Timed out: exceeds " + limit + "ms"));
}, limit);
};
task.timeout(task.limit);
task.call(task, done);
} else {
task.timeout = config.timeout;
task.call(task);
done();
}
} catch (err) {
raise(err);
} else if (errors.length) {
console.error();
console.log(red(errors.length + " test(s) failed!"));
titles = [];
errors.forEach(function(titles, index) {
console.error();
log_titles(console.error, titles, (index + 1) + ") ");
var lines = titles.error.stack.split('\n');
console.error(red(lines[0]));
console.error(lines.slice(1).join("\n"));
}); });
}); process.exit(1);
}; } else {
console.log();
console.log(green(total + " test(s) passed."));
}
function raise(err) {
clearTimeout(timer);
done = function() {};
task.titles.error = err;
errors.push(task.titles);
log_titles(console.log, task.titles, red('\u00D7 '));
process.nextTick(run);
}
});

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

@@ -18,9 +18,8 @@ if (failures) {
console.error("!!! " + Object.keys(failed_files).join(", ")); console.error("!!! " + Object.keys(failed_files).join(", "));
process.exit(1); process.exit(1);
} }
console.log();
var mocha_tests = require("./mocha.js"); require("./mocha.js");
mocha_tests();
/* -----[ utils ]----- */ /* -----[ utils ]----- */
@@ -172,7 +171,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 +185,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 +343,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 +371,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,