Compare commits

...

25 Commits

Author SHA1 Message Date
Alex Lam S.L
9f67866147 v3.12.0 2020-11-23 01:10:39 +08:00
Alex Lam S.L
645d5a348b workaround Safari quirks (#4314)
fixes #1753
2020-11-21 10:30:46 +08:00
Alex Lam S.L
cf120c7cea fix corner case in merge_vars & reduce_vars (#4313)
fixes #4312
2020-11-21 08:57:59 +08:00
Alex Lam S.L
8d30902ba9 fix corner case in mangle (#4311) 2020-11-21 08:05:40 +08:00
Alex Lam S.L
02459cddf9 gate galio workaround (#4310) 2020-11-21 03:37:33 +08:00
Alex Lam S.L
1b579779be fix corner case in collapse_vars (#4309)
fixes #4308
2020-11-20 06:23:37 +08:00
Alex Lam S.L
b18b70f63b fix corner case in hoist_props (#4307) 2020-11-20 00:02:25 +08:00
Alex Lam S.L
641406d491 fix corner cases in reduce_vars & unused (#4306) 2020-11-19 11:25:36 +08:00
Alex Lam S.L
134ef0b1eb fix corner case in dead_code (#4304) 2020-11-19 08:34:55 +08:00
Alex Lam S.L
db87dcf13e enhance varify (#4303) 2020-11-19 07:58:33 +08:00
Alex Lam S.L
aecbabc587 fix corner case in merge_vars (#4302)
fixes #4301
2020-11-19 05:44:47 +08:00
Alex Lam S.L
fd6544b340 fix corner case reduce_vars (#4300)
fixes #4297
2020-11-19 01:11:28 +08:00
Alex Lam S.L
f6a83f7944 fix corner case in merge_vars (#4299)
fixes #4298
2020-11-18 23:43:55 +08:00
Alex Lam S.L
35283e5dd1 enhance arguments (#4296) 2020-11-18 11:39:32 +08:00
Alex Lam S.L
7a51c17ff0 fix corner case in merge_vars (#4295)
fixes #4294
2020-11-18 09:32:53 +08:00
Alex Lam S.L
aff842f2f9 fix corner case in arguments (#4293)
fixes #4291
2020-11-18 08:54:58 +08:00
Alex Lam S.L
0bedd031da fix corner cases in collapse_vars, unused & varify (#4292)
fixes #4290
2020-11-18 08:22:54 +08:00
Alex Lam S.L
caa92aea5d fix corner case in merge_vars (#4289)
fixes #4288
2020-11-18 04:03:20 +08:00
Alex Lam S.L
383163afa6 fix corner case in collapse_vars (#4287)
fixes #4286
2020-11-17 18:03:31 +08:00
Alex Lam S.L
8a83c8dd46 fix corner cases in collapse_vars & dead_code (#4285)
fixes #4284
2020-11-17 16:23:50 +08:00
Alex Lam S.L
2a612fd472 fix corner case in reduce_vars (#4283)
fixes #4282
2020-11-17 14:43:04 +08:00
Alex Lam S.L
b9798a01a8 fix corner case in reduce_vars (#4281)
fixes #4280
2020-11-17 12:59:44 +08:00
Alex Lam S.L
6dbacb5e3f enhance varify (#4279) 2020-11-17 12:35:00 +08:00
Alex Lam S.L
e5f80afc53 support destructured literals (#4278) 2020-11-17 08:01:24 +08:00
Alex Lam S.L
42e34c870a fix corner case in unused (#4277)
fixes #4276
2020-11-17 02:06:00 +08:00
21 changed files with 3729 additions and 426 deletions

View File

@@ -135,6 +135,10 @@ a double dash to prevent input files being used as option arguments:
--toplevel Compress and/or mangle variables in top level scope. --toplevel Compress and/or mangle variables in top level scope.
--verbose Print diagnostic messages. --verbose Print diagnostic messages.
--warn Print warning messages. --warn Print warning messages.
--webkit Support non-standard Safari/Webkit.
Equivalent to setting `webkit: true` in `minify()`
for `mangle` and `output` options.
By default UglifyJS will not try to be Safari-proof.
--wrap <name> Embed everything in a big function, making the --wrap <name> Embed everything in a big function, making the
“exports” and “global” variables available. You “exports” and “global” variables available. You
need to pass an argument to this option to need to pass an argument to this option to
@@ -519,6 +523,9 @@ if (result.error) throw result.error;
- `warnings` (default `false`) — pass `true` to return compressor warnings - `warnings` (default `false`) — pass `true` to return compressor warnings
in `result.warnings`. Use the value `"verbose"` for more detailed warnings. in `result.warnings`. Use the value `"verbose"` for more detailed warnings.
- `webkit` (default `false`) -- enable workarounds for Safari/WebKit bugs.
PhantomJS users should set this option to `true`.
## Minify options structure ## Minify options structure
```javascript ```javascript
@@ -868,6 +875,8 @@ can pass additional arguments that control the code output:
} }
``` ```
- `galio` (default `false`) -- enable workarounds for ANT Galio bugs
- `indent_level` (default `4`) - `indent_level` (default `4`)
- `indent_start` (default `0`) -- prefix all lines by that many spaces - `indent_start` (default `0`) -- prefix all lines by that many spaces
@@ -906,8 +915,7 @@ can pass additional arguments that control the code output:
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts) - `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
- `webkit` (default `false`) -- enable workarounds for WebKit bugs. - `v8` (default `false`) -- enable workarounds for Chrome & Node.js bugs
PhantomJS users should set this option to `true`.
- `width` (default `80`) -- only takes effect when beautification is on, this - `width` (default `80`) -- only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to specifies an (orientative) line width that the beautifier will try to
@@ -1138,7 +1146,7 @@ To enable fast minify mode with the API use:
UglifyJS.minify(code, { compress: false, mangle: true }); UglifyJS.minify(code, { compress: false, mangle: true });
``` ```
#### Source maps and debugging ### Source maps and debugging
Various `compress` transforms that simplify, rearrange, inline and remove code Various `compress` transforms that simplify, rearrange, inline and remove code
are known to have an adverse effect on debugging with source maps. This is are known to have an adverse effect on debugging with source maps. This is
@@ -1150,6 +1158,10 @@ disable the Uglify `compress` option and just use `mangle`.
To allow for better optimizations, the compiler makes various assumptions: To allow for better optimizations, the compiler makes various assumptions:
- The code does not rely on preserving its runtime performance characteristics.
Typically uglified code will run faster due to less instructions and easier
inlining, but may be slower on rare occasions for a specific platform, e.g.
see [`reduce_funcs`](#compress-options).
- `.toString()` and `.valueOf()` don't have side effects, and for built-in - `.toString()` and `.valueOf()` don't have side effects, and for built-in
objects they have not been overridden. objects they have not been overridden.
- `undefined`, `NaN` and `Infinity` have not been externally redefined. - `undefined`, `NaN` and `Infinity` have not been externally redefined.
@@ -1177,3 +1189,7 @@ To allow for better optimizations, the compiler makes various assumptions:
top.B = "PASS"; top.B = "PASS";
console.log(B); console.log(B);
``` ```
- Use of `arguments` alongside destructuring as function parameters, e.g.
`function({}, arguments) {}` will result in `SyntaxError` in earlier versions
of Chrome and Node.js - UglifyJS may modify the input which in turn may
suppress those errors.

View File

@@ -111,6 +111,7 @@ function process_option(name, no_value) {
" --validate Perform validation during AST manipulations.", " --validate Perform validation during AST manipulations.",
" --verbose Print diagnostic messages.", " --verbose Print diagnostic messages.",
" --warn Print warning messages.", " --warn Print warning messages.",
" --webkit Support non-standard Safari/Webkit.",
" --wrap <name> Embed everything as a function with “exports” corresponding to “name” globally.", " --wrap <name> Embed everything as a function with “exports” corresponding to “name” globally.",
" --reduce-test Reduce a standalone test case (assumes cloned repository).", " --reduce-test Reduce a standalone test case (assumes cloned repository).",
].join("\n")); ].join("\n"));
@@ -142,6 +143,7 @@ function process_option(name, no_value) {
case "timings": case "timings":
case "toplevel": case "toplevel":
case "validate": case "validate":
case "webkit":
options[name] = true; options[name] = true;
break; break;
case "keep-fnames": case "keep-fnames":

View File

@@ -414,8 +414,12 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
_validate: function() { _validate: function() {
if (this.init instanceof AST_Definitions) { if (this.init instanceof AST_Definitions) {
if (this.init.definitions.length != 1) throw new Error("init must have single declaration"); if (this.init.definitions.length != 1) throw new Error("init must have single declaration");
} else if (!(this.init instanceof AST_PropAccess || this.init instanceof AST_SymbolRef)) { } else {
throw new Error("init must be assignable"); validate_destructured(this.init, function(node) {
if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) {
throw new Error("init must be assignable: " + node.TYPE);
}
});
} }
must_be_expression(this, "object"); must_be_expression(this, "object");
}, },
@@ -496,12 +500,24 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
} }
}, AST_Scope); }, AST_Scope);
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", { var AST_Lambda = DEFNODE("Lambda", "name argnames length_read uses_arguments", {
$documentation: "Base class for functions", $documentation: "Base class for functions",
$propdoc: { $propdoc: {
name: "[AST_SymbolDeclaration?] the name of this function", name: "[AST_SymbolDeclaration?] the name of this function",
argnames: "[AST_SymbolFunarg*] array of function arguments", argnames: "[(AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
},
each_argname: function(visit) {
var tw = new TreeWalker(function(node) {
if (node instanceof AST_DestructuredKeyVal) {
node.value.walk(tw);
return true;
}
if (node instanceof AST_SymbolFunarg) visit(node);
});
this.argnames.forEach(function(argname) {
argname.walk(tw);
});
}, },
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
@@ -515,8 +531,10 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", {
}, },
_validate: function() { _validate: function() {
this.argnames.forEach(function(node) { this.argnames.forEach(function(node) {
validate_destructured(node, function(node) {
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]"); if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
}); });
});
}, },
}, AST_Scope); }, AST_Scope);
@@ -748,8 +766,10 @@ var AST_Const = DEFNODE("Const", null, {
_validate: function() { _validate: function() {
this.definitions.forEach(function(node) { this.definitions.forEach(function(node) {
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
if (!(node.name instanceof AST_SymbolConst)) throw new Error("name must be AST_SymbolConst"); validate_destructured(node.name, function(node) {
must_be_expression(node, "value"); if (!(node instanceof AST_SymbolConst)) throw new Error("name must be AST_SymbolConst");
});
if (node.value != null) must_be_expression(node, "value");
}); });
}, },
}, AST_Definitions); }, AST_Definitions);
@@ -759,7 +779,9 @@ var AST_Let = DEFNODE("Let", null, {
_validate: function() { _validate: function() {
this.definitions.forEach(function(node) { this.definitions.forEach(function(node) {
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
if (!(node.name instanceof AST_SymbolLet)) throw new Error("name must be AST_SymbolLet"); validate_destructured(node.name, function(node) {
if (!(node instanceof AST_SymbolLet)) throw new Error("name must be AST_SymbolLet");
});
if (node.value != null) must_be_expression(node, "value"); if (node.value != null) must_be_expression(node, "value");
}); });
}, },
@@ -770,7 +792,9 @@ var AST_Var = DEFNODE("Var", null, {
_validate: function() { _validate: function() {
this.definitions.forEach(function(node) { this.definitions.forEach(function(node) {
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
if (!(node.name instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar"); validate_destructured(node.name, function(node) {
if (!(node instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar");
});
if (node.value != null) must_be_expression(node, "value"); if (node.value != null) must_be_expression(node, "value");
}); });
}, },
@@ -969,6 +993,14 @@ var AST_Assign = DEFNODE("Assign", null, {
$documentation: "An assignment expression — `a = b + 5`", $documentation: "An assignment expression — `a = b + 5`",
_validate: function() { _validate: function() {
if (this.operator.indexOf("=") < 0) throw new Error('operator must contain "="'); if (this.operator.indexOf("=") < 0) throw new Error('operator must contain "="');
if (this.left instanceof AST_Destructured) {
if (this.operator != "=") throw new Error("invalid destructuring operator: " + this.operator);
validate_destructured(this.left, function(node) {
if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) {
throw new Error("left must be assignable: " + node.TYPE);
}
});
}
}, },
}, AST_Binary); }, AST_Binary);
@@ -992,6 +1024,77 @@ var AST_Array = DEFNODE("Array", "elements", {
}, },
}); });
var AST_Destructured = DEFNODE("Destructured", null, {
$documentation: "Base class for destructured literal",
});
function validate_destructured(node, check) {
if (node instanceof AST_DestructuredArray) return node.elements.forEach(function(node) {
if (!(node instanceof AST_Hole)) validate_destructured(node, check);
});
if (node instanceof AST_DestructuredObject) return node.properties.forEach(function(prop) {
validate_destructured(prop.value, check);
});
check(node);
}
var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
$documentation: "A destructured array literal",
$propdoc: {
elements: "[AST_Node*] array of elements",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.elements.forEach(function(element) {
element.walk(visitor);
});
});
},
}, AST_Destructured);
var AST_DestructuredKeyVal = DEFNODE("DestructuredKeyVal", "key value", {
$documentation: "A key: value destructured property",
$propdoc: {
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
value: "[AST_Node] property value",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.key instanceof AST_Node) node.key.walk(visitor);
node.value.walk(visitor);
});
},
_validate: function() {
if (typeof this.key != "string") {
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
must_be_expression(this, "key");
}
must_be_expression(this, "value");
},
});
var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", {
$documentation: "A destructured object literal",
$propdoc: {
properties: "[AST_DestructuredKeyVal*] array of properties",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.properties.forEach(function(prop) {
prop.walk(visitor);
});
});
},
_validate: function() {
this.properties.forEach(function(node) {
if (!(node instanceof AST_DestructuredKeyVal)) throw new Error("properties must be AST_DestructuredKeyVal[]");
});
},
}, AST_Destructured);
var AST_Object = DEFNODE("Object", "properties", { var AST_Object = DEFNODE("Object", "properties", {
$documentation: "An object literal", $documentation: "An object literal",
$propdoc: { $propdoc: {

File diff suppressed because it is too large Load Diff

View File

@@ -89,6 +89,7 @@ function minify(files, options) {
toplevel: false, toplevel: false,
validate: false, validate: false,
warnings: false, warnings: false,
webkit: false,
wrap: false, wrap: false,
}, true); }, true);
if (options.validate) AST_Node.enable_validation(); if (options.validate) AST_Node.enable_validation();
@@ -101,6 +102,7 @@ function minify(files, options) {
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]);
set_shorthand("webkit", options, [ "mangle", "output" ]);
var quoted_props; var quoted_props;
if (options.mangle) { if (options.mangle) {
options.mangle = defaults(options.mangle, { options.mangle = defaults(options.mangle, {
@@ -111,6 +113,7 @@ function minify(files, options) {
properties: false, properties: false,
reserved: [], reserved: [],
toplevel: false, toplevel: false,
webkit: false,
}, true); }, true);
if (options.mangle.properties) { if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") { if (typeof options.mangle.properties != "object") {

View File

@@ -56,6 +56,7 @@ function OutputStream(options) {
beautify : false, beautify : false,
braces : false, braces : false,
comments : false, comments : false,
galio : false,
ie8 : false, ie8 : false,
indent_level : 4, indent_level : 4,
indent_start : 0, indent_start : 0,
@@ -69,6 +70,7 @@ function OutputStream(options) {
semicolons : true, semicolons : true,
shebang : true, shebang : true,
source_map : null, source_map : null,
v8 : false,
webkit : false, webkit : false,
width : 80, width : 80,
wrap_iife : false, wrap_iife : false,
@@ -499,11 +501,11 @@ function OutputStream(options) {
} }
} }
if (/comment[134]/.test(c.type)) { if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\n"); print("//" + c.value.replace(/[@#]__PURE__/g, " ") + "\n");
indent(); indent();
last_nlb = true; last_nlb = true;
} else if (c.type == "comment2") { } else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/"); print("/*" + c.value.replace(/[@#]__PURE__/g, " ") + "*/");
last_nlb = false; last_nlb = false;
} }
}); });
@@ -557,10 +559,10 @@ function OutputStream(options) {
space(); space();
} }
if (/comment[134]/.test(c.type)) { if (/comment[134]/.test(c.type)) {
print("//" + c.value.replace(/[@#]__PURE__/g, ' ')); print("//" + c.value.replace(/[@#]__PURE__/g, " "));
need_newline_indented = true; need_newline_indented = true;
} else if (c.type == "comment2") { } else if (c.type == "comment2") {
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/"); print("/*" + c.value.replace(/[@#]__PURE__/g, " ") + "*/");
need_space = true; need_space = true;
} }
}); });
@@ -610,7 +612,7 @@ function OutputStream(options) {
}, },
parent : function(n) { parent : function(n) {
return stack[stack.length - 2 - (n || 0)]; return stack[stack.length - 2 - (n || 0)];
} },
}; };
} }
@@ -652,14 +654,8 @@ function OutputStream(options) {
/* -----[ PARENTHESES ]----- */ /* -----[ PARENTHESES ]----- */
function PARENS(nodetype, func) { function PARENS(nodetype, func) {
if (Array.isArray(nodetype)) {
nodetype.forEach(function(nodetype) {
PARENS(nodetype, func);
});
} else {
nodetype.DEFMETHOD("needs_parens", func); nodetype.DEFMETHOD("needs_parens", func);
} }
}
PARENS(AST_Node, return_false); PARENS(AST_Node, return_false);
@@ -667,11 +663,11 @@ function OutputStream(options) {
// the first token to appear in a statement. // the first token to appear in a statement.
PARENS(AST_Function, function(output) { PARENS(AST_Function, function(output) {
if (!output.has_parens() && first_in_statement(output)) return true; if (!output.has_parens() && first_in_statement(output)) return true;
if (output.option('webkit')) { if (output.option("webkit")) {
var p = output.parent(); var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) return true; if (p instanceof AST_PropAccess && p.expression === this) return true;
} }
if (output.option('wrap_iife')) { if (output.option("wrap_iife")) {
var p = output.parent(); var p = output.parent();
if (p instanceof AST_Call && p.expression === this) return true; if (p instanceof AST_Call && p.expression === this) return true;
} }
@@ -679,9 +675,10 @@ function OutputStream(options) {
// same goes for an object literal, because otherwise it would be // same goes for an object literal, because otherwise it would be
// interpreted as a block of code. // interpreted as a block of code.
PARENS(AST_Object, function(output) { function needs_parens_obj(output) {
return !output.has_parens() && first_in_statement(output); return !output.has_parens() && first_in_statement(output);
}); }
PARENS(AST_Object, needs_parens_obj);
PARENS(AST_Unary, function(output) { PARENS(AST_Unary, function(output) {
var p = output.parent(); var p = output.parent();
@@ -701,6 +698,7 @@ function OutputStream(options) {
|| p instanceof AST_Conditional || p instanceof AST_Conditional
// { [(1, 2)]: 3 }[2] ==> 3 // { [(1, 2)]: 3 }[2] ==> 3
// { foo: (1, 2) }.foo ==> 2 // { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_DestructuredKeyVal
|| p instanceof AST_ObjectProperty || p instanceof AST_ObjectProperty
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2 // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|| p instanceof AST_PropAccess && p.expression === this || p instanceof AST_PropAccess && p.expression === this
@@ -747,7 +745,7 @@ function OutputStream(options) {
var p = output.parent(); var p = output.parent();
if (p instanceof AST_New) return p.expression === this; if (p instanceof AST_New) return p.expression === this;
// https://bugs.webkit.org/show_bug.cgi?id=123506 // https://bugs.webkit.org/show_bug.cgi?id=123506
if (output.option('webkit')) { if (output.option("webkit")) {
var g = output.parent(1); var g = output.parent(1);
return this.expression instanceof AST_Function return this.expression instanceof AST_Function
&& p instanceof AST_PropAccess && p instanceof AST_PropAccess
@@ -771,23 +769,35 @@ function OutputStream(options) {
if (p instanceof AST_PropAccess && p.expression === this) { if (p instanceof AST_PropAccess && p.expression === this) {
var value = this.value; var value = this.value;
// https://github.com/mishoo/UglifyJS/issues/115 // https://github.com/mishoo/UglifyJS/issues/115
return value < 0
// https://github.com/mishoo/UglifyJS/pull/1009 // https://github.com/mishoo/UglifyJS/pull/1009
return value < 0 || /^0/.test(make_num(value)); || output.option("galio") && /^0/.test(make_num(value));
} }
}); });
PARENS([ AST_Assign, AST_Conditional ], function(output) { function needs_parens_assign_cond(self, output) {
var p = output.parent(); var p = output.parent();
// 1 + (a = 2) + 3 → 6, side effect setting a = 2 // 1 + (a = 2) + 3 → 6, side effect setting a = 2
if (p instanceof AST_Binary) return !(p instanceof AST_Assign); if (p instanceof AST_Binary) return !(p instanceof AST_Assign);
// (a = func)() —or— new (a = Object)() // (a = func)() —or— new (a = Object)()
if (p instanceof AST_Call) return p.expression === this; if (p instanceof AST_Call) return p.expression === self;
// (a = foo) ? bar : baz // (a = foo) ? bar : baz
if (p instanceof AST_Conditional) return p.condition === this; if (p instanceof AST_Conditional) return p.condition === self;
// (a = foo)["prop"] —or— (a = foo).prop // (a = foo)["prop"] —or— (a = foo).prop
if (p instanceof AST_PropAccess) return p.expression === this; if (p instanceof AST_PropAccess) return p.expression === self;
// !(a = false) → true // !(a = false) → true
if (p instanceof AST_Unary) return true; if (p instanceof AST_Unary) return true;
}
PARENS(AST_Assign, function(output) {
if (needs_parens_assign_cond(this, output)) return true;
// v8 parser bug => workaround
// f([1], [a] = []) => f([1], ([a] = []))
if (output.option("v8")) return this.left instanceof AST_Destructured;
// ({ p: a } = o);
if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output);
});
PARENS(AST_Conditional, function(output) {
return needs_parens_assign_cond(this, output);
}); });
/* -----[ PRINTERS ]----- */ /* -----[ PRINTERS ]----- */
@@ -1274,6 +1284,38 @@ function OutputStream(options) {
output.space(); output.space();
} : noop); } : noop);
}); });
DEFPRINT(AST_DestructuredArray, function(output) {
var a = this.elements, len = a.length;
output.with_square(len > 0 ? function() {
output.space();
a.forEach(function(exp, i) {
if (i) output.comma();
exp.print(output);
// If the final element is a hole, we need to make sure it
// doesn't look like a trailing comma, by inserting an actual
// trailing comma.
if (i === len - 1 && exp instanceof AST_Hole)
output.comma();
});
output.space();
} : noop);
});
DEFPRINT(AST_DestructuredKeyVal, print_key_value);
DEFPRINT(AST_DestructuredObject, function(output) {
var props = this.properties;
if (props.length > 0) output.with_block(function() {
props.forEach(function(prop, i) {
if (i) {
output.print(",");
output.newline();
}
output.indent();
prop.print(output);
});
output.newline();
});
else print_braced_empty(this, output);
});
DEFPRINT(AST_Object, function(output) { DEFPRINT(AST_Object, function(output) {
var props = this.properties; var props = this.properties;
if (props.length > 0) output.with_block(function() { if (props.length > 0) output.with_block(function() {
@@ -1314,12 +1356,13 @@ function OutputStream(options) {
} }
} }
DEFPRINT(AST_ObjectKeyVal, function(output) { function print_key_value(output) {
var self = this; var self = this;
print_property_key(self, output); print_property_key(self, output);
output.colon(); output.colon();
self.value.print(output); self.value.print(output);
}); }
DEFPRINT(AST_ObjectKeyVal, print_key_value);
function print_accessor(type) { function print_accessor(type) {
return function(output) { return function(output) {
var self = this; var self = this;
@@ -1483,6 +1526,7 @@ function OutputStream(options) {
AST_Constant, AST_Constant,
AST_Debugger, AST_Debugger,
AST_Definitions, AST_Definitions,
AST_Destructured,
AST_Finally, AST_Finally,
AST_Jump, AST_Jump,
AST_Lambda, AST_Lambda,
@@ -1497,7 +1541,7 @@ function OutputStream(options) {
output.add_mapping(this.start); output.add_mapping(this.start);
}); });
DEFMAP([ AST_ObjectProperty ], function(output) { DEFMAP([ AST_DestructuredKeyVal, AST_ObjectProperty ], function(output) {
if (typeof this.key == "string") output.add_mapping(this.start, this.key); if (typeof this.key == "string") output.add_mapping(this.start, this.key);
}); });
})(); })();

View File

@@ -973,14 +973,16 @@ function parse($TEXT, options) {
if (!is("punc", ";")) { if (!is("punc", ";")) {
init = is("keyword", "const") init = is("keyword", "const")
? (next(), const_(true)) ? (next(), const_(true))
: is("keyword", "let")
? (next(), let_(true))
: is("keyword", "var") : is("keyword", "var")
? (next(), var_(true)) ? (next(), var_(true))
: expression(true, true); : expression(true, true);
if (is("operator", "in")) { if (is("operator", "in")) {
if (init instanceof AST_Var) { if (init instanceof AST_Definitions) {
if (init.definitions.length > 1) if (init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos); croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
} else if (!is_assignable(init)) { } else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) {
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos); croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
} }
next(); next();
@@ -1025,7 +1027,7 @@ function parse($TEXT, options) {
var argnames = []; var argnames = [];
for (var first = true; !is("punc", ")");) { for (var first = true; !is("punc", ")");) {
if (first) first = false; else expect(","); if (first) first = false; else expect(",");
argnames.push(as_symbol(AST_SymbolFunarg)); argnames.push(maybe_destructured(AST_SymbolFunarg));
} }
next(); next();
var loop = S.in_loop; var loop = S.in_loop;
@@ -1146,16 +1148,16 @@ function parse($TEXT, options) {
}); });
} }
function vardefs(type, no_in, must_init) { function vardefs(type, no_in) {
var a = []; var a = [];
for (;;) { for (;;) {
var start = S.token; var start = S.token;
var name = as_symbol(type); var name = maybe_destructured(type);
var value = null; var value = null;
if (is("operator", "=")) { if (is("operator", "=")) {
next(); next();
value = expression(false, no_in); value = expression(false, no_in);
} else if (must_init) { } else if (!no_in && (type === AST_SymbolConst || name instanceof AST_Destructured)) {
croak("Missing initializer in declaration"); croak("Missing initializer in declaration");
} }
a.push(new AST_VarDef({ a.push(new AST_VarDef({
@@ -1174,7 +1176,7 @@ function parse($TEXT, options) {
var const_ = function(no_in) { var const_ = function(no_in) {
return new AST_Const({ return new AST_Const({
start : prev(), start : prev(),
definitions : vardefs(AST_SymbolConst, no_in, true), definitions : vardefs(AST_SymbolConst, no_in),
end : prev() end : prev()
}); });
}; };
@@ -1306,7 +1308,8 @@ function parse($TEXT, options) {
unexpected(); unexpected();
}; };
function expr_list(closing, allow_trailing_comma, allow_empty) { function expr_list(closing, allow_trailing_comma, allow_empty, parser) {
if (!parser) parser = expression;
var first = true, a = []; var first = true, a = [];
while (!is("punc", closing)) { while (!is("punc", closing)) {
if (first) first = false; else expect(","); if (first) first = false; else expect(",");
@@ -1314,7 +1317,7 @@ function parse($TEXT, options) {
if (is("punc", ",") && allow_empty) { if (is("punc", ",") && allow_empty) {
a.push(new AST_Hole({ start: S.token, end: S.token })); a.push(new AST_Hole({ start: S.token, end: S.token }));
} else { } else {
a.push(expression(false)); a.push(parser());
} }
} }
next(); next();
@@ -1449,6 +1452,54 @@ function parse($TEXT, options) {
return sym; return sym;
} }
function maybe_destructured(type) {
var start = S.token;
if (is("punc", "[")) {
next();
return new AST_DestructuredArray({
start: start,
elements: expr_list("]", !options.strict, true, function() {
return maybe_destructured(type);
}),
end: prev(),
});
}
if (is("punc", "{")) {
next();
var first = true, a = [];
while (!is("punc", "}")) {
if (first) first = false; else expect(",");
// allow trailing comma
if (!options.strict && is("punc", "}")) break;
var key_start = S.token;
var key = as_property_key();
if (!is("punc", ":") && key_start.type == "name") {
a.push(new AST_DestructuredKeyVal({
start: key_start,
key: key,
value: _make_symbol(type, key_start),
end: prev(),
}));
continue;
}
expect(":");
a.push(new AST_DestructuredKeyVal({
start: key_start,
key: key,
value: maybe_destructured(type),
end: prev(),
}));
}
next();
return new AST_DestructuredObject({
start: start,
properties: a,
end: prev(),
});
}
return as_symbol(type);
}
function mark_pure(call) { function mark_pure(call) {
var start = call.start; var start = call.start;
var comments = start.comments_before; var comments = start.comments_before;
@@ -1578,11 +1629,43 @@ function parse($TEXT, options) {
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef; return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
} }
function to_destructured(node) {
if (node instanceof AST_Array) {
var elements = node.elements.map(to_destructured);
return all(elements, function(node) {
return node instanceof AST_Destructured || node instanceof AST_Hole || is_assignable(node);
}) ? new AST_DestructuredArray({
start: node.start,
elements: elements,
end: node.end,
}) : node;
}
if (!(node instanceof AST_Object)) return node;
var props = [];
for (var i = 0; i < node.properties.length; i++) {
var prop = node.properties[i];
if (!(prop instanceof AST_ObjectKeyVal)) return node;
var value = to_destructured(prop.value);
if (!(value instanceof AST_Destructured || is_assignable(value))) return node;
props.push(new AST_DestructuredKeyVal({
start: prop.start,
key: prop.key,
value: value,
end: prop.end,
}));
}
return new AST_DestructuredObject({
start: node.start,
properties: props,
end: node.end,
});
}
var maybe_assign = function(no_in) { var maybe_assign = function(no_in) {
var start = S.token; var start = S.token;
var left = maybe_conditional(no_in), val = S.token.value; var left = maybe_conditional(no_in), val = S.token.value;
if (is("operator") && ASSIGNMENT[val]) { if (is("operator") && ASSIGNMENT[val]) {
if (is_assignable(left)) { if (is_assignable(left) || val == "=" && (left = to_destructured(left)) instanceof AST_Destructured) {
next(); next();
return new AST_Assign({ return new AST_Assign({
start : start, start : start,

View File

@@ -72,7 +72,7 @@ SymbolDef.prototype = {
if (def) { if (def) {
this.mangled_name = def.mangled_name || def.name; this.mangled_name = def.mangled_name || def.name;
} else { } else {
this.mangled_name = next_mangled_name(this.scope, options, this); this.mangled_name = next_mangled_name(this, options);
} }
if (this.global && cache) { if (this.global && cache) {
cache.set(this.name, this.mangled_name); cache.set(this.name, this.mangled_name);
@@ -204,7 +204,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 2: find back references and eval // pass 2: find back references and eval
self.globals = new Dictionary(); self.globals = new Dictionary();
var in_arg = [];
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
if (node instanceof AST_Lambda) {
in_arg.push(node);
node.argnames.forEach(function(argname) {
argname.walk(tw);
});
in_arg.pop();
walk_body(node, tw);
return true;
}
if (node instanceof AST_LoopControl) { if (node instanceof AST_LoopControl) {
if (node.label) node.label.thedef.references.push(node); if (node.label) node.label.thedef.references.push(node);
return true; return true;
@@ -212,11 +222,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var name = node.name; var name = node.name;
var sym = node.scope.find_variable(name); var sym = node.scope.find_variable(name);
for (var i = in_arg.length; i > 0 && sym;) {
i = in_arg.lastIndexOf(sym.scope, i - 1);
if (i < 0) break;
var decl = sym.orig[0];
if (decl instanceof AST_SymbolFunarg || decl instanceof AST_SymbolLambda) {
node.in_arg = true;
break;
}
sym = sym.scope.parent_scope.find_variable(name);
}
if (!sym) { if (!sym) {
sym = self.def_global(node); sym = self.def_global(node);
} else if (name == "arguments" && sym.scope instanceof AST_Lambda) { } else if (name == "arguments" && sym.scope instanceof AST_Lambda) {
if (!(tw.parent() instanceof AST_PropAccess)) {
sym.scope.uses_arguments = "d";
} else if (!sym.scope.uses_arguments) {
sym.scope.uses_arguments = true; sym.scope.uses_arguments = true;
} }
}
if (name == "eval") { if (name == "eval") {
var parent = tw.parent(); var parent = tw.parent();
if (parent.TYPE == "Call" && parent.expression === node) { if (parent.TYPE == "Call" && parent.expression === node) {
@@ -408,7 +432,8 @@ function names_in_use(scope, options) {
return names; return names;
} }
function next_mangled_name(scope, options, def) { function next_mangled_name(def, options) {
var scope = def.scope;
var in_use = names_in_use(scope, options); var in_use = names_in_use(scope, options);
var holes = scope.cname_holes; var holes = scope.cname_holes;
var names = Object.create(null); var names = Object.create(null);
@@ -461,6 +486,7 @@ function _default_mangler_options(options) {
keep_fnames : false, keep_fnames : false,
reserved : [], reserved : [],
toplevel : false, toplevel : false,
webkit : false,
}); });
if (!Array.isArray(options.reserved)) options.reserved = []; if (!Array.isArray(options.reserved)) options.reserved = [];
// Never mangle arguments // Never mangle arguments
@@ -495,9 +521,24 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
return true; return true;
} }
if (node instanceof AST_BlockScope) { if (node instanceof AST_BlockScope) {
var to_mangle = []; if (options.webkit && node instanceof AST_IterationStatement && node.init instanceof AST_Let) {
node.init.definitions.forEach(function(defn) {
defn.name.match_symbol(function(sym) {
if (!(sym instanceof AST_SymbolLet)) return;
var def = sym.definition();
var scope = sym.scope.parent_scope;
var redef = scope.def_variable(sym);
sym.thedef = def;
scope.to_mangle.push(redef);
def.redefined = function() {
return redef;
};
});
}, true);
}
node.to_mangle = [];
node.variables.each(function(def) { node.variables.each(function(def) {
if (!defer_redef(def)) to_mangle.push(def); if (!defer_redef(def)) node.to_mangle.push(def);
}); });
descend(); descend();
if (options.cache && node instanceof AST_Toplevel) { if (options.cache && node instanceof AST_Toplevel) {
@@ -508,7 +549,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
sym.scope = node; sym.scope = node;
sym.reference(options); sym.reference(options);
} }
to_mangle.forEach(mangle); node.to_mangle.forEach(mangle);
return true; return true;
} }
if (node instanceof AST_Label) { if (node instanceof AST_Label) {
@@ -528,13 +569,19 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
def.mangle(options); def.mangle(options);
} }
function defer_redef(def, node) { function defer_redef(def) {
var sym = def.orig[0];
var redef = def.redefined(); var redef = def.redefined();
if (!redef) return false; if (!redef) {
if (!(sym instanceof AST_SymbolConst)) return false;
var scope = def.scope.resolve();
if (def.scope === scope) return false;
redef = scope.def_variable(sym);
scope.to_mangle.push(redef);
}
redefined.push(def); redefined.push(def);
def.references.forEach(reference); def.references.forEach(reference);
var node = def.orig[0]; if (sym instanceof AST_SymbolCatch || sym instanceof AST_SymbolConst) reference(sym);
if (node instanceof AST_SymbolCatch || node instanceof AST_SymbolConst) reference(node);
return true; return true;
function reference(sym) { function reference(sym) {

View File

@@ -160,6 +160,16 @@ TreeTransformer.prototype = new TreeWalker;
DEF(AST_Array, function(self, tw) { DEF(AST_Array, function(self, tw) {
self.elements = do_list(self.elements, tw); self.elements = do_list(self.elements, tw);
}); });
DEF(AST_DestructuredArray, function(self, tw) {
self.elements = do_list(self.elements, tw);
});
DEF(AST_DestructuredKeyVal, function(self, tw) {
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
self.value = self.value.transform(tw);
});
DEF(AST_DestructuredObject, function(self, tw) {
self.properties = do_list(self.properties, tw);
});
DEF(AST_Object, function(self, tw) { DEF(AST_Object, function(self, tw) {
self.properties = do_list(self.properties, tw); self.properties = do_list(self.properties, tw);
}); });

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.11.6", "version": "3.12.0",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -63,7 +63,6 @@ function make_code(ast, options) {
function parse_test(file) { function parse_test(file) {
var script = fs.readFileSync(file, "utf8"); var script = fs.readFileSync(file, "utf8");
// TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS/issues/348
try { try {
var ast = U.parse(script, { var ast = U.parse(script, {
filename: file filename: file

View File

@@ -807,3 +807,47 @@ issue_4200: {
} }
expect_stdout: "undefined" expect_stdout: "undefined"
} }
issue_4291_1: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
console.log(function() {
arguments[0] = "PASS";
return arguments;
}()[0]);
}
expect: {
console.log(function() {
arguments[0] = "PASS";
return arguments;
}()[0]);
}
expect_stdout: "PASS"
}
issue_4291_2: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
var a = function() {
if (arguments[0])
arguments[1] = "PASS";
return arguments;
}(42);
console.log(a[1], a[0], a.length);
}
expect: {
var a = function(argument_0) {
if (argument_0)
arguments[1] = "PASS";
return arguments;
}(42);
console.log(a[1], a[0], a.length);
}
expect_stdout: "PASS 42 1"
}

View File

@@ -1,3 +1,45 @@
mangle_block: {
mangle = {
toplevel: false,
}
input: {
var o = "PASS";
{
const a = "FAIL";
}
console.log(o);
}
expect: {
var o = "PASS";
{
const a = "FAIL";
}
console.log(o);
}
expect_stdout: "PASS"
}
mangle_block_toplevel: {
mangle = {
toplevel: true,
}
input: {
var o = "PASS";
{
const a = "FAIL";
}
console.log(o);
}
expect: {
var o = "PASS";
{
const c = "FAIL";
}
console.log(o);
}
expect_stdout: "PASS"
}
mangle_catch_1: { mangle_catch_1: {
mangle = {} mangle = {}
input: { input: {
@@ -11,8 +53,8 @@ mangle_catch_1: {
expect: { expect: {
try { try {
throw "eeeee"; throw "eeeee";
} catch (e) { } catch (o) {
const o = typeof d; const e = typeof d;
} }
console.log(typeof a, typeof b); console.log(typeof a, typeof b);
} }
@@ -57,6 +99,23 @@ retain_block: {
expect_stdout: true expect_stdout: true
} }
retain_catch: {
options = {
dead_code: true,
}
input: {
try {} catch (a) {
const a = "aa";
}
}
expect: {
try {} catch (a) {
const a = "aa";
}
}
expect_stdout: true
}
if_dead_branch: { if_dead_branch: {
options = { options = {
conditionals: true, conditionals: true,
@@ -1225,3 +1284,66 @@ issue_4274_2: {
} }
expect_stdout: true expect_stdout: true
} }
issue_4290_1: {
options = {
unused: true,
}
input: {
const a = 0;
var a;
}
expect: {
const a = 0;
var a;
}
expect_stdout: true
}
issue_4305_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function() {
const arguments = function() {
while (console.log("PASS"));
};
arguments();
})();
}
expect: {
(function() {
const arguments = function() {
while (console.log("PASS"));
};
arguments();
})();
}
expect_stdout: true
}
issue_4305_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
const a = function() {
while (console.log("aaaaa"));
};
a();
})();
}
expect: {
(function(a) {
const a = function() {
while (console.log("aaaaa"));
};
a();
})();
}
expect_stdout: true
}

File diff suppressed because it is too large Load Diff

View File

@@ -521,7 +521,7 @@ issue_2531_2: {
options = { options = {
evaluate: true, evaluate: true,
inline: true, inline: true,
passes: 3, passes: 2,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
@@ -556,9 +556,10 @@ issue_2531_3: {
options = { options = {
evaluate: true, evaluate: true,
inline: true, inline: true,
passes: 3, passes: 2,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
sequences: true,
side_effects: true, side_effects: true,
toplevel: true, toplevel: true,
unused: true, unused: true,

View File

@@ -297,6 +297,33 @@ name_collision_3: {
expect_stdout: "true 4 6" expect_stdout: "true 4 6"
} }
name_collision_4: {
options = {
hoist_props: true,
reduce_vars: true,
}
input: {
console.log(function() {
var o = {
p: 0,
q: "PASS",
};
return function(o_p) {
if (!o.p) return o_p;
}(o.q);
}());
}
expect: {
console.log(function() {
var o_p$0 = 0, o_q = "PASS";
return function(o_p) {
if (!o_p$0) return o_p;
}(o_q);
}());
}
expect_stdout: "PASS"
}
contains_this_1: { contains_this_1: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -20,6 +20,26 @@ retain_block: {
node_version: ">=4" node_version: ">=4"
} }
retain_catch: {
options = {
dead_code: true,
}
input: {
"use strict";
try {} catch (a) {
let a = "aa";
}
}
expect: {
"use strict";
try {} catch (a) {
let a = "aa";
}
}
expect_stdout: true
node_version: ">=4"
}
if_dead_branch: { if_dead_branch: {
options = { options = {
conditionals: true, conditionals: true,
@@ -1006,3 +1026,205 @@ issue_4274_2: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=4" node_version: ">=4"
} }
issue_4276_1: {
options = {
unused: true,
}
input: {
"use strict";
try {
let a = b, b;
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
expect: {
"use strict";
try {
let a = b, b;
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4276_2: {
options = {
unused: true,
}
input: {
"use strict";
try {
let a = f(), b;
console.log("FAIL");
function f() {
return b;
}
} catch (e) {
console.log("PASS");
}
}
expect: {
"use strict";
try {
let a = f(), b;
console.log("FAIL");
function f() {
return b;
}
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4290_1: {
options = {
unused: true,
}
input: {
"use strict";
let a;
var a;
}
expect: {
"use strict";
let a;
var a;
}
expect_stdout: true
node_version: ">=4"
}
issue_4290_2: {
options = {
collapse_vars: true,
}
input: {
"use strict";
try {
console.log(function(a) {
a = c;
let c;
return a;
}());
} catch (e) {
console.log("PASS");
}
}
expect: {
"use strict";
try {
console.log(function(a) {
a = c;
let c;
return a;
}());
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4305_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function() {
let arguments = function() {
while (console.log("PASS"));
};
arguments();
})();
}
expect: {
(function() {
let arguments = function() {
while (console.log("PASS"));
};
arguments();
})();
}
expect_stdout: true
node_version: ">=6"
}
issue_4305_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
"use strict";
(function(a) {
let a = function() {
while (console.log("aaaaa"));
};
a();
})();
}
expect: {
"use strict";
(function(a) {
let a = function() {
while (console.log("aaaaa"));
};
a();
})();
}
expect_stdout: true
node_version: ">=4"
}
issue_1753: {
mangle = {
toplevel: false,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_1753_toplevel: {
mangle = {
toplevel: true,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let e = 0; e < 1; e++)
console.log(e);
}
expect_stdout: "0"
node_version: ">=4"
}

View File

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

View File

@@ -234,3 +234,178 @@ issue_4191_let: {
expect_stdout: "function undefined" expect_stdout: "function undefined"
node_version: ">=4" node_version: ">=4"
} }
forin_const_1: {
options = {
join_vars: true,
reduce_vars: true,
toplevel: true,
varify: true,
}
input: {
const o = {
foo: 42,
bar: "PASS",
};
for (const k in o)
console.log(k, o[k]);
}
expect: {
var o = {
foo: 42,
bar: "PASS",
};
for (const k in o)
console.log(k, o[k]);
}
expect_stdout: true
node_version: ">=4"
}
forin_const_2: {
options = {
join_vars: true,
reduce_vars: true,
toplevel: true,
varify: true,
}
input: {
const o = {
p: 42,
q: "PASS",
};
for (const [ k ] in o)
console.log(k, o[k]);
}
expect: {
var o = {
p: 42,
q: "PASS",
}, k;
for ([ k ] in o)
console.log(k, o[k]);
}
expect_stdout: [
"p 42",
"q PASS",
]
node_version: ">=6"
}
forin_let_1: {
options = {
join_vars: true,
reduce_vars: true,
toplevel: true,
varify: true,
}
input: {
"use strict";
let o = {
foo: 42,
bar: "PASS",
};
for (let k in o)
console.log(k, o[k]);
}
expect: {
"use strict";
var o = {
foo: 42,
bar: "PASS",
}, k;
for (k in o)
console.log(k, o[k]);
}
expect_stdout: [
"foo 42",
"bar PASS",
]
node_version: ">=4"
}
forin_let_2: {
options = {
join_vars: true,
reduce_vars: true,
toplevel: true,
varify: true,
}
input: {
let o = {
p: 42,
q: "PASS",
};
for (let [ k ] in o)
console.log(k, o[k]);
}
expect: {
var o = {
p: 42,
q: "PASS",
}, k;
for ([ k ] in o)
console.log(k, o[k]);
}
expect_stdout: [
"p 42",
"q PASS",
]
node_version: ">=6"
}
issue_4290_1_const: {
options = {
reduce_vars: true,
toplevel: true,
varify: true,
}
input: {
const a = 0;
var a;
}
expect: {
const a = 0;
var a;
}
expect_stdout: true
}
issue_4290_1_let: {
options = {
reduce_vars: true,
toplevel: true,
varify: true,
}
input: {
"use strict";
let a = 0;
var a;
}
expect: {
"use strict";
let a = 0;
var a;
}
expect_stdout: true
node_version: ">=4"
}
drop_forin_let: {
options = {
loops: true,
toplevel: true,
unused: true,
varify: true,
}
input: {
"use strict";
for (let a in console.log("PASS"));
}
expect: {
"use strict";
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -95,6 +95,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
// quick ignores // quick ignores
if (node instanceof U.AST_Accessor) return; if (node instanceof U.AST_Accessor) return;
if (node instanceof U.AST_Destructured) return;
if (node instanceof U.AST_Directive) return; if (node instanceof U.AST_Directive) return;
if (!in_list && node instanceof U.AST_EmptyStatement) return; if (!in_list && node instanceof U.AST_EmptyStatement) return;
if (node instanceof U.AST_Label) return; if (node instanceof U.AST_Label) return;
@@ -114,6 +115,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
// ignore lvalues // ignore lvalues
if (parent instanceof U.AST_Assign && parent.left === node) return; if (parent instanceof U.AST_Assign && parent.left === node) return;
if (parent instanceof U.AST_DestructuredArray) return;
if (parent instanceof U.AST_DestructuredKeyVal && parent.value === node) return;
if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) { if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) {
case "++": case "++":
case "--": case "--":
@@ -250,13 +253,23 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
} }
else if (node instanceof U.AST_ForIn) { else if (node instanceof U.AST_ForIn) {
var expr = [ var expr;
node.init, switch ((node.start._permute * steps | 0) % 3) {
node.object, case 0:
node.body, if (!(node.init instanceof U.AST_Definitions
][ (node.start._permute * steps | 0) % 3 ]; && node.init.definitions[0].name instanceof U.AST_Destructured)) {
expr = node.init;
}
break;
case 1:
expr = node.object;
break;
case 2:
if (!has_loopcontrol(node.body, node, parent)) expr = node.body;
break;
}
node.start._permute += step; node.start._permute += step;
if (expr && (expr !== node.body || !has_loopcontrol(expr, node, parent))) { if (expr) {
CHANGED = true; CHANGED = true;
return to_statement(expr); return to_statement(expr);
} }
@@ -389,6 +402,13 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true; CHANGED = true;
return List.skip; return List.skip;
} }
// skip element/property from (destructured) array/object
if (parent instanceof U.AST_Array || parent instanceof U.AST_Destructured || parent instanceof AST_Object) {
node.start._permute++;
CHANGED = true;
return List.skip;
}
} }
// replace this node // replace this node

View File

@@ -377,6 +377,118 @@ function createArgs(recurmax, stmtDepth, canThrow) {
return args.join(", "); return args.join(", ");
} }
function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames, maybe, dontStore) {
var avoid = [];
var len = unique_vars.length;
var pairs = createPairs(recurmax);
unique_vars.length = len;
return pairs;
function createAssignmentValue(recurmax) {
var current = VAR_NAMES;
VAR_NAMES = (varNames || VAR_NAMES).slice();
var value = varNames && rng(2) ? createValue() : createExpression(recurmax, noComma, stmtDepth, canThrow);
VAR_NAMES = current;
return value;
}
function createKey(recurmax, keys) {
var save = VAR_NAMES;
VAR_NAMES = VAR_NAMES.filter(function(name) {
return avoid.indexOf(name) < 0;
});
var len = VAR_NAMES.length;
var key;
do {
key = createObjectKey(recurmax, stmtDepth, canThrow);
} while (keys.indexOf(key) >= 0);
VAR_NAMES = save.concat(VAR_NAMES.slice(len));
return key;
}
function createPairs(recurmax) {
var names = [], values = [];
var m = rng(4), n = rng(4);
if (!varNames) m = Math.max(m, n, 1);
for (var i = Math.max(m, n); --i >= 0;) {
if (i < m && i < n) {
createDestructured(recurmax, names, values);
continue;
}
if (i < m) {
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
var name = createVarName(maybe, dontStore);
unique_vars.length -= 6;
avoid.push(name);
unique_vars.push(name);
names.unshift(name);
}
if (i < n) {
values.unshift(createAssignmentValue(recurmax));
}
}
return {
names: names,
values: values,
};
}
function createDestructured(recurmax, names, values) {
switch (rng(20)) {
case 0:
if (--recurmax < 0) {
names.unshift("[]");
values.unshift('""');
} else {
var pairs = createPairs(recurmax);
while (!rng(10)) {
var index = rng(pairs.names.length + 1);
pairs.names.splice(index, 0, "");
pairs.values.splice(index, 0, rng(2) ? createAssignmentValue(recurmax) : "");
}
names.unshift("[ " + pairs.names.join(", ") + " ]");
values.unshift("[ " + pairs.values.join(", ") + " ]");
}
break;
case 1:
if (--recurmax < 0) {
names.unshift("{}");
values.unshift('""');
} else {
var pairs = createPairs(recurmax);
var keys = [];
pairs.names.forEach(function(name, index) {
if (/^[[{]/.test(name)) {
var key;
do {
key = KEYS[rng(KEYS.length)];
} while (keys.indexOf(key) >= 0);
keys[index] = key;
}
});
names.unshift("{ " + pairs.names.map(function(name, index) {
var key = index in keys ? keys[index] : rng(10) && createKey(recurmax, keys);
return key ? key + ": " + name : name;
}).join(", ") + " }");
values.unshift("{ " + pairs.values.map(function(value, index) {
var key = index in keys ? keys[index] : createKey(recurmax, keys);
return key + ": " + value;
}).join(", ") + " }");
}
break;
default:
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
var name = createVarName(maybe, dontStore);
unique_vars.length -= 6;
avoid.push(name);
unique_vars.push(name);
names.unshift(name);
values.unshift(createAssignmentValue(recurmax));
break;
}
}
}
function filterDirective(s) { function filterDirective(s) {
if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ";" + s[2]; if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ";" + s[2];
return s; return s;
@@ -415,11 +527,37 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
return names.indexOf(name) < 0; return names.indexOf(name) < 0;
}); });
var len = VAR_NAMES.length; var len = VAR_NAMES.length;
var s = type + " " + names.map(function(name) { var s = type + " ";
switch (rng(10)) {
case 0:
while (!rng(10)) names.splice(rng(names.length + 1), 0, "");
s += "[ " + names.join(", ") + " ] = [ " + names.map(function() {
return rng(10) ? createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) : "";
}).join(", ") + " ];";
break;
case 1:
var keys = [];
s += "{ " + names.map(function(name, i) {
var key = createObjectKey(recurmax, stmtDepth, canThrow);
if (!/\[/.test(key)) keys[i] = key;
return key + ": " + name;
}).join(", ") + "} = { " + names.map(function(name, i) {
var key = i in keys ? keys[i] : createObjectKey(recurmax, stmtDepth, canThrow);
return key + ": " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
}).join(", ") + "};";
break;
default:
s += names.map(function(name, i) {
if (type == "let" && !rng(10)) {
VAR_NAMES.push(name);
return name;
}
var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow); var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
VAR_NAMES.push(name); VAR_NAMES.push(name);
return name + " = " + value; return name + " = " + value;
}).join(", ") + ";"; }).join(", ") + ";";
break;
}
VAR_NAMES = save.concat(VAR_NAMES.slice(len)); VAR_NAMES = save.concat(VAR_NAMES.slice(len));
return s; return s;
} }
@@ -429,9 +567,9 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
if (--recurmax < 0) { return ";"; } if (--recurmax < 0) { return ";"; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0; if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
var s = []; var s = [];
var name; var name, args;
var varNames = VAR_NAMES.slice();
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
var namesLenBefore = VAR_NAMES.length;
if (allowDefun || rng(5) > 0) { if (allowDefun || rng(5) > 0) {
name = "f" + funcs++; name = "f" + funcs++;
} else { } else {
@@ -439,7 +577,16 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
name = createVarName(MANDATORY, !allowDefun); name = createVarName(MANDATORY, !allowDefun);
unique_vars.length -= 3; unique_vars.length -= 3;
} }
s.push("function " + name + "(" + createParams() + "){", strictMode()); var params;
if ((!allowDefun || !(name in called)) && rng(2)) {
called[name] = false;
var pairs = createAssignmentPairs(recurmax, COMMA_OK, stmtDepth, canThrow, varNames, MANDATORY);
params = pairs.names.join(", ");
args = pairs.values.join(", ");
} else {
params = createParams();
}
s.push("function " + name + "(" + params + "){", strictMode());
s.push(defns()); s.push(defns());
if (rng(5) === 0) { if (rng(5) === 0) {
// functions with functions. lower the recursion to prevent a mess. // functions with functions. lower the recursion to prevent a mess.
@@ -450,17 +597,16 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
} }
s.push("}", ""); s.push("}", "");
s = filterDirective(s).join("\n"); s = filterDirective(s).join("\n");
VAR_NAMES.length = namesLenBefore;
}); });
VAR_NAMES = varNames;
if (!allowDefun) { if (!allowDefun) {
// avoid "function statements" (decl inside statements) // avoid "function statements" (decl inside statements)
s = "var " + createVarName(MANDATORY) + " = " + s; s = "var " + createVarName(MANDATORY) + " = " + s;
s += "(" + createArgs(recurmax, stmtDepth, canThrow) + ")"; s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")";
} else if (!(name in called) || rng(3) > 0) { } else if (!(name in called) || args || rng(3)) {
s += "var " + createVarName(MANDATORY) + " = " + name; s += "var " + createVarName(MANDATORY) + " = " + name;
s += "(" + createArgs(recurmax, stmtDepth, canThrow) + ")"; s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")";
} }
return s + ";"; return s + ";";
@@ -561,8 +707,9 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
return [ return [
"{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ", "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
label.target + " for (", label.target + " for (",
/^key/.test(key) ? "var " : "", !/^key/.test(key) ? rng(10) ? "" : "var " : rng(10) ? "var " : rng(2) ? "let " : "const ",
key + " in expr" + loop + ") {", rng(10) ? key : rng(5) ? "[ " + key + " ]" : "{ length: " + key + " }",
" in expr" + loop + ") {",
rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "", rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "",
createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth), createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
"}}", "}}",
@@ -576,7 +723,12 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
// note: default does not _need_ to be last // note: default does not _need_ to be last
return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}"; return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
case STMT_VAR: case STMT_VAR:
switch (rng(3)) { if (!rng(20)) {
var pairs = createAssignmentPairs(recurmax, NO_COMMA, stmtDepth, canThrow, null, MANDATORY);
return "var " + pairs.names.map(function(name, index) {
return index in pairs.values ? name + " = " + pairs.values[index] : name;
}).join(", ") + ";";
} else switch (rng(3)) {
case 0: case 0:
unique_vars.push("c"); unique_vars.push("c");
var name = createVarName(MANDATORY); var name = createVarName(MANDATORY);
@@ -719,7 +871,28 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
return getVarName(); return getVarName();
case p++: case p++:
switch (rng(20)) {
case 0:
return [
"[ ",
new Array(rng(3)).join(","),
getVarName(NO_CONST),
new Array(rng(3)).join(","),
" ] = ",
createArrayLiteral(recurmax, stmtDepth, canThrow),
].join("");
case 1:
return [
"{ ",
rng(2) ? "" : createObjectKey(recurmax, stmtDepth, canThrow) + ": ",
getVarName(NO_CONST),
" } = ",
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow),
" || {}",
].join("");
default:
return getVarName(NO_CONST) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); return getVarName(NO_CONST) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
}
case p++: case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: case p++:
@@ -864,7 +1037,10 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
case p++: case p++:
case p++: case p++:
var name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2); var name;
do {
name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
} while (name in called && !called[name]);
called[name] = true; called[name] = true;
return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")"; return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
} }
@@ -1002,13 +1178,77 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
case 3: case 3:
assignee = getVarName(); assignee = getVarName();
expr = "(" + assignee + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) switch (rng(20)) {
+ "]" + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; case 0:
expr = [
"([ ",
assignee,
"[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
" ] = [ ",
_createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
" ])",
].join("");
break;
case 1:
var key1 = createObjectKey(recurmax, stmtDepth, canThrow);
var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1;
expr = [
"({ ",
key1, ": ", assignee,
"[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
" } = { ",
key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
" })",
].join("");
break;
default:
expr = [
"(",
assignee,
"[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
createAssignment(),
_createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
")",
].join("");
break;
}
return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")"; return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")";
case 4: case 4:
assignee = getVarName(); assignee = getVarName();
expr = "(" + assignee + "." + getDotKey(true) + createAssignment() switch (rng(20)) {
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; case 0:
expr = [
"([ ",
assignee,
".", getDotKey(true),
" ] = [ ",
_createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
" ])",
].join("");
break;
case 1:
var key1 = createObjectKey(recurmax, stmtDepth, canThrow);
var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1;
expr = [
"({ ",
key1, ": ", assignee,
".", getDotKey(true),
" } = { ",
key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
" })",
].join("");
break;
default:
expr = [
"(",
assignee,
".", getDotKey(true),
createAssignment(),
_createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
")",
].join("");
break;
}
return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")"; return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")";
default: default:
return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow); return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
@@ -1351,7 +1591,14 @@ function patch_try_catch(orig, toplevel) {
} }
} }
var minify_options = require("./options.json").map(JSON.stringify); var minify_options = require("./options.json");
if (typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") {
minify_options.forEach(function(o) {
if (!("output" in o)) o.output = {};
o.output.v8 = true;
});
}
minify_options = minify_options.map(JSON.stringify);
var original_code, original_result, errored; var original_code, original_result, errored;
var uglify_code, uglify_result, ok; var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {