Merge branch 'master' into harmony-v3.0.24

This commit is contained in:
alexlamsl
2017-07-08 13:12:54 +08:00
15 changed files with 706 additions and 228 deletions

View File

@@ -653,6 +653,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `booleans` -- various optimizations for boolean context, for example `!!a - `booleans` -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c` ? b : c → a ? b : c`
- `typeofs` -- default `true`. Transforms `typeof foo == "undefined"` into
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues.
- `loops` -- optimizations for `do`, `while` and `for` loops when we can - `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition statically determine the condition
@@ -897,7 +901,6 @@ when this flag is on:
- `new Object()` → `{}` - `new Object()` → `{}`
- `String(exp)` or `exp.toString()` → `"" + exp` - `String(exp)` or `exp.toString()` → `"" + exp`
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new` - `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
- `typeof foo == "undefined"` → `foo === void 0`
- `void 0` → `undefined` (if there is a variable named "undefined" in - `void 0` → `undefined` (if there is a variable named "undefined" in
scope; we do it because the variable name will be mangled, typically scope; we do it because the variable name will be mangled, typically
reduced to a single character) reduced to a single character)
@@ -1050,3 +1053,29 @@ in total it's a bit more than just using UglifyJS's own parser.
[acorn]: https://github.com/ternjs/acorn [acorn]: https://github.com/ternjs/acorn
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k [sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k
### Uglify Fast Minify Mode
It's not well known, but variable and function name mangling accounts for
95% of the size reduction in minified code for most javascript - not
elaborate code transforms. One can simply disable `compress` to speed up
Uglify builds by 3 to 4 times. In this fast `mangle`-only mode Uglify has
comparable minify speeds and gzip sizes to
[`butternut`](https://www.npmjs.com/package/butternut):
| d3.js | minify size | gzip size | minify time (seconds) |
| --- | ---: | ---: | ---: |
| original | 451,131 | 108,733 | - |
| uglify-js@3.0.23 mangle=false, compress=false | 316,600 | 85,245 | 0.73 |
| uglify-js@3.0.23 mangle=true, compress=false | 220,216 | 72,730 | 1.21 |
| Butternut 0.4.6 | 217,568 | 72,738 | 1.81 |
| uglify-js@3.0.23 mangle=true, compress=true | 212,511 | 71,560 | 4.64 |
To enable fast minify mode from the CLI use:
```
uglifyjs file.js -m
```
To enable fast minify mode with the API use:
```js
UglifyJS.minify(code, { compress: false, mangle: true });
```

View File

@@ -1160,7 +1160,7 @@ TreeWalker.prototype = {
if (!ret && descend) { if (!ret && descend) {
descend.call(node); descend.call(node);
} }
this.pop(node); this.pop();
return ret; return ret;
}, },
parent: function(n) { parent: function(n) {
@@ -1179,8 +1179,8 @@ TreeWalker.prototype = {
} }
this.stack.push(node); this.stack.push(node);
}, },
pop: function(node) { pop: function() {
this.stack.pop(); var node = this.stack.pop();
if (node instanceof AST_Lambda || node instanceof AST_Class) { if (node instanceof AST_Lambda || node instanceof AST_Class) {
this.directives = Object.getPrototypeOf(this.directives); this.directives = Object.getPrototypeOf(this.directives);
} }

View File

@@ -82,6 +82,7 @@ function Compressor(options, false_by_default) {
switches : !false_by_default, switches : !false_by_default,
top_retain : null, top_retain : null,
toplevel : !!(options && options["top_retain"]), toplevel : !!(options && options["top_retain"]),
typeofs : !false_by_default,
unsafe : false, unsafe : false,
unsafe_comps : false, unsafe_comps : false,
unsafe_Func : false, unsafe_Func : false,
@@ -850,16 +851,20 @@ merge(Compressor.prototype, {
function has_overlapping_symbol(fn, arg) { function has_overlapping_symbol(fn, arg) {
var found = false; var found = false;
arg.walk(new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
if (found) return true; if (found) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) { if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope; var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) { if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true; if (s === scope) return true;
} }
found = true; return found = true;
} }
})); if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
return found = true;
}
});
arg.walk(tw);
return found; return found;
} }
@@ -874,13 +879,17 @@ merge(Compressor.prototype, {
&& all(iife.args, function(arg) { && all(iife.args, function(arg) {
return !(arg instanceof AST_Expansion); return !(arg instanceof AST_Expansion);
})) { })) {
fn.argnames.forEach(function(sym, i) { var names = Object.create(null);
for (var i = fn.argnames.length; --i >= 0;) {
var sym = fn.argnames[i];
if (sym.name in names) continue;
names[sym.name] = true;
if (sym instanceof AST_Expansion) { if (sym instanceof AST_Expansion) {
var elements = iife.args.slice(i); var elements = iife.args.slice(i);
if (all(elements, function(arg) { if (all(elements, function(arg) {
return !has_overlapping_symbol(fn, arg); return !has_overlapping_symbol(fn, arg);
})) { })) {
candidates.push(make_node(AST_VarDef, sym, { candidates.unshift(make_node(AST_VarDef, sym, {
name: sym.expression, name: sym.expression,
value: make_node(AST_Array, iife, { value: make_node(AST_Array, iife, {
elements: elements elements: elements
@@ -891,12 +900,12 @@ merge(Compressor.prototype, {
var arg = iife.args[i]; var arg = iife.args[i];
if (!arg) arg = make_node(AST_Undefined, sym); if (!arg) arg = make_node(AST_Undefined, sym);
else if (has_overlapping_symbol(fn, arg)) arg = null; else if (has_overlapping_symbol(fn, arg)) arg = null;
if (arg) candidates.push(make_node(AST_VarDef, sym, { if (arg) candidates.unshift(make_node(AST_VarDef, sym, {
name: sym, name: sym,
value: arg value: arg
})); }));
} }
}); }
} }
} }
@@ -1268,21 +1277,21 @@ merge(Compressor.prototype, {
for (var i = 0, len = statements.length; i < len; i++) { for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i]; var stat = statements[i];
if (prev) { if (prev) {
if (stat instanceof AST_For) { if (stat instanceof AST_For && !(stat.init instanceof AST_Definitions)) {
try { var abort = false;
prev.body.walk(new TreeWalker(function(node) { prev.body.walk(new TreeWalker(function(node) {
if (node instanceof AST_Binary && node.operator == "in") if (abort || node instanceof AST_Scope) return true;
throw cons_seq; if (node instanceof AST_Binary && node.operator == "in") {
})); abort = true;
if (stat.init && !(stat.init instanceof AST_Definitions)) { return true;
stat.init = cons_seq(stat.init);
} }
else if (!stat.init) { }));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body.drop_side_effect_free(compressor); stat.init = prev.body.drop_side_effect_free(compressor);
n--; n--;
} }
} catch(ex) {
if (ex !== cons_seq) throw ex;
} }
} }
else if (stat instanceof AST_If) { else if (stat instanceof AST_If) {
@@ -1613,13 +1622,8 @@ merge(Compressor.prototype, {
// descendant of AST_Node. // descendant of AST_Node.
AST_Node.DEFMETHOD("evaluate", function(compressor){ AST_Node.DEFMETHOD("evaluate", function(compressor){
if (!compressor.option("evaluate")) return this; if (!compressor.option("evaluate")) return this;
try {
var val = this._eval(compressor); var val = this._eval(compressor);
return !val || val instanceof RegExp || typeof val != "object" ? val : this; return !val || val instanceof RegExp || typeof val != "object" ? val : this;
} catch(ex) {
if (ex !== def) throw ex;
return this;
}
}); });
var unaryPrefix = makePredicate("! ~ - + void"); var unaryPrefix = makePredicate("! ~ - + void");
AST_Node.DEFMETHOD("is_constant", function(){ AST_Node.DEFMETHOD("is_constant", function(){
@@ -1665,34 +1669,33 @@ merge(Compressor.prototype, {
def(AST_Statement, function(){ def(AST_Statement, function(){
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start)); throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
}); });
def(AST_Lambda, function(){ def(AST_Lambda, return_this);
throw def; def(AST_Class, return_this);
});
def(AST_Class, function() {
throw def;
});
function ev(node, compressor) { function ev(node, compressor) {
if (!compressor) throw new Error("Compressor must be passed"); if (!compressor) throw new Error("Compressor must be passed");
return node._eval(compressor); return node._eval(compressor);
}; };
def(AST_Node, function(){ def(AST_Node, return_this);
throw def; // not constant
});
def(AST_Constant, function(){ def(AST_Constant, function(){
return this.getValue(); return this.getValue();
}); });
def(AST_TemplateString, function() { def(AST_TemplateString, function() {
if (this.segments.length !== 1) throw def; if (this.segments.length !== 1) return this;
return this.segments[0].value; return this.segments[0].value;
}); });
def(AST_Array, function(compressor){ def(AST_Array, function(compressor){
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
return this.elements.map(function(element) { var elements = [];
return ev(element, compressor); for (var i = 0, len = this.elements.length; i < len; i++) {
}); var element = this.elements[i];
var value = ev(element, compressor);
if (element === value) return this;
elements.push(value);
} }
throw def; return elements;
}
return this;
}); });
def(AST_Object, function(compressor){ def(AST_Object, function(compressor){
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
@@ -1704,164 +1707,263 @@ merge(Compressor.prototype, {
key = key.name; key = key.name;
} else if (key instanceof AST_Node) { } else if (key instanceof AST_Node) {
key = ev(key, compressor); key = ev(key, compressor);
if (key === prop.key) return this;
} }
if (typeof Object.prototype[key] === 'function') { if (typeof Object.prototype[key] === 'function') {
throw def; return this;
} }
val[key] = ev(prop.value, compressor); val[key] = ev(prop.value, compressor);
if (val[key] === prop.value) return this;
} }
return val; return val;
} }
throw def; return this;
}); });
def(AST_UnaryPrefix, function(compressor){ def(AST_UnaryPrefix, function(compressor){
var e = this.expression;
switch (this.operator) {
case "!": return !ev(e, compressor);
case "typeof":
// Function would be evaluated to an array and so typeof would // Function would be evaluated to an array and so typeof would
// incorrectly return 'object'. Hence making is a special case. // incorrectly return 'object'. Hence making is a special case.
if (is_func_expr(e)) return typeof function(){}; if (this.operator == "typeof" && is_func_expr(this.expression)) {
return typeof function(){};
e = ev(e, compressor); }
var e = ev(this.expression, compressor);
if (e === this.expression) return this;
switch (this.operator) {
case "!": return !e;
case "typeof":
// typeof <RegExp> returns "object" or "function" on different platforms // typeof <RegExp> returns "object" or "function" on different platforms
// so cannot evaluate reliably // so cannot evaluate reliably
if (e instanceof RegExp) throw def; if (e instanceof RegExp) return this;
return typeof e; return typeof e;
case "void": return void ev(e, compressor); case "void": return void e;
case "~": return ~ev(e, compressor); case "~": return ~e;
case "-": return -ev(e, compressor); case "-": return -e;
case "+": return +ev(e, compressor); case "+": return +e;
} }
throw def; return this;
}); });
def(AST_Binary, function(c){ def(AST_Binary, function(compressor){
var left = this.left, right = this.right, result; var left = ev(this.left, compressor);
if (left === this.left) return this;
var right = ev(this.right, compressor);
if (right === this.right) return this;
var result;
switch (this.operator) { switch (this.operator) {
case "&&" : result = ev(left, c) && ev(right, c); break; case "&&" : result = left && right; break;
case "||" : result = ev(left, c) || ev(right, c); break; case "||" : result = left || right; break;
case "|" : result = ev(left, c) | ev(right, c); break; case "|" : result = left | right; break;
case "&" : result = ev(left, c) & ev(right, c); break; case "&" : result = left & right; break;
case "^" : result = ev(left, c) ^ ev(right, c); break; case "^" : result = left ^ right; break;
case "+" : result = ev(left, c) + ev(right, c); break; case "+" : result = left + right; break;
case "*" : result = ev(left, c) * ev(right, c); break; case "*" : result = left * right; break;
case "**" : result = Math.pow(ev(left, c), ev(right, c)); break; case "**" : result = Math.pow(left, right); break;
case "/" : result = ev(left, c) / ev(right, c); break; case "/" : result = left / right; break;
case "%" : result = ev(left, c) % ev(right, c); break; case "%" : result = left % right; break;
case "-" : result = ev(left, c) - ev(right, c); break; case "-" : result = left - right; break;
case "<<" : result = ev(left, c) << ev(right, c); break; case "<<" : result = left << right; break;
case ">>" : result = ev(left, c) >> ev(right, c); break; case ">>" : result = left >> right; break;
case ">>>" : result = ev(left, c) >>> ev(right, c); break; case ">>>" : result = left >>> right; break;
case "==" : result = ev(left, c) == ev(right, c); break; case "==" : result = left == right; break;
case "===" : result = ev(left, c) === ev(right, c); break; case "===" : result = left === right; break;
case "!=" : result = ev(left, c) != ev(right, c); break; case "!=" : result = left != right; break;
case "!==" : result = ev(left, c) !== ev(right, c); break; case "!==" : result = left !== right; break;
case "<" : result = ev(left, c) < ev(right, c); break; case "<" : result = left < right; break;
case "<=" : result = ev(left, c) <= ev(right, c); break; case "<=" : result = left <= right; break;
case ">" : result = ev(left, c) > ev(right, c); break; case ">" : result = left > right; break;
case ">=" : result = ev(left, c) >= ev(right, c); break; case ">=" : result = left >= right; break;
default: default:
throw def; return this;
} }
if (isNaN(result) && c.find_parent(AST_With)) { if (isNaN(result) && compressor.find_parent(AST_With)) {
// leave original expression as is // leave original expression as is
throw def; return this;
} }
return result; return result;
}); });
def(AST_Conditional, function(compressor){ def(AST_Conditional, function(compressor){
return ev(this.condition, compressor) var condition = ev(this.condition, compressor);
? ev(this.consequent, compressor) if (condition === this.condition) return this;
: ev(this.alternative, compressor); var node = condition ? this.consequent : this.alternative;
var value = ev(node, compressor);
return value === node ? this : value;
}); });
def(AST_SymbolRef, function(compressor){ def(AST_SymbolRef, function(compressor){
if (!compressor.option("reduce_vars") || this._evaluating) throw def; if (!compressor.option("reduce_vars")) return this;
this._evaluating = true;
try {
var fixed = this.fixed_value(); var fixed = this.fixed_value();
if (!fixed) throw def; if (!fixed) return this;
this._eval = return_this;
var value = ev(fixed, compressor); var value = ev(fixed, compressor);
if (value === fixed) {
delete this._eval;
return this;
}
if (!HOP(fixed, "_eval")) fixed._eval = function() { if (!HOP(fixed, "_eval")) fixed._eval = function() {
return value; return value;
}; };
if (value && typeof value == "object" && this.definition().escaped) throw def; if (value && typeof value == "object" && this.definition().escaped) {
return value; delete this._eval;
} finally { return this;
this._evaluating = false;
} }
this._eval = fixed._eval;
return value;
}); });
var global_objs = {
Array: Array,
Boolean: Boolean,
Math: Math,
Number: Number,
RegExp: RegExp,
Object: Object,
String: String,
};
function convert_to_predicate(obj) {
for (var key in obj) {
obj[key] = makePredicate(obj[key]);
}
}
var static_values = {
Math: [
"E",
"LN10",
"LN2",
"LOG2E",
"LOG10E",
"PI",
"SQRT1_2",
"SQRT2",
],
Number: [
"MAX_VALUE",
"MIN_VALUE",
"NaN",
"NEGATIVE_INFINITY",
"POSITIVE_INFINITY",
],
};
convert_to_predicate(static_values);
def(AST_PropAccess, function(compressor){ def(AST_PropAccess, function(compressor){
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
var key = this.property; var key = this.property;
if (key instanceof AST_Node) { if (key instanceof AST_Node) {
key = ev(key, compressor); key = ev(key, compressor);
if (key === this.property) return this;
}
var exp = this.expression;
var val;
if (exp instanceof AST_SymbolRef && exp.undeclared()) {
if (!(static_values[exp.name] || return_false)(key)) return this;
val = global_objs[exp.name];
} else {
val = ev(exp, compressor);
if (!val || val === exp || !HOP(val, key)) return this;
} }
var val = ev(this.expression, compressor);
if (val && HOP(val, key)) {
return val[key]; return val[key];
} }
} return this;
throw def;
}); });
var object_fns = [ var object_fns = [
'constructor', "constructor",
'toString', "toString",
'valueOf', "valueOf",
]; ];
var native_fns = { var native_fns = {
Array: makePredicate([ Array: [
'indexOf', "indexOf",
'join', "join",
'lastIndexOf', "lastIndexOf",
'slice', "slice",
].concat(object_fns)), ].concat(object_fns),
Boolean: makePredicate(object_fns), Boolean: object_fns,
Number: makePredicate([ Number: [
'toExponential', "toExponential",
'toFixed', "toFixed",
'toPrecision', "toPrecision",
].concat(object_fns)), ].concat(object_fns),
RegExp: makePredicate([ RegExp: [
'test', "test",
].concat(object_fns)), ].concat(object_fns),
String: makePredicate([ String: [
'charAt', "charAt",
'charCodeAt', "charCodeAt",
'concat', "concat",
'indexOf', "indexOf",
'italics', "italics",
'lastIndexOf', "lastIndexOf",
'match', "match",
'replace', "replace",
'search', "search",
'slice', "slice",
'split', "split",
'substr', "substr",
'substring', "substring",
'trim', "trim",
].concat(object_fns)), ].concat(object_fns),
}; };
convert_to_predicate(native_fns);
var static_fns = {
Array: [
"isArray",
],
Math: [
"abs",
"acos",
"asin",
"atan",
"ceil",
"cos",
"exp",
"floor",
"log",
"round",
"sin",
"sqrt",
"tan",
"atan2",
"pow",
"max",
"min"
],
Number: [
"isFinite",
"isNaN",
],
Object: [
"keys",
"getOwnPropertyNames",
],
String: [
"fromCharCode",
],
};
convert_to_predicate(static_fns);
def(AST_Call, function(compressor){ def(AST_Call, function(compressor){
var exp = this.expression; var exp = this.expression;
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) { if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
var key = exp.property; var key = exp.property;
if (key instanceof AST_Node) { if (key instanceof AST_Node) {
key = ev(key, compressor); key = ev(key, compressor);
if (key === exp.property) return this;
} }
var val = ev(exp.expression, compressor); var val;
if ((val && native_fns[val.constructor.name] || return_false)(key)) { var e = exp.expression;
return val[key].apply(val, this.args.map(function(arg) { if (e instanceof AST_SymbolRef && e.undeclared()) {
return ev(arg, compressor); if (!(static_fns[e.name] || return_false)(key)) return this;
})); val = global_objs[e.name];
} else {
val = ev(e, compressor);
if (val === e || !(val && native_fns[val.constructor.name] || return_false)(key)) return this;
} }
var args = [];
for (var i = 0, len = this.args.length; i < len; i++) {
var arg = this.args[i];
var value = ev(arg, compressor);
if (arg === value) return this;
args.push(value);
} }
throw def; return val[key].apply(val, args);
}); }
def(AST_New, function(compressor){ return this;
throw def;
}); });
def(AST_New, return_this);
})(function(node, func){ })(function(node, func){
node.DEFMETHOD("_eval", func); node.DEFMETHOD("_eval", func);
}); });
@@ -3782,7 +3884,8 @@ merge(Compressor.prototype, {
case "==": case "==":
case "!=": case "!=":
// "undefined" == typeof x => undefined === x // "undefined" == typeof x => undefined === x
if (self.left instanceof AST_String if (compressor.option("typeofs")
&& self.left instanceof AST_String
&& self.left.value == "undefined" && self.left.value == "undefined"
&& self.right instanceof AST_UnaryPrefix && self.right instanceof AST_UnaryPrefix
&& self.right.operator == "typeof") { && self.right.operator == "typeof") {
@@ -4510,6 +4613,17 @@ merge(Compressor.prototype, {
return self; return self;
}); });
AST_Lambda.DEFMETHOD("contains_this", function() {
var result;
var self = this;
self.walk(new TreeWalker(function(node) {
if (result) return true;
if (node instanceof AST_This) return result = true;
if (node !== self && node instanceof AST_Scope && !(node instanceof AST_Arrow)) return true;
}));
return result;
});
OPT(AST_Dot, function(self, compressor){ OPT(AST_Dot, function(self, compressor){
var def = self.resolve_defines(compressor); var def = self.resolve_defines(compressor);
if (def) { if (def) {
@@ -4524,6 +4638,20 @@ merge(Compressor.prototype, {
}) })
}).optimize(compressor); }).optimize(compressor);
} }
if (compressor.option("unsafe") && self.expression instanceof AST_Object) {
var values = self.expression.properties;
for (var i = values.length; --i >= 0;) {
if (values[i].key === prop) {
var value = values[i].value;
if (value instanceof AST_Function ? !value.contains_this() : !value.has_side_effects(compressor)) {
var obj = self.expression.clone();
obj.properties = obj.properties.slice();
obj.properties.splice(i, 1);
return make_sequence(self, [ obj, value ]).optimize(compressor);
}
}
}
}
if (compressor.option("unsafe_proto") if (compressor.option("unsafe_proto")
&& self.expression instanceof AST_Dot && self.expression instanceof AST_Dot
&& self.expression.property == "prototype") { && self.expression.property == "prototype") {
@@ -4656,17 +4784,6 @@ merge(Compressor.prototype, {
return self; return self;
}); });
AST_Lambda.DEFMETHOD("contains_this", function() {
var result;
var self = this;
self.walk(new TreeWalker(function(node) {
if (result) return true;
if (node instanceof AST_This) return result = true;
if (node !== self && node instanceof AST_Scope && !(node instanceof AST_Arrow)) return true;
}));
return result;
});
OPT(AST_ConciseMethod, function(self, compressor){ OPT(AST_ConciseMethod, function(self, compressor){
// p(){return x;} ---> p:()=>x // p(){return x;} ---> p:()=>x
if (compressor.option("arrows") if (compressor.option("arrows")

View File

@@ -745,14 +745,15 @@ function OutputStream(options) {
// parens around it too, otherwise the call will be // parens around it too, otherwise the call will be
// interpreted as passing the arguments to the upper New // interpreted as passing the arguments to the upper New
// expression. // expression.
try { var parens = false;
this.walk(new TreeWalker(function(node) { this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Call) throw p; if (parens || node instanceof AST_Scope) return true;
})); if (node instanceof AST_Call) {
} catch(ex) { parens = true;
if (ex !== p) throw ex;
return true; return true;
} }
}));
return parens;
} }
}); });
@@ -1369,19 +1370,17 @@ function OutputStream(options) {
}); });
function parenthesize_for_noin(node, output, noin) { function parenthesize_for_noin(node, output, noin) {
if (!noin) node.print(output); var parens = false;
else try {
// need to take some precautions here: // need to take some precautions here:
// https://github.com/mishoo/UglifyJS2/issues/60 // https://github.com/mishoo/UglifyJS2/issues/60
node.walk(new TreeWalker(function(node){ if (noin) node.walk(new TreeWalker(function(node) {
if (node instanceof AST_Binary && node.operator == "in") if (parens || node instanceof AST_Scope) return true;
throw output; if (node instanceof AST_Binary && node.operator == "in") {
})); parens = true;
node.print(output); return true;
} catch(ex) {
if (ex !== output) throw ex;
node.print(output, true);
} }
}));
node.print(output, parens);
}; };
DEFPRINT(AST_VarDef, function(self, output){ DEFPRINT(AST_VarDef, function(self, output){

View File

@@ -70,7 +70,7 @@ TreeTransformer.prototype = new TreeWalker;
if (y !== undefined) x = y; if (y !== undefined) x = y;
} }
} }
tw.pop(this); tw.pop();
return x; return x;
}); });
}; };

View File

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"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.0.23", "version": "3.0.24",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -2400,3 +2400,89 @@ issue_2187_3: {
} }
expect_stdout: "1" expect_stdout: "1"
} }
issue_2203_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return function(c) {
return c.a;
}((String, (Object, this)));
}
}.b());
}
expect: {
a = "FAIL";
console.log({
a: "PASS",
b: function() {
return function(c) {
return c.a;
}((String, (Object, this)));
}
}.b());
}
expect_stdout: "PASS"
}
issue_2203_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
a = "PASS";
console.log({
a: "FAIL",
b: function() {
return function(c) {
return c.a;
}((String, (Object, function() {
return this;
}())));
}
}.b());
}
expect: {
a = "PASS";
console.log({
a: "FAIL",
b: function() {
return function(c) {
return (String, (Object, function() {
return this;
}())).a;
}();
}
}.b());
}
expect_stdout: "PASS"
}
duplicate_argname: {
options = {
collapse_vars: true,
unused: true,
}
input: {
function f() { return "PASS"; }
console.log(function(a, a) {
f++;
return a;
}("FAIL", f()));
}
expect: {
function f() { return "PASS"; }
console.log(function(a, a) {
f++;
return a;
}("FAIL", f()));
}
expect_stdout: "PASS"
}

View File

@@ -345,21 +345,25 @@ unsafe_constant: {
unsafe_object: { unsafe_object: {
options = { options = {
evaluate: true, evaluate: true,
unsafe : true reduce_vars: true,
toplevel: true,
unsafe: true,
} }
input: { input: {
var o = { a: 1 };
console.log( console.log(
({a:1}) + 1, o + 1,
({a:1}).a + 1, o.a + 1,
({a:1}).b + 1, o.b + 1,
({a:1}).a.b + 1 o.a.b + 1
); );
} }
expect: { expect: {
var o = { a: 1 };
console.log( console.log(
({a:1}) + 1, o + 1,
2, 2,
({a:1}).b + 1, o.b + 1,
1..b + 1 1..b + 1
); );
} }
@@ -369,21 +373,25 @@ unsafe_object: {
unsafe_object_nested: { unsafe_object_nested: {
options = { options = {
evaluate: true, evaluate: true,
unsafe : true reduce_vars: true,
toplevel: true,
unsafe: true,
} }
input: { input: {
var o = { a: { b: 1 } };
console.log( console.log(
({a:{b:1}}) + 1, o + 1,
({a:{b:1}}).a + 1, o.a + 1,
({a:{b:1}}).b + 1, o.b + 1,
({a:{b:1}}).a.b + 1 o.a.b + 1
); );
} }
expect: { expect: {
var o = { a: { b: 1 } };
console.log( console.log(
({a:{b:1}}) + 1, o + 1,
({a:{b:1}}).a + 1, o.a + 1,
({a:{b:1}}).b + 1, o.b + 1,
2 2
); );
} }
@@ -393,20 +401,24 @@ unsafe_object_nested: {
unsafe_object_complex: { unsafe_object_complex: {
options = { options = {
evaluate: true, evaluate: true,
unsafe : true reduce_vars: true,
toplevel: true,
unsafe: true,
} }
input: { input: {
var o = { a: { b: 1 }, b: 1 };
console.log( console.log(
({a:{b:1},b:1}) + 1, o + 1,
({a:{b:1},b:1}).a + 1, o.a + 1,
({a:{b:1},b:1}).b + 1, o.b + 1,
({a:{b:1},b:1}).a.b + 1 o.a.b + 1
); );
} }
expect: { expect: {
var o = { a: { b: 1 }, b: 1 };
console.log( console.log(
({a:{b:1},b:1}) + 1, o + 1,
({a:{b:1},b:1}).a + 1, o.a + 1,
2, 2,
2 2
); );
@@ -417,21 +429,25 @@ unsafe_object_complex: {
unsafe_object_repeated: { unsafe_object_repeated: {
options = { options = {
evaluate: true, evaluate: true,
unsafe : true reduce_vars: true,
toplevel: true,
unsafe: true,
} }
input: { input: {
var o = { a: { b: 1 }, a: 1 };
console.log( console.log(
({a:{b:1},a:1}) + 1, o + 1,
({a:{b:1},a:1}).a + 1, o.a + 1,
({a:{b:1},a:1}).b + 1, o.b + 1,
({a:{b:1},a:1}).a.b + 1 o.a.b + 1
); );
} }
expect: { expect: {
var o = { a: { b: 1 }, a: 1 };
console.log( console.log(
({a:{b:1},a:1}) + 1, o + 1,
2, 2,
({a:{b:1},a:1}).b + 1, o.b + 1,
1..b + 1 1..b + 1
); );
} }
@@ -480,9 +496,9 @@ unsafe_function: {
expect: { expect: {
console.log( console.log(
({a:{b:1},b:function(){}}) + 1, ({a:{b:1},b:function(){}}) + 1,
({a:{b:1},b:function(){}}).a + 1, ({b:function(){}}, {b:1}) + 1,
({a:{b:1},b:function(){}}).b + 1, ({a:{b:1}}, function(){}) + 1,
({a:{b:1},b:function(){}}).a.b + 1 ({b:function(){}}, {b:1}).b + 1
); );
} }
expect_stdout: true expect_stdout: true
@@ -730,8 +746,8 @@ unsafe_prototype_function: {
var d = ({toString: 0}) + ""; var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2]; var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2]; var f = (({toString: 0}) + "")[2];
var g = ({valueOf: 0}).valueOf(); var g = ({}, 0)();
var h = "" + ({toString: 0}); var h = ({}, 0)();
} }
} }
@@ -1162,3 +1178,75 @@ string_charCodeAt: {
} }
expect_stdout: "NaN" expect_stdout: "NaN"
} }
issue_2207_1: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(String.fromCharCode(65));
console.log(Math.max(3, 6, 2, 7, 3, 4));
console.log(Math.cos(1.2345));
console.log(Math.cos(1.2345) - Math.sin(4.321));
console.log(Math.pow(Math.PI, Math.E - Math.LN10));
}
expect: {
console.log("A");
console.log(7);
console.log(Math.cos(1.2345));
console.log(1.2543732512566947);
console.log(1.6093984514472044);
}
expect_stdout: true
}
issue_2207_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect_stdout: true
}
issue_2207_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);
console.log(Number.NaN);
console.log(Number.NEGATIVE_INFINITY);
console.log(Number.POSITIVE_INFINITY);
}
expect: {
console.log(Number.MAX_VALUE);
console.log(5e-324);
console.log(NaN);
console.log(-1/0);
console.log(1/0);
}
expect_stdout: true
}

View File

@@ -37,6 +37,7 @@ object: {
VALUE: 42, VALUE: 42,
}, },
}, },
side_effects: true,
unsafe: true, unsafe: true,
} }
input: { input: {
@@ -140,9 +141,9 @@ mixed: {
console.log(CONFIG); console.log(CONFIG);
} }
expect_warnings: [ expect_warnings: [
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:126,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]', 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]', 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:128,22]',
'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:130,8]',
] ]
} }

View File

@@ -1,6 +1,7 @@
typeof_eq_undefined: { typeof_eq_undefined: {
options = { options = {
comparisons: true comparisons: true,
typeofs: true,
} }
input: { input: {
var a = typeof b != "undefined"; var a = typeof b != "undefined";
@@ -24,6 +25,7 @@ typeof_eq_undefined_ie8: {
options = { options = {
comparisons: true, comparisons: true,
ie8: true, ie8: true,
typeofs: true,
} }
input: { input: {
var a = typeof b != "undefined"; var a = typeof b != "undefined";
@@ -45,7 +47,8 @@ typeof_eq_undefined_ie8: {
undefined_redefined: { undefined_redefined: {
options = { options = {
comparisons: true comparisons: true,
typeofs: true,
} }
input: { input: {
function f(undefined) { function f(undefined) {
@@ -58,7 +61,8 @@ undefined_redefined: {
undefined_redefined_mangle: { undefined_redefined_mangle: {
options = { options = {
comparisons: true comparisons: true,
typeofs: true,
} }
mangle = {} mangle = {}
input: { input: {

View File

@@ -480,3 +480,17 @@ do_switch: {
} while (false); } while (false);
} }
} }
in_parenthesis_1: {
input: {
for (("foo" in {});0;);
}
expect_exact: 'for(("foo"in{});0;);'
}
in_parenthesis_2: {
input: {
for ((function(){ "foo" in {}; });0;);
}
expect_exact: 'for(function(){"foo"in{}};0;);'
}

View File

@@ -98,3 +98,19 @@ new_with_assignement_expression: {
new y([a, b] = [3, 4]); new y([a, b] = [3, 4]);
} }
} }
dot_parenthesis_1: {
input: {
console.log(new (Math.random().constructor) instanceof Number);
}
expect_exact: "console.log(new(Math.random().constructor)instanceof Number);"
expect_stdout: "true"
}
dot_parenthesis_2: {
input: {
console.log(typeof new function(){Math.random()}.constructor);
}
expect_exact: "console.log(typeof new function(){Math.random()}.constructor);"
expect_stdout: "function"
}

View File

@@ -659,3 +659,116 @@ accessor_this: {
expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);' expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);'
expect_stdout: "1 2 2" expect_stdout: "1 2 2"
} }
issue_2208_1: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
p: function() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
}
issue_2208_2: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
a: 42,
p: function() {
return this.a;
}
}.p());
}
expect: {
console.log({
a: 42,
p: function() {
return this.a;
}
}.p());
}
expect_stdout: "42"
}
issue_2208_3: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
a = 42;
console.log({
p: function() {
return function() {
return this.a;
}();
}
}.p());
}
expect: {
a = 42;
console.log(function() {
return this.a;
}());
}
expect_stdout: "42"
}
issue_2208_4: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
function foo() {}
console.log({
a: foo(),
p: function() {
return 42;
}
}.p());
}
expect: {
function foo() {}
console.log((foo(), function() {
return 42;
})());
}
expect_stdout: "42"
}
issue_2208_5: {
options = {
inline: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
p: "FAIL",
p: function() {
return 42;
}
}.p());
}
expect: {
console.log(42);
}
expect_stdout: "42"
}

View File

@@ -1469,6 +1469,7 @@ issue_1670_1: {
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
switches: true, switches: true,
typeofs: true,
unused: true, unused: true,
} }
input: { input: {
@@ -1532,6 +1533,7 @@ issue_1670_3: {
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
switches: true, switches: true,
typeofs: true,
unused: true, unused: true,
} }
input: { input: {

View File

@@ -176,6 +176,11 @@ for_sequences: {
// 4 // 4
x = (foo in bar); x = (foo in bar);
for (y = 5; false;); for (y = 5; false;);
// 5
x = function() {
foo in bar;
};
for (y = 5; false;);
} }
expect: { expect: {
// 1 // 1
@@ -188,6 +193,10 @@ for_sequences: {
// 4 // 4
x = (foo in bar); x = (foo in bar);
for (y = 5; false;); for (y = 5; false;);
// 5
for (x = function() {
foo in bar;
}, y = 5; false;);
} }
} }