Compare commits

..

44 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
Alex Lam S.L
e390e7e124 v3.11.6 2020-11-14 22:21:19 +08:00
Alex Lam S.L
6fd5b5b371 fix corner case in loops (#4275)
fixes #4274
2020-11-14 02:08:05 +08:00
Alex Lam S.L
fba27bfb71 fix corner case in evaluate (#4272)
fixes #4271
2020-11-11 00:06:13 +08:00
Alex Lam S.L
41310e6404 fix corner case in objects (#4270)
fixes #4269
2020-11-09 10:47:02 +08:00
Alex Lam S.L
91fc1c82b5 support computed property name in object literal (#4268) 2020-11-08 23:38:32 +08:00
Alex Lam S.L
810cd40356 fix corner case in inline (#4266)
fixes #4265
2020-11-08 18:50:08 +08:00
Alex Lam S.L
1cbd07e789 support shorthand method name in object literal (#4264) 2020-11-08 13:17:53 +08:00
Alex Lam S.L
b82de04775 support shorthand property name in object literal (#4263) 2020-11-08 10:44:44 +08:00
Alex Lam S.L
4bbeb09f7c fix corner case in reduce_vars (#4262)
fixes #4261
2020-11-07 10:00:04 +08:00
Alex Lam S.L
c2f6fd5fde fix corner case in functions (#4260)
fixes #4259
2020-11-06 03:55:25 +08:00
Alex Lam S.L
af4ea3ff69 v3.11.5 2020-11-03 08:59:02 +08:00
Alex Lam S.L
e7643248a3 fix corner case in merge_vars (#4258)
fixes #4257
2020-11-02 01:01:00 +08:00
Alex Lam S.L
68091dbf69 fix corner case in merge_vars (#4256)
fixes #4255
2020-11-01 14:34:07 +08:00
Alex Lam S.L
cbf7269296 fix corner case in merge_vars (#4254)
fixes #4253
2020-11-01 10:37:21 +08:00
Alex Lam S.L
d8563caba7 improve resilience against spurious time-outs (#4252) 2020-10-30 11:06:48 +08:00
Alex Lam S.L
2e0ad40fe6 fix corner case in ie8 (#4251)
fixes #4250
2020-10-30 11:06:31 +08:00
Alex Lam S.L
5d12abc41b fix corner cases in collapse_vars (#4249)
fixes #4248
2020-10-30 10:04:23 +08:00
Alex Lam S.L
79e5c3f564 improve warnings (#4247)
closes #4244
2020-10-27 17:39:33 +08:00
Alex Lam S.L
607f87c5cd fix corner case in booleans (#4246)
fixes #4245
2020-10-26 18:53:58 +08:00
34 changed files with 4746 additions and 704 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":
@@ -276,7 +278,9 @@ function convert_ast(fn) {
function run() { function run() {
var content = options.sourceMap && options.sourceMap.content; var content = options.sourceMap && options.sourceMap.content;
if (content && content != "inline") { if (content && content != "inline") {
UglifyJS.AST_Node.info("Using input source map: " + content); UglifyJS.AST_Node.info("Using input source map: {content}", {
content : content,
});
options.sourceMap.content = read_file(content, content); options.sourceMap.content = read_file(content, content);
} }
try { try {

View File

@@ -137,17 +137,17 @@ var AST_Node = DEFNODE("Node", "start end", {
}, null); }, null);
(AST_Node.log_function = function(fn, verbose) { (AST_Node.log_function = function(fn, verbose) {
var printed = Object.create(null); if (!fn) {
if (fn) {
AST_Node.info = verbose ? function(text, props) {
log("INFO: " + string_template(text, props));
} : noop;
AST_Node.warn = function(text, props) {
log("WARN: " + string_template(text, props));
};
} else {
AST_Node.info = AST_Node.warn = noop; AST_Node.info = AST_Node.warn = noop;
return;
} }
var printed = Object.create(null);
AST_Node.info = verbose ? function(text, props) {
log("INFO: " + string_template(text, props));
} : noop;
AST_Node.warn = function(text, props) {
log("WARN: " + string_template(text, props));
};
function log(msg) { function log(msg) {
if (printed[msg]) return; if (printed[msg]) return;
@@ -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,7 +531,9 @@ 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) {
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]"); validate_destructured(node, function(node) {
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: {
@@ -1015,24 +1118,28 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties", $documentation: "Base class for literal object properties",
$propdoc: { $propdoc: {
key: "[string|AST_SymbolAccessor] property name. For ObjectKeyVal this is a string. For getters and setters this is an AST_SymbolAccessor.", key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor." value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.",
}, },
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
if (node.key instanceof AST_Node) node.key.walk(visitor);
node.value.walk(visitor); node.value.walk(visitor);
}); });
}
});
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
$documentation: "A key: value object property",
$propdoc: {
quote: "[string] the original quote character"
}, },
_validate: function() { _validate: function() {
if (typeof this.key != "string") throw new Error("key must be string"); 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");
}
if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node");
},
});
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
$documentation: "A key: value object property",
_validate: function() {
must_be_expression(this, "value"); must_be_expression(this, "value");
}, },
}, AST_ObjectProperty); }, AST_ObjectProperty);
@@ -1040,7 +1147,6 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, { var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
$documentation: "An object setter property", $documentation: "An object setter property",
_validate: function() { _validate: function() {
if (!(this.key instanceof AST_SymbolAccessor)) throw new Error("key must be AST_SymbolAccessor");
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
}, },
}, AST_ObjectProperty); }, AST_ObjectProperty);
@@ -1048,7 +1154,6 @@ var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
$documentation: "An object getter property", $documentation: "An object getter property",
_validate: function() { _validate: function() {
if (!(this.key instanceof AST_SymbolAccessor)) throw new Error("key must be AST_SymbolAccessor");
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
}, },
}, AST_ObjectProperty); }, AST_ObjectProperty);
@@ -1065,10 +1170,6 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
}, },
}); });
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
$documentation: "The name of a property accessor (setter/getter function)"
}, AST_Symbol);
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
$documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)", $documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)",
}, AST_Symbol); }, AST_Symbol);

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,9 @@ function read_source_map(name, toplevel) {
return to_ascii(match[2]); return to_ascii(match[2]);
} }
} }
AST_Node.warn("inline source map not found: " + name); AST_Node.warn("inline source map not found: {name}", {
name: name,
});
} }
function parse_source_map(content) { function parse_source_map(content) {
@@ -87,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();
@@ -99,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, {
@@ -109,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") {
@@ -258,6 +263,7 @@ function minify(files, options) {
} catch (ex) { } catch (ex) {
return { error: ex }; return { error: ex };
} finally { } finally {
AST_Node.log_function();
AST_Node.disable_validation(); AST_Node.disable_validation();
} }
} }

View File

@@ -115,9 +115,6 @@
value : from_moz(M.value) value : from_moz(M.value)
}; };
if (M.kind == "init") return new AST_ObjectKeyVal(args); if (M.kind == "init") return new AST_ObjectKeyVal(args);
args.key = new AST_SymbolAccessor({
name: args.key
});
args.value = new AST_Accessor(args.value); args.value = new AST_Accessor(args.value);
if (M.kind == "get") return new AST_ObjectGetter(args); if (M.kind == "get") return new AST_ObjectGetter(args);
if (M.kind == "set") return new AST_ObjectSetter(args); if (M.kind == "set") return new AST_ObjectSetter(args);
@@ -385,7 +382,7 @@
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
var key = { var key = {
type: "Literal", type: "Literal",
value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key value: M.key
}; };
var kind; var kind;
if (M instanceof AST_ObjectKeyVal) { if (M instanceof AST_ObjectKeyVal) {

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,13 +654,7 @@ function OutputStream(options) {
/* -----[ PARENTHESES ]----- */ /* -----[ PARENTHESES ]----- */
function PARENS(nodetype, func) { function PARENS(nodetype, func) {
if (Array.isArray(nodetype)) { nodetype.DEFMETHOD("needs_parens", func);
nodetype.forEach(function(nodetype) {
PARENS(nodetype, func);
});
} else {
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();
@@ -699,7 +696,9 @@ function OutputStream(options) {
// (false, true) ? (a = 10, b = 20) : (c = 30) // (false, true) ? (a = 10, b = 20) : (c = 30)
// ==> 20 (side effect, set a := 10 and b := 20) // ==> 20 (side effect, set a := 10 and b := 20)
|| p instanceof AST_Conditional || p instanceof AST_Conditional
// { [(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
@@ -746,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
@@ -769,24 +768,36 @@ function OutputStream(options) {
var p = output.parent(); var p = output.parent();
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
// https://github.com/mishoo/UglifyJS/pull/1009 return value < 0
return value < 0 || /^0/.test(make_num(value)); // https://github.com/mishoo/UglifyJS/pull/1009
|| 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 ]----- */
@@ -1273,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() {
@@ -1289,34 +1332,43 @@ function OutputStream(options) {
else print_braced_empty(this, output); else print_braced_empty(this, output);
}); });
function print_property_name(key, quote, output) { function print_property_key(self, output) {
if (output.option("quote_keys")) { var key = self.key;
if (key instanceof AST_Node) {
output.with_square(function() {
key.print(output);
});
} else if (output.option("quote_keys")) {
output.print_string(key); output.print_string(key);
} else if ("" + +key == key && key >= 0) { } else if ("" + +key == key && key >= 0) {
output.print(make_num(key)); output.print(make_num(key));
} else if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) {
output.print_string(key, quote);
} else {
output.print_name(key);
}
} else { } else {
output.print_string(key, quote); var quote = self.start && self.start.quote;
if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) {
output.print_string(key, quote);
} else {
output.print_name(key);
}
} else {
output.print_string(key, quote);
}
} }
} }
DEFPRINT(AST_ObjectKeyVal, function(output) { function print_key_value(output) {
var self = this; var self = this;
print_property_name(self.key, self.quote, 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;
output.print(type); output.print(type);
output.space(); output.space();
print_property_name(self.key.name, self.quote, output); print_property_key(self, output);
self.value._codegen(output, true); self.value._codegen(output, true);
}; };
} }
@@ -1474,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,
@@ -1488,14 +1541,7 @@ function OutputStream(options) {
output.add_mapping(this.start); output.add_mapping(this.start);
}); });
DEFMAP([ DEFMAP([ AST_DestructuredKeyVal, AST_ObjectProperty ], function(output) {
AST_ObjectGetter, if (typeof this.key == "string") output.add_mapping(this.start, this.key);
AST_ObjectSetter,
], function(output) {
output.add_mapping(this.start, this.key.name);
});
DEFMAP([ AST_ObjectProperty ], function(output) {
output.add_mapping(this.start, this.key);
}); });
})(); })();

View File

@@ -753,7 +753,7 @@ function parse($TEXT, options) {
} }
} }
var statement = embed_tokens(function(strict_defun) { var statement = embed_tokens(function() {
handle_regexp(); handle_regexp();
switch (S.token.type) { switch (S.token.type) {
case "string": case "string":
@@ -844,9 +844,6 @@ function parse($TEXT, options) {
return for_(); return for_();
case "function": case "function":
if (!strict_defun && S.input.has_directive("use strict")) {
croak("In strict mode code, functions can only be declared at top level or immediately within another function.");
}
next(); next();
return function_(AST_Defun); return function_(AST_Defun);
@@ -976,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();
@@ -1028,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;
@@ -1038,7 +1037,7 @@ function parse($TEXT, options) {
S.input.push_directives_stack(); S.input.push_directives_stack();
S.in_loop = 0; S.in_loop = 0;
S.labels = []; S.labels = [];
var body = block_(true); var body = block_();
if (S.input.has_directive("use strict")) { if (S.input.has_directive("use strict")) {
if (name) strict_verify_symbol(name); if (name) strict_verify_symbol(name);
argnames.forEach(strict_verify_symbol); argnames.forEach(strict_verify_symbol);
@@ -1067,12 +1066,12 @@ function parse($TEXT, options) {
}); });
} }
function block_(strict_defun) { function block_() {
expect("{"); expect("{");
var a = []; var a = [];
while (!is("punc", "}")) { while (!is("punc", "}")) {
if (is("eof")) expect_token("punc", "}"); if (is("eof")) expect_token("punc", "}");
a.push(statement(strict_defun)); a.push(statement());
} }
next(); next();
return a; return a;
@@ -1149,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({
@@ -1177,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()
}); });
}; };
@@ -1222,7 +1221,7 @@ function parse($TEXT, options) {
var tok = S.token, ret; var tok = S.token, ret;
switch (tok.type) { switch (tok.type) {
case "name": case "name":
ret = _make_symbol(AST_SymbolRef); ret = _make_symbol(AST_SymbolRef, tok);
break; break;
case "num": case "num":
ret = new AST_Number({ start: tok, end: tok, value: tok.value }); ret = new AST_Number({ start: tok, end: tok, value: tok.value });
@@ -1309,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(",");
@@ -1317,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();
@@ -1340,51 +1340,62 @@ function parse($TEXT, options) {
var first = true, a = []; var first = true, a = [];
while (!is("punc", "}")) { while (!is("punc", "}")) {
if (first) first = false; else expect(","); if (first) first = false; else expect(",");
if (!options.strict && is("punc", "}")) // allow trailing comma
// allow trailing comma if (!options.strict && is("punc", "}")) break;
break;
var start = S.token; var start = S.token;
var type = start.type; var key = as_property_key();
var name = as_property_name(); if (is("punc", "(")) {
if (type == "name" && !is("punc", ":")) { var func_start = S.token;
var key = new AST_SymbolAccessor({ var func = function_(AST_Function);
start: S.token, func.start = func_start;
name: "" + as_property_name(), func.end = prev();
end: prev() a.push(new AST_ObjectKeyVal({
}); start: start,
if (name == "get") { key: key,
a.push(new AST_ObjectGetter({ value: func,
start : start, end: prev(),
key : key, }));
value : create_accessor(), continue;
end : prev() }
})); if (!is("punc", ":") && start.type == "name") switch (key) {
continue; case "get":
} a.push(new AST_ObjectGetter({
if (name == "set") { start: start,
a.push(new AST_ObjectSetter({ key: as_property_key(),
start : start, value: create_accessor(),
key : key, end: prev(),
value : create_accessor(), }));
end : prev() continue;
})); case "set":
continue; a.push(new AST_ObjectSetter({
} start: start,
key: as_property_key(),
value: create_accessor(),
end: prev(),
}));
continue;
default:
a.push(new AST_ObjectKeyVal({
start: start,
key: key,
value: _make_symbol(AST_SymbolRef, start),
end: prev(),
}));
continue;
} }
expect(":"); expect(":");
a.push(new AST_ObjectKeyVal({ a.push(new AST_ObjectKeyVal({
start : start, start: start,
quote : start.quote, key: key,
key : "" + name, value: expression(false),
value : expression(false), end: prev(),
end : prev()
})); }));
} }
next(); next();
return new AST_Object({ properties: a }); return new AST_Object({ properties: a });
}); });
function as_property_name() { function as_property_key() {
var tmp = S.token; var tmp = S.token;
switch (tmp.type) { switch (tmp.type) {
case "operator": case "operator":
@@ -1395,7 +1406,13 @@ function parse($TEXT, options) {
case "keyword": case "keyword":
case "atom": case "atom":
next(); next();
return tmp.value; return "" + tmp.value;
case "punc":
if (tmp.value != "[") unexpected();
next();
var key = expression(false);
expect("]");
return key;
default: default:
unexpected(); unexpected();
} }
@@ -1408,12 +1425,12 @@ function parse($TEXT, options) {
return name; return name;
} }
function _make_symbol(type) { function _make_symbol(type, token) {
var name = S.token.value; var name = token.value;
return new (name == "this" ? AST_This : type)({ return new (name === "this" ? AST_This : type)({
name : String(name), name: "" + name,
start : S.token, start: token,
end : S.token end: token,
}); });
} }
@@ -1427,7 +1444,7 @@ function parse($TEXT, options) {
if (!noerror) croak("Name expected"); if (!noerror) croak("Name expected");
return null; return null;
} }
var sym = _make_symbol(type); var sym = _make_symbol(type, S.token);
if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) { if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
strict_verify_symbol(sym); strict_verify_symbol(sym);
} }
@@ -1435,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;
@@ -1564,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,
@@ -1616,7 +1713,7 @@ function parse($TEXT, options) {
var body = []; var body = [];
S.input.push_directives_stack(); S.input.push_directives_stack();
while (!is("eof")) while (!is("eof"))
body.push(statement(true)); body.push(statement());
S.input.pop_directives_stack(); S.input.pop_directives_stack();
var end = prev(); var end = prev();
var toplevel = options.toplevel; var toplevel = options.toplevel;

View File

@@ -81,8 +81,8 @@ var builtins = function() {
function reserve_quoted_keys(ast, reserved) { function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) { ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal) { if (node instanceof AST_ObjectProperty) {
if (node.quote) add(node.key); if (node.start && node.start.quote) add(node.key);
} else if (node instanceof AST_Sub) { } else if (node instanceof AST_Sub) {
addStrings(node.property, add); addStrings(node.property, add);
} }
@@ -165,11 +165,8 @@ function mangle_properties(ast, options) {
} }
} else if (node instanceof AST_Dot) { } else if (node instanceof AST_Dot) {
add(node.property); add(node.property);
} else if (node instanceof AST_ObjectKeyVal) {
add(node.key);
} else if (node instanceof AST_ObjectProperty) { } else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above if (typeof node.key == "string") add(node.key);
add(node.key.name);
} else if (node instanceof AST_Sub) { } else if (node instanceof AST_Sub) {
addStrings(node.property, add); addStrings(node.property, add);
} }
@@ -198,11 +195,8 @@ function mangle_properties(ast, options) {
} }
} else if (node instanceof AST_Dot) { } else if (node instanceof AST_Dot) {
node.property = mangle(node.property); node.property = mangle(node.property);
} else if (node instanceof AST_ObjectKeyVal) {
node.key = mangle(node.key);
} else if (node instanceof AST_ObjectProperty) { } else if (node instanceof AST_ObjectProperty) {
// setter or getter if (typeof node.key == "string") node.key = mangle(node.key);
node.key.name = mangle(node.key.name);
} else if (node instanceof AST_Sub) { } else if (node instanceof AST_Sub) {
if (!options.keep_quoted) mangleStrings(node.property); if (!options.keep_quoted) mangleStrings(node.property);
} }

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,10 +222,24 @@ 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) {
sym.scope.uses_arguments = true; if (!(tw.parent() instanceof AST_PropAccess)) {
sym.scope.uses_arguments = "d";
} else if (!sym.scope.uses_arguments) {
sym.scope.uses_arguments = true;
}
} }
if (name == "eval") { if (name == "eval") {
var parent = tw.parent(); var parent = tw.parent();
@@ -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,10 +160,21 @@ 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);
}); });
DEF(AST_ObjectProperty, function(self, tw) { DEF(AST_ObjectProperty, function(self, tw) {
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
self.value = self.value.transform(tw); self.value = self.value.transform(tw);
}); });
})(function(node, descend) { })(function(node, descend) {

View File

@@ -143,8 +143,9 @@ function push_uniq(array, el) {
} }
function string_template(text, props) { function string_template(text, props) {
return text.replace(/\{(.+?)\}/g, function(str, p) { return text.replace(/\{([^}]+)\}/g, function(str, p) {
return props && props[p]; var value = props[p];
return value instanceof AST_Node ? value.print_to_string() : value;
}); });
} }

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.4", "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
@@ -315,6 +314,7 @@ function test_case(test) {
if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties); if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties);
} }
var output_code = make_code(output, output_options); var output_code = make_code(output, output_options);
U.AST_Node.log_function();
if (expect != output_code) { if (expect != output_code) {
log([ log([
"!!! failed", "!!! failed",
@@ -386,7 +386,7 @@ function test_case(test) {
mangle: test.mangle mangle: test.mangle
}); });
var actual = stdout[toplevel ? 1 : 0]; var actual = stdout[toplevel ? 1 : 0];
if (test.expect_stdout === true) { if (test.expect_stdout === true || test.expect_stdout instanceof Error && test.expect_stdout.name === actual.name) {
test.expect_stdout = actual; test.expect_stdout = actual;
} }
if (!sandbox.same_stdout(test.expect_stdout, actual)) { if (!sandbox.same_stdout(test.expect_stdout, actual)) {

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

@@ -8577,3 +8577,28 @@ issue_4242: {
} }
expect_stdout: "undefined" expect_stdout: "undefined"
} }
issue_4248: {
options = {
collapse_vars: true,
}
input: {
var a = 0;
try {
a = 1;
b[1];
} catch (e) {
console.log(a);
}
}
expect: {
var a = 0;
try {
a = 1;
b[1];
} catch (e) {
console.log(a);
}
}
expect_stdout: "1"
}

View File

@@ -83,13 +83,11 @@ ifs_3_should_warn: {
"WARN: Condition left of && always false [test/compress/conditionals.js:3,12]", "WARN: Condition left of && always false [test/compress/conditionals.js:3,12]",
"WARN: Condition always false [test/compress/conditionals.js:3,12]", "WARN: Condition always false [test/compress/conditionals.js:3,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:3,34]", "WARN: Dropping unreachable code [test/compress/conditionals.js:3,34]",
"WARN: Declarations in unreachable code! [test/compress/conditionals.js:4,12]",
"WARN: + in boolean context always true [test/compress/conditionals.js:10,19]", "WARN: + in boolean context always true [test/compress/conditionals.js:10,19]",
"WARN: Boolean || always true [test/compress/conditionals.js:10,12]", "WARN: Boolean || always true [test/compress/conditionals.js:10,12]",
"WARN: Condition left of || always true [test/compress/conditionals.js:10,12]", "WARN: Condition left of || always true [test/compress/conditionals.js:10,12]",
"WARN: Condition always true [test/compress/conditionals.js:10,12]", "WARN: Condition always true [test/compress/conditionals.js:10,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:12,15]", "WARN: Dropping unreachable code [test/compress/conditionals.js:12,15]",
"WARN: Declarations in unreachable code! [test/compress/conditionals.js:13,12]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]", "WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:10,12]", "WARN: Dropping side-effect-free statement [test/compress/conditionals.js:10,12]",
] ]

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,
@@ -1084,3 +1143,207 @@ issue_4231: {
} }
expect_stdout: "function" expect_stdout: "function"
} }
issue_4245: {
options = {
booleans: true,
}
input: {
const a = f();
function f() {
typeof a;
}
}
expect: {
const a = f();
function f() {
a,
1;
}
}
expect_stdout: true
}
issue_4248: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
try {
(function() {
a = "PASS";
b[a];
const b = 0;
})();
} catch (e) {
console.log(a);
}
}
expect: {
var a = "FAIL";
try {
(function() {
a = "PASS";
b[a];
const b = 0;
})();
} catch (e) {
console.log(a);
}
}
expect_stdout: "PASS"
}
issue_4261: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
{
const a = 42;
(function() {
function f() {
console.log(a);
}
function g() {
while (f());
}
(function() {
while (g());
})();
})();
}
}
expect: {
{
const a = 42;
(function() {
function g() {
while (void console.log(a));
}
(function() {
while (g());
})();
})();
}
}
expect_stdout: "42"
}
issue_4274_1: {
options = {
loops: true,
}
input: {
for (;;) {
if (console.log("PASS")) {
const a = 0;
} else {
break;
var a;
}
}
}
expect: {
for (; console.log("PASS");) {
{
const a = 0;
}
var a;
}
}
expect_stdout: true
}
issue_4274_2: {
options = {
loops: true,
}
input: {
for (;;) {
if (!console.log("PASS")) {
break;
var a;
} else {
const a = 0;
}
}
}
expect: {
for (; console.log("PASS");) {
{
const a = 0;
}
var a;
}
}
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
}

View File

@@ -61,8 +61,6 @@ dead_code_2_should_warn: {
expect_stdout: true expect_stdout: true
expect_warnings: [ expect_warnings: [
"WARN: Dropping unreachable code [test/compress/dead-code.js:8,12]", "WARN: Dropping unreachable code [test/compress/dead-code.js:8,12]",
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:10,16]",
"WARN: Dropping unreachable code [test/compress/dead-code.js:10,16]",
] ]
node_version: "<=4" node_version: "<=4"
} }
@@ -103,11 +101,9 @@ dead_code_constant_boolean_should_warn_more: {
"WARN: + in boolean context always true [test/compress/dead-code.js:1,33]", "WARN: + in boolean context always true [test/compress/dead-code.js:1,33]",
"WARN: Boolean || always true [test/compress/dead-code.js:1,16]", "WARN: Boolean || always true [test/compress/dead-code.js:1,16]",
"WARN: Dropping unreachable code [test/compress/dead-code.js:1,45]", "WARN: Dropping unreachable code [test/compress/dead-code.js:1,45]",
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:3,12]",
"WARN: Boolean expression always true [test/compress/dead-code.js:6,47]", "WARN: Boolean expression always true [test/compress/dead-code.js:6,47]",
"WARN: Boolean && always false [test/compress/dead-code.js:6,28]", "WARN: Boolean && always false [test/compress/dead-code.js:6,28]",
"WARN: Dropping unreachable code [test/compress/dead-code.js:6,63]", "WARN: Dropping unreachable code [test/compress/dead-code.js:6,63]",
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:9,12]",
"WARN: Dropping side-effect-free statement [test/compress/dead-code.js:1,15]", "WARN: Dropping side-effect-free statement [test/compress/dead-code.js:1,15]",
"WARN: Dropping side-effect-free statement [test/compress/dead-code.js:6,28]", "WARN: Dropping side-effect-free statement [test/compress/dead-code.js:6,28]",
] ]

File diff suppressed because it is too large Load Diff

View File

@@ -3047,3 +3047,30 @@ issue_4214: {
} }
expect_stdout: "NaN" expect_stdout: "NaN"
} }
issue_4271: {
options = {
evaluate: true,
unsafe: true,
}
input: {
({
p: null,
q: (console.log("foo"), 42),
p: function() {}
})[console.log("bar"), "p"] && console.log("PASS");
}
expect: {
({
p: null,
q: (console.log("foo"), 42),
p: function() {}
})[console.log("bar"), "p"],
console.log("PASS");
}
expect_stdout: [
"foo",
"bar",
"PASS",
]
}

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,
@@ -2081,7 +2082,7 @@ issue_3016_1: {
var b = 1; var b = 1;
do { do {
3[b]; 3[b];
} while(0); } while (0);
console.log(b); console.log(b);
} }
expect_stdout: "1" expect_stdout: "1"
@@ -2112,7 +2113,7 @@ issue_3016_2: {
do { do {
a = 3, a = 3,
a[b]; a[b];
} while(0); } while (0);
var a; var a;
console.log(b); console.log(b);
} }
@@ -2145,7 +2146,7 @@ issue_3016_2_ie8: {
do { do {
a = 3, a = 3,
a[b]; a[b];
} while(0); } while (0);
var a; var a;
console.log(b); console.log(b);
} }
@@ -2175,7 +2176,7 @@ issue_3016_3: {
var b = 1; var b = 1;
do { do {
console.log((a = void 0, a ? "FAIL" : a = "PASS")); console.log((a = void 0, a ? "FAIL" : a = "PASS"));
} while(b--); } while (b--);
var a; var a;
} }
expect_stdout: [ expect_stdout: [
@@ -2208,7 +2209,7 @@ issue_3016_3_ie8: {
var b = 1; var b = 1;
do { do {
console.log((a = void 0, a ? "FAIL" : a = "PASS")); console.log((a = void 0, a ? "FAIL" : a = "PASS"));
} while(b--); } while (b--);
var a; var a;
} }
expect_stdout: [ expect_stdout: [
@@ -5115,3 +5116,102 @@ issue_4233: {
} }
expect_stdout: "number" expect_stdout: "number"
} }
issue_4259: {
options = {
collapse_vars: true,
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function b() {
var c = b;
for (b in c);
};
a();
console.log(typeof a);
}
expect: {
function a() {
for (a in a);
}
a();
console.log(typeof a);
}
expect_stdout: "function"
}
issue_4261: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
try {
throw 42;
} catch (e) {
(function() {
function f() {
e.p;
}
function g() {
while (f());
}
(function() {
while (console.log(g()));
})();
})();
}
}
expect: {
try {
throw 42;
} catch (e) {
(function() {
function g() {
while (void e.p);
}
(function() {
while (console.log(g()));
})();
})();
}
}
expect_stdout: true
}
issue_4265: {
options = {
conditionals: true,
dead_code: true,
inline: true,
sequences: true,
}
input: {
function f() {
console;
if ([ function() {
return this + console.log(a);
a;
var a;
}() ]);
return 0;
}
f();
}
expect: {
function f() {
return console, function() {
return console.log(a);
var a;
}(), 0;
}
f();
}
expect_stdout: "undefined"
}

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

@@ -2877,3 +2877,30 @@ issue_4235: {
} }
expect_stdout: "undefined" expect_stdout: "undefined"
} }
issue_4250: {
options = {
ie8: true,
loops: true,
unused: true,
}
input: {
console.log(function f() {
(function() {
for (f in "f");
})();
return f;
var f;
}());
}
expect: {
console.log(function f() {
(function() {
for (f in "f");
})();
return f;
var f;
}());
}
expect_stdout: "0"
}

View File

@@ -39,7 +39,7 @@ non_hoisted_function_after_return: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:4,16]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:4,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:7,16]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:7,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:11,21]" "WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:11,21]",
] ]
} }
@@ -84,15 +84,12 @@ non_hoisted_function_after_return_2a: {
} }
} }
expect_warnings: [ expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:4,16]", "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:4,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:4,16]", "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:7,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:7,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]",
"INFO: pass 0: last_count: Infinity, count: 35", "INFO: pass 0: last_count: Infinity, count: 35",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]", "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:9,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]",
"INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]", "INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]",
"INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]", "INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]",
@@ -138,10 +135,7 @@ non_hoisted_function_after_return_2b: {
} }
} }
expect_warnings: [ expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:6,16]", "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:8,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:6,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:8,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]",
] ]
} }
@@ -242,15 +236,12 @@ non_hoisted_function_after_return_2a_strict: {
} }
expect_stdout: "5 6" expect_stdout: "5 6"
expect_warnings: [ expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:5,16]", "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:5,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:5,16]", "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:8,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:8,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]",
"INFO: pass 0: last_count: Infinity, count: 46", "INFO: pass 0: last_count: Infinity, count: 46",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]", "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:10,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:10,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]",
"INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]", "INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]",
"INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]", "INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]",
@@ -301,10 +292,7 @@ non_hoisted_function_after_return_2b_strict: {
} }
expect_stdout: "5 6" expect_stdout: "5 6"
expect_warnings: [ expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:7,16]", "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:9,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]",
] ]
} }

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,
@@ -893,3 +913,318 @@ issue_4231: {
expect_stdout: "function" expect_stdout: "function"
node_version: ">=4" node_version: ">=4"
} }
issue_4245: {
options = {
booleans: true,
}
input: {
"use strict";
let a = f();
function f() {
typeof a;
}
}
expect: {
"use strict";
let a = f();
function f() {
a,
1;
}
}
expect_stdout: ReferenceError("a is not defined")
node_version: ">=4"
}
issue_4248: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
try {
(function() {
"use strict";
a = "PASS";
b[a];
let b;
})();
} catch (e) {
console.log(a);
}
}
expect: {
var a = "FAIL";
try {
(function() {
"use strict";
a = "PASS";
b[a];
let b;
})();
} catch (e) {
console.log(a);
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4274_1: {
options = {
loops: true,
}
input: {
"use strict";
for (;;) {
if (console.log("PASS")) {
let a;
} else {
break;
var a;
}
}
}
expect: {
"use strict";
for (; console.log("PASS");) {
{
let a;
}
var a;
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4274_2: {
options = {
loops: true,
}
input: {
"use strict";
for (;;) {
if (!console.log("PASS")) {
break;
var a;
} else {
let a;
}
}
}
expect: {
"use strict";
for (; console.log("PASS");) {
{
let a;
}
var a;
}
}
expect_stdout: "PASS"
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

@@ -3092,3 +3092,94 @@ issue_4237_2: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_4253: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
switch (0) {
default:
var a = "FAIL";
a = a && a;
try {
break;
} catch (e) {}
var b = 42;
}
console.log(b);
}
expect: {
switch (0) {
default:
var a = "FAIL";
a = a && a;
try {
break;
} catch (e) {}
var b = 42;
}
console.log(b);
}
expect_stdout: "undefined"
}
issue_4255: {
options = {
dead_code: true,
loops: true,
merge_vars: true,
toplevel: true,
}
input: {
L: for (var a = 2; --a;)
for (var b = 0; console.log(b); --b)
break L;
}
expect: {
L: for (var a = 2; --a;) {
var b = 0;
if (console.log(b))
break L;
}
}
expect_stdout: "0"
}
issue_4257: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a = 0;
for (var i = 0; i < 2; i++)
switch (--a) {
case 0:
var b = 0;
break;
case 0:
default:
var c = 1 + (0 | (b && A));
console.log(c);
}
}
expect: {
var a = 0;
for (var i = 0; i < 2; i++)
switch (--a) {
case 0:
var b = 0;
break;
case 0:
default:
var c = 1 + (0 | (b && A));
console.log(c);
}
}
expect_stdout: [
"1",
"1",
]
}

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

@@ -221,3 +221,142 @@ numeric_literal: {
"8 7 8", "8 7 8",
] ]
} }
evaluate_computed_key: {
options = {
evaluate: true,
objects: true,
}
input: {
console.log({
["foo" + "bar"]: "PASS",
}.foobar);
}
expect: {
console.log({
foobar: "PASS",
}.foobar);
}
expect_stdout: "PASS"
node_version: ">=4"
}
keep_computed_key: {
options = {
side_effects: true,
}
input: {
({
[console.log("PASS")]: 42,
});
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4269_1: {
options = {
evaluate: true,
objects: true,
}
input: {
console.log({
get 0() {
return "FAIL";
},
[0]: "PASS",
}[0]);
}
expect: {
console.log({
get 0() {
return "FAIL";
},
[0]: "PASS",
}[0]);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4269_2: {
options = {
evaluate: true,
objects: true,
}
input: {
console.log({
get [0]() {
return "FAIL";
},
0: "PASS",
}[0]);
}
expect: {
console.log({
get [0]() {
return "FAIL";
},
0: "PASS",
}[0]);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4269_3: {
options = {
evaluate: true,
objects: true,
}
input: {
console.log({
["foo"]: "bar",
get 42() {
return "FAIL";
},
42: "PASS",
}[42]);
}
expect: {
console.log({
["foo"]: "bar",
get 42() {
return "FAIL";
},
42: "PASS",
}[42]);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4269_4: {
options = {
evaluate: true,
objects: true,
}
input: {
console.log({
get 42() {
return "FAIL";
},
["foo"]: "bar",
42: "PASS",
}[42]);
}
expect: {
console.log({
get 42() {
return "FAIL";
},
["foo"]: "bar",
42: "PASS",
}[42]);
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -1999,7 +1999,7 @@ issue_1606: {
var a, b; var a, b;
function g(){}; function g(){};
b = 2; b = 2;
x(b); x(2);
} }
} }
} }

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

@@ -5,7 +5,7 @@ describe("tokens", function() {
it("Should give correct positions for accessors", function() { it("Should give correct positions for accessors", function() {
// location 0 1 2 3 4 // location 0 1 2 3 4
// 01234567890123456789012345678901234567890123456789 // 01234567890123456789012345678901234567890123456789
var ast = UglifyJS.parse("var obj = { get latest() { return undefined; } }"); var ast = UglifyJS.parse("var obj = { get [prop]() { return undefined; } }");
// test all AST_ObjectProperty tokens are set as expected // test all AST_ObjectProperty tokens are set as expected
var found = false; var found = false;
ast.walk(new UglifyJS.TreeWalker(function(node) { ast.walk(new UglifyJS.TreeWalker(function(node) {
@@ -13,9 +13,9 @@ describe("tokens", function() {
found = true; found = true;
assert.equal(node.start.pos, 12); assert.equal(node.start.pos, 12);
assert.equal(node.end.endpos, 46); assert.equal(node.end.endpos, 46);
assert(node.key instanceof UglifyJS.AST_SymbolAccessor); assert(node.key instanceof UglifyJS.AST_SymbolRef);
assert.equal(node.key.start.pos, 16); assert.equal(node.key.start.pos, 17);
assert.equal(node.key.end.endpos, 22); assert.equal(node.key.end.endpos, 21);
assert(node.value instanceof UglifyJS.AST_Accessor); assert(node.value instanceof UglifyJS.AST_Accessor);
assert.equal(node.value.start.pos, 22); assert.equal(node.value.start.pos, 22);
assert.equal(node.value.end.endpos, 46); assert.equal(node.value.end.endpos, 46);

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
@@ -593,7 +613,7 @@ function is_error(result) {
} }
function is_timed_out(result) { function is_timed_out(result) {
return is_error(result) && /timed out/.test(result); return is_error(result) && /timed out/.test(result.message);
} }
function is_statement(node) { function is_statement(node) {

View File

@@ -276,6 +276,7 @@ var NO_DEFUN = false;
var DEFUN_OK = true; var DEFUN_OK = true;
var DONT_STORE = true; var DONT_STORE = true;
var NO_CONST = true; var NO_CONST = true;
var NO_DUPLICATE = true;
var VAR_NAMES = [ var VAR_NAMES = [
"a", "a",
@@ -356,11 +357,15 @@ function createFunctions(n, recurmax, allowDefun, canThrow, stmtDepth) {
return s; return s;
} }
function createParams() { function createParams(noDuplicate) {
var len = unique_vars.length;
var params = []; var params = [];
for (var n = rng(4); --n >= 0;) { for (var n = rng(4); --n >= 0;) {
params.push(createVarName(MANDATORY)); var name = createVarName(MANDATORY);
if (noDuplicate) unique_vars.push(name);
params.push(name);
} }
unique_vars.length = len;
return params.join(", "); return params.join(", ");
} }
@@ -372,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;
@@ -410,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 + " ";
var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow); switch (rng(10)) {
VAR_NAMES.push(name); case 0:
return name + " = " + value; while (!rng(10)) names.splice(rng(names.length + 1), 0, "");
}).join(", ") + ";"; 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_NAMES.push(name);
return name + " = " + value;
}).join(", ") + ";";
break;
}
VAR_NAMES = save.concat(VAR_NAMES.slice(len)); VAR_NAMES = save.concat(VAR_NAMES.slice(len));
return s; return s;
} }
@@ -424,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 {
@@ -434,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.
@@ -445,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 + ";";
@@ -556,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),
"}}", "}}",
@@ -571,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);
@@ -714,7 +871,28 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
return getVarName(); return getVarName();
case p++: case p++:
return getVarName(NO_CONST) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); 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);
}
case p++: case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: case p++:
@@ -859,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) + ")";
} }
@@ -908,21 +1089,27 @@ function getDotKey(assign) {
return key; return key;
} }
function createAccessor(recurmax, stmtDepth, canThrow) { function createObjectKey(recurmax, stmtDepth, canThrow) {
return rng(10) ? KEYS[rng(KEYS.length)] : "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]";
}
function createObjectFunction(recurmax, stmtDepth, canThrow) {
var namesLenBefore = VAR_NAMES.length; var namesLenBefore = VAR_NAMES.length;
var s; var s;
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
var prop1 = getDotKey(); switch (rng(3)) {
if (rng(2) == 0) { case 0:
s = [ s = [
"get " + prop1 + "(){", "get " + createObjectKey(recurmax, stmtDepth, canThrow) + "(){",
strictMode(), strictMode(),
defns(), defns(),
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), _createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC), createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
"}," "},",
]; ];
} else { break;
case 1:
var prop1 = createObjectKey(recurmax, stmtDepth, canThrow);
var prop2; var prop2;
do { do {
prop2 = getDotKey(); prop2 = getDotKey();
@@ -933,8 +1120,18 @@ function createAccessor(recurmax, stmtDepth, canThrow) {
defns(), defns(),
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), _createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"this." + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";", "this." + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
"}," "},",
]; ];
break;
default:
s = [
createObjectKey(recurmax, stmtDepth, canThrow) + "(" + createParams(NO_DUPLICATE) + "){",
strictMode(),
defns(),
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"},",
]
break;
} }
}); });
VAR_NAMES.length = namesLenBefore; VAR_NAMES.length = namesLenBefore;
@@ -944,13 +1141,16 @@ function createAccessor(recurmax, stmtDepth, canThrow) {
function createObjectLiteral(recurmax, stmtDepth, canThrow) { function createObjectLiteral(recurmax, stmtDepth, canThrow) {
recurmax--; recurmax--;
var obj = ["({"]; var obj = ["({"];
for (var i = rng(6); --i >= 0;) { for (var i = rng(6); --i >= 0;) switch (rng(30)) {
if (rng(20) == 0) { case 0:
obj.push(createAccessor(recurmax, stmtDepth, canThrow)); obj.push(createObjectFunction(recurmax, stmtDepth, canThrow));
} else { break;
var key = KEYS[rng(KEYS.length)]; case 1:
obj.push(key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "),"); obj.push(getVarName() + ",");
} break;
default:
obj.push(createObjectKey(recurmax, stmtDepth, canThrow) + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "),");
break;
} }
obj.push("})"); obj.push("})");
return obj.join("\n"); return obj.join("\n");
@@ -978,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);
@@ -1327,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++) {
@@ -1369,7 +1640,12 @@ for (var round = 1; round <= num_iterations; round++) {
} }
} }
// ignore difference in error message caused by Temporal Dead Zone // ignore difference in error message caused by Temporal Dead Zone
if (!ok && errored) ok = uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError"; if (!ok && errored && uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError") ok = true;
// ignore spurious time-outs
if (!ok && errored && /timed out/.test(original_result.message) && !/timed out/.test(uglify_result.message)) {
if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = sandbox.run_code(original_code, toplevel, 10000);
ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result);
}
// ignore difference in error message caused by `in` // ignore difference in error message caused by `in`
// ignore difference in depth of termination caused by infinite recursion // ignore difference in depth of termination caused by infinite recursion
if (!ok) { if (!ok) {