Compare commits

...

33 Commits

Author SHA1 Message Date
Alex Lam S.L
c14d09ba84 v3.3.26 2018-05-20 17:32:32 +00:00
Alex Lam S.L
4fc39d8dad fix corner case in collapse_vars (#3139) 2018-05-19 05:45:14 +08:00
exvisory
0b7c70f726 Update README.md to clarify --source-map filename option (#3137)
Clarify that the --source-map filename option does NOT change the source map output filename but does set the 'file' attribute within the output source map. This was already documented in the API section of the README so I just copied that to the CLI options section, and a fragment to the CLI summary.
2018-05-18 15:50:36 +08:00
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 1335 additions and 386 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

@@ -118,7 +118,8 @@ a double dash to prevent input files being used as option arguments:
JS that was generated from some other original JS that was generated from some other original
code. Specify "inline" if the source map is code. Specify "inline" if the source map is
included within the sources. included within the sources.
`filename` Name and/or location of the output source. `filename` Filename and/or location of the output source
(sets `file` attribute in source map).
`includeSources` Pass this flag if you want to include `includeSources` Pass this flag if you want to include
the content of source files in the the content of source files in the
source map as sourcesContent property. source map as sourcesContent property.
@@ -149,7 +150,9 @@ debugging your compressed JavaScript. To get a source map, pass
Additional options: Additional options:
- `--source-map "filename='<NAME>'"` to specify the name of the source map. - `--source-map "filename='<NAME>'"` to specify the name of the source map. The value of
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
in source map file.
- `--source-map "root='<URL>'"` to pass the URL where the original files can be found. - `--source-map "root='<URL>'"` to pass the URL where the original files can be found.
@@ -1041,8 +1044,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,53 @@ merge(Compressor.prototype, {
} }
} }
function should_stop(node, parent) {
if (parent instanceof AST_For) return node !== parent.init;
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 (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 +1464,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 +1535,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 +1597,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 +3362,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 +4488,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 +4706,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 +5016,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 +5044,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 +5076,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 +5363,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 +5402,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.26",
"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,
@@ -4901,6 +4931,27 @@ collapse_rhs_lhs_2: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
collapse_rhs_loop: {
options = {
collapse_vars: true,
}
input: {
var s;
s = "<tpl>PASS</tpl>";
for (var m, r = /<tpl>(.*)<\/tpl>/; m = s.match(r);)
s = s.replace(m[0], m[1]);
console.log(s);
}
expect: {
var s;
s = "<tpl>PASS</tpl>";
for (var m, r = /<tpl>(.*)<\/tpl>/; m = s.match(r);)
s = s.replace(m[0], m[1]);
console.log(s);
}
expect_stdout: "PASS"
}
collapse_rhs_side_effects: { collapse_rhs_side_effects: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -5327,3 +5378,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,