Compare commits

..

52 Commits

Author SHA1 Message Date
Alex Lam S.L
766742e1d3 v3.12.8 2021-02-13 21:38:27 +08:00
Alex Lam S.L
94e8944f67 avoid false positive in --reduce-test (#4648) 2021-02-13 21:15:11 +08:00
Alex Lam S.L
83197ffdb3 fix corner case in evaluate (#4645)
fixes #4644
2021-02-12 03:26:12 +08:00
Alex Lam S.L
952765be66 enhance join_vars (#4643) 2021-02-11 04:59:25 +08:00
Alex Lam S.L
083679bcad fix corner cases with asynchronous generators (#4642)
fixes #4641
2021-02-10 23:41:00 +08:00
Alex Lam S.L
f5659f292b enhance collapse_vars (#4637) 2021-02-10 23:06:12 +08:00
Alex Lam S.L
c6e287331d fix corner cases in inline (#4640)
fixes #4639
2021-02-10 20:40:57 +08:00
Alex Lam S.L
a98ec7e4df enhance side_effects (#4638) 2021-02-10 18:09:54 +08:00
Alex Lam S.L
5ec82e5801 fix corner case in reduce_vars (#4636) 2021-02-10 10:37:00 +08:00
Alex Lam S.L
c76481341c fix corner case in merge_vars (#4635) 2021-02-10 09:13:38 +08:00
Alex Lam S.L
5e6307974f fix corner case in collapse_vars (#4634)
fixes #4633
2021-02-10 08:45:36 +08:00
Alex Lam S.L
228cdf8e7e reject invalid for...of syntax (#4632) 2021-02-10 05:42:27 +08:00
Alex Lam S.L
14fedbf123 fix corner case with template literals (#4631)
fixes #4630
2021-02-09 14:21:15 +08:00
Alex Lam S.L
fcee32527b fix corner case in merge_vars (#4629)
fixes #4628
2021-02-09 12:36:12 +08:00
Alex Lam S.L
e13d1e9969 support for [await]...of statements (#4627) 2021-02-09 04:28:23 +08:00
Alex Lam S.L
aedc1e7fc9 improve false positive detection in ufuzz (#4626) 2021-02-08 20:17:14 +08:00
Alex Lam S.L
353f654038 fix corner case in --reduce-test (#4625) 2021-02-08 19:16:21 +08:00
Alex Lam S.L
357d861246 fix corner case in conditionals (#4624)
fixes #4623
2021-02-08 18:31:08 +08:00
Alex Lam S.L
fd4caf7a9c support generator functions (#4620) 2021-02-08 06:44:20 +08:00
Alex Lam S.L
c44b6399c3 fix corner case in side_effects (#4622)
fixes #4621
2021-02-07 22:40:41 +08:00
Alex Lam S.L
522cceeccf fix corner case in functions (#4619)
fixes #4618
2021-02-07 13:52:09 +08:00
Alex Lam S.L
5c84dfa151 v3.12.7 2021-02-07 02:43:47 +08:00
Alex Lam S.L
5359900b78 enhance compress on arrow and async functions (#4616) 2021-02-06 12:39:46 +08:00
Alex Lam S.L
739fa266f8 fix corner case in pure_getters (#4615)
fixes #4614
2021-02-05 09:03:51 +08:00
Alex Lam S.L
da24dfb59e fix corner cases with function inlining (#4613)
fixes #4612
2021-02-05 04:49:37 +08:00
Alex Lam S.L
a2f27c7640 fix corner cases in templates (#4610) 2021-02-02 17:39:30 +00:00
Alex Lam S.L
3c556b8689 fix corner case in arguments (#4609)
fixes #4608
2021-02-02 23:07:31 +08:00
Alex Lam S.L
7110c6923b fix corner case in templates (#4607)
fixes #4606
2021-02-02 02:29:43 +08:00
Alex Lam S.L
b27b6807cb fix corner case in collapse_vars (#4605)
fixes #4604
2021-02-01 23:24:11 +08:00
Alex Lam S.L
ba6e29d6fd introduce templates (#4603) 2021-02-01 17:20:13 +08:00
Alex Lam S.L
d4685640a0 support template literals (#4601) 2021-02-01 10:36:45 +08:00
Alex Lam S.L
ac7b5c07d7 v3.12.6 2021-02-01 01:37:32 +08:00
Alex Lam S.L
0cd4a199b0 fix corner case in conditionals (#4599)
fixes #4598
2021-01-30 16:54:29 +08:00
Alex Lam S.L
35435d4bd3 suppress false positives due to nested objects (#4597) 2021-01-29 13:21:19 +08:00
Alex Lam S.L
d0bb147639 fix corner case in inline (#4596)
fixes #4595
2021-01-27 01:30:05 +08:00
Alex Lam S.L
4723b4541e workaround tty bugs on Node.js (#4594) 2021-01-26 23:07:48 +08:00
Alex Lam S.L
9d23ba0a22 support exponentiation operator (#4593) 2021-01-25 05:48:51 +08:00
Alex Lam S.L
a08d42555a fix infinite recursion in ufuzz code generation (#4592) 2021-01-24 23:37:57 +08:00
Alex Lam S.L
fd7ad8e779 fix corner cases in collapse_vars (#4591)
fixes #4590
2021-01-24 22:15:43 +08:00
Alex Lam S.L
a36c5472d2 fix corner cases with default parameters (#4589)
fixes #4588
2021-01-24 11:00:47 +08:00
Alex Lam S.L
8bfd891c09 support BigInt literals (#4583) 2021-01-24 09:51:18 +08:00
Alex Lam S.L
ef9f7ca3e7 fix corner case in collapse_vars (#4587)
fixes #4586
2021-01-24 07:05:43 +08:00
Alex Lam S.L
acc443b2cf fix corner case in reduce_vars (#4585)
fixes #4584
2021-01-24 03:37:52 +08:00
Alex Lam S.L
f87e7be12c fix corner case in reduce_vars (#4582)
fixes #4581
2021-01-23 02:14:53 +08:00
Alex Lam S.L
c0614654d9 improve ufuzz on destructuring (#4580) 2021-01-23 02:00:26 +08:00
Alex Lam S.L
0358637725 workaround Node.js bug (#4579) 2021-01-22 11:34:30 +08:00
Alex Lam S.L
63b5b6d2b3 suppress false positives in ufuzz (#4578) 2021-01-22 02:33:00 +08:00
Alex Lam S.L
e675262d51 suppress false positives in ufuzz (#4577) 2021-01-21 14:33:31 +08:00
Alex Lam S.L
c1e771a89a fix corner case in rests (#4576)
fixes #4575
2021-01-21 07:23:06 +08:00
Alex Lam S.L
bc7a88baea suppress false positives in ufuzz (#4574) 2021-01-20 21:03:33 +08:00
Alex Lam S.L
018e0350f8 workaround GitHub Actions bug (#4573) 2021-01-20 20:17:58 +08:00
Alex Lam S.L
d37ee4d41c support asynchronous test cases properly (#4529) 2021-01-20 07:27:32 +08:00
47 changed files with 4483 additions and 870 deletions

View File

@@ -32,6 +32,8 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install GNU Core Utilities - name: Install GNU Core Utilities
if: ${{ startsWith(matrix.os, 'macos') }} if: ${{ startsWith(matrix.os, 'macos') }}
env:
HOMEBREW_NO_INSTALL_CLEANUP: 1
shell: bash shell: bash
run: | run: |
brew install coreutils brew install coreutils

View File

@@ -769,6 +769,9 @@ to be `false` and all symbol names will be omitted.
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches - `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `templates` (default: `true`) -- compact template literals by embedding expressions
and/or converting to string literals, e.g. `` `foo ${42}` → "foo 42"``
- `top_retain` (default: `null`) -- prevent specific toplevel functions and - `top_retain` (default: `null`) -- prevent specific toplevel functions and
variables from `unused` removal (can be array, comma-separated, RegExp or variables from `unused` removal (can be array, comma-separated, RegExp or
function. Implies `toplevel`) function. Implies `toplevel`)
@@ -808,6 +811,8 @@ to be `false` and all symbol names will be omitted.
- `varify` (default: `true`) -- convert block-scoped declaractions into `var` - `varify` (default: `true`) -- convert block-scoped declaractions into `var`
whenever safe to do so whenever safe to do so
- `yields` (default: `true`) -- apply optimizations to `yield` expressions
## Mangle options ## Mangle options
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes - `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
@@ -1254,3 +1259,25 @@ To allow for better optimizations, the compiler makes various assumptions:
}()) => b)()); }()) => b)());
``` ```
UglifyJS may modify the input which in turn may suppress those errors. UglifyJS may modify the input which in turn may suppress those errors.
- Some arithmetic operations with `BigInt` may throw `TypeError`:
```javascript
1n + 1;
// TypeError: can't convert BigInt to number
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of JavaScript will throw `SyntaxError` with the
following:
```javascript
console.log(String.raw`\uFo`);
// SyntaxError: Invalid Unicode escape sequence
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of JavaScript will throw `SyntaxError` with the
following:
```javascript
try {} catch (e) {
for (var e of []);
}
// SyntaxError: Identifier 'e' has already been declared
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -113,7 +113,9 @@ var AST_Node = DEFNODE("Node", "start end", {
walk: function(visitor) { walk: function(visitor) {
visitor.visit(this); visitor.visit(this);
}, },
_validate: noop, _validate: function() {
if (this.TYPE == "Node") throw new Error("should not instantiate AST_Node");
},
validate: function() { validate: function() {
var ctor = this.CTOR; var ctor = this.CTOR;
do { do {
@@ -187,6 +189,9 @@ AST_Node.disable_validation = function() {
var AST_Statement = DEFNODE("Statement", null, { var AST_Statement = DEFNODE("Statement", null, {
$documentation: "Base class of all statements", $documentation: "Base class of all statements",
_validate: function() {
if (this.TYPE == "Statement") throw new Error("should not instantiate AST_Statement");
},
}); });
var AST_Debugger = DEFNODE("Debugger", null, { var AST_Debugger = DEFNODE("Debugger", null, {
@@ -215,7 +220,7 @@ function validate_expression(value, prop, multiple, allow_spread, allow_hole) {
if (value instanceof AST_Destructured) throw new Error(prop + " cannot " + multiple + " AST_Destructured"); if (value instanceof AST_Destructured) throw new Error(prop + " cannot " + multiple + " AST_Destructured");
if (value instanceof AST_Hole && !allow_hole) throw new Error(prop + " cannot " + multiple + " AST_Hole"); if (value instanceof AST_Hole && !allow_hole) throw new Error(prop + " cannot " + multiple + " AST_Hole");
if (value instanceof AST_Spread && !allow_spread) throw new Error(prop + " cannot " + multiple + " AST_Spread"); if (value instanceof AST_Spread && !allow_spread) throw new Error(prop + " cannot " + multiple + " AST_Spread");
if (value instanceof AST_Statement && !is_function(value)) { if (value instanceof AST_Statement && !(value instanceof AST_LambdaExpression)) {
throw new Error(prop + " cannot " + multiple + " AST_Statement"); throw new Error(prop + " cannot " + multiple + " AST_Statement");
} }
if (value instanceof AST_SymbolDeclaration) { if (value instanceof AST_SymbolDeclaration) {
@@ -249,7 +254,7 @@ var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_s
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
functions: "[Object/S] like `variables`, but only lists function declarations", functions: "[Object/S] like `variables`, but only lists function declarations",
parent_scope: "[AST_Scope?/S] link to the parent scope", parent_scope: "[AST_Scope?/S] link to the parent scope",
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope", variables: "[Object/S] a map of name ---> SymbolDef for all variables/functions defined in this scope",
}, },
clone: function(deep) { clone: function(deep) {
var node = this._clone(deep); var node = this._clone(deep);
@@ -265,6 +270,7 @@ var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_s
return this.parent_scope.resolve(); return this.parent_scope.resolve();
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "BlockScope") throw new Error("should not instantiate AST_BlockScope");
if (this.parent_scope == null) return; if (this.parent_scope == null) return;
if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope"); if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope");
if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope"); if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope");
@@ -289,9 +295,10 @@ var AST_Block = DEFNODE("Block", "body", {
}); });
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "Block") throw new Error("should not instantiate AST_Block");
this.body.forEach(function(node) { this.body.forEach(function(node) {
if (!(node instanceof AST_Statement)) throw new Error("body must be AST_Statement[]"); if (!(node instanceof AST_Statement)) throw new Error("body must be AST_Statement[]");
if (is_function(node)) throw new Error("body cannot contain AST_Function"); if (node instanceof AST_LambdaExpression) throw new Error("body cannot contain AST_LambdaExpression");
}); });
}, },
}, AST_BlockScope); }, AST_BlockScope);
@@ -306,8 +313,9 @@ var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "StatementWithBody") throw new Error("should not instantiate AST_StatementWithBody");
if (!(this.body instanceof AST_Statement)) throw new Error("body must be AST_Statement"); if (!(this.body instanceof AST_Statement)) throw new Error("body must be AST_Statement");
if (is_function(this.body)) throw new Error("body cannot be AST_Function"); if (this.body instanceof AST_LambdaExpression) throw new Error("body cannot be AST_LambdaExpression");
}, },
}, AST_BlockScope); }, AST_BlockScope);
@@ -346,7 +354,10 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
}, AST_StatementWithBody); }, AST_StatementWithBody);
var AST_IterationStatement = DEFNODE("IterationStatement", null, { var AST_IterationStatement = DEFNODE("IterationStatement", null, {
$documentation: "Internal class. All loops inherit from it." $documentation: "Internal class. All loops inherit from it.",
_validate: function() {
if (this.TYPE == "IterationStatement") throw new Error("should not instantiate AST_IterationStatement");
},
}, AST_StatementWithBody); }, AST_StatementWithBody);
var AST_DWLoop = DEFNODE("DWLoop", "condition", { var AST_DWLoop = DEFNODE("DWLoop", "condition", {
@@ -355,6 +366,7 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", {
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop");
must_be_expression(this, "condition"); must_be_expression(this, "condition");
}, },
}, AST_IterationStatement); }, AST_IterationStatement);
@@ -401,7 +413,7 @@ var AST_For = DEFNODE("For", "init condition step", {
if (this.init != null) { if (this.init != null) {
if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node"); if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node");
if (this.init instanceof AST_Statement if (this.init instanceof AST_Statement
&& !(this.init instanceof AST_Definitions || is_function(this.init))) { && !(this.init instanceof AST_Definitions || this.init instanceof AST_LambdaExpression)) {
throw new Error("init cannot be AST_Statement"); throw new Error("init cannot be AST_Statement");
} }
} }
@@ -410,11 +422,11 @@ var AST_For = DEFNODE("For", "init condition step", {
}, },
}, AST_IterationStatement); }, AST_IterationStatement);
var AST_ForIn = DEFNODE("ForIn", "init object", { var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", {
$documentation: "A `for ... in` statement", $documentation: "Base class for enumeration loops, i.e. `for ... in`, `for ... of` & `for await ... of`",
$propdoc: { $propdoc: {
init: "[AST_Node] the `for/in` initialization code", init: "[AST_Node] the assignment target during iteration",
object: "[AST_Node] the object that we're looping through" object: "[AST_Node] the object to iterate over"
}, },
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
@@ -425,6 +437,7 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
}); });
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "ForEnumeration") throw new Error("should not instantiate AST_ForEnumeration");
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 { } else {
@@ -438,6 +451,18 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
}, },
}, AST_IterationStatement); }, AST_IterationStatement);
var AST_ForIn = DEFNODE("ForIn", null, {
$documentation: "A `for ... in` statement",
}, AST_ForEnumeration);
var AST_ForOf = DEFNODE("ForOf", null, {
$documentation: "A `for ... of` statement",
}, AST_ForEnumeration);
var AST_ForAwaitOf = DEFNODE("ForAwaitOf", null, {
$documentation: "A `for await ... of` statement",
}, AST_ForOf);
var AST_With = DEFNODE("With", "expression", { var AST_With = DEFNODE("With", "expression", {
$documentation: "A `with` statement", $documentation: "A `with` statement",
$propdoc: { $propdoc: {
@@ -467,12 +492,15 @@ var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", {
return this.uses_eval || this.uses_with; return this.uses_eval || this.uses_with;
}, },
resolve: return_this, resolve: return_this,
_validate: function() {
if (this.TYPE == "Scope") throw new Error("should not instantiate AST_Scope");
},
}, AST_Block); }, AST_Block);
var AST_Toplevel = DEFNODE("Toplevel", "globals", { var AST_Toplevel = DEFNODE("Toplevel", "globals", {
$documentation: "The toplevel scope", $documentation: "The toplevel scope",
$propdoc: { $propdoc: {
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", globals: "[Object/S] a map of name ---> SymbolDef for all undeclared names",
}, },
wrap: function(name) { wrap: function(name) {
var body = this.body; var body = this.body;
@@ -517,8 +545,9 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", {
$documentation: "Base class for functions", $documentation: "Base class for functions",
$propdoc: { $propdoc: {
argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals", argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
length_read: "[boolean/S] whether length property of this function is accessed",
rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent", rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array", uses_arguments: "[boolean/S] whether this function accesses the arguments array",
}, },
each_argname: function(visit) { each_argname: function(visit) {
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
@@ -549,6 +578,7 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", {
}); });
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "Lambda") throw new Error("should not instantiate AST_Lambda");
this.argnames.forEach(function(node) { this.argnames.forEach(function(node) {
validate_destructured(node, function(node) { validate_destructured(node, function(node) {
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]"); if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
@@ -567,12 +597,33 @@ var AST_Accessor = DEFNODE("Accessor", null, {
}, },
}, AST_Lambda); }, AST_Lambda);
var AST_LambdaExpression = DEFNODE("LambdaExpression", "inlined", {
$documentation: "Base class for function expressions",
$propdoc: {
inlined: "[boolean/S] whether this function has been inlined",
},
_validate: function() {
if (this.TYPE == "LambdaExpression") throw new Error("should not instantiate AST_LambdaExpression");
},
}, AST_Lambda);
function is_arrow(node) { function is_arrow(node) {
return node instanceof AST_AsyncArrow || node instanceof AST_Arrow; return node instanceof AST_Arrow || node instanceof AST_AsyncArrow;
} }
function is_function(node) { function is_async(node) {
return is_arrow(node) || node instanceof AST_AsyncFunction || node instanceof AST_Function; return node instanceof AST_AsyncArrow
|| node instanceof AST_AsyncDefun
|| node instanceof AST_AsyncFunction
|| node instanceof AST_AsyncGeneratorDefun
|| node instanceof AST_AsyncGeneratorFunction;
}
function is_generator(node) {
return node instanceof AST_AsyncGeneratorDefun
|| node instanceof AST_AsyncGeneratorFunction
|| node instanceof AST_GeneratorDefun
|| node instanceof AST_GeneratorFunction;
} }
function walk_lambda(node, tw) { function walk_lambda(node, tw) {
@@ -583,7 +634,7 @@ function walk_lambda(node, tw) {
} }
} }
var AST_Arrow = DEFNODE("Arrow", "inlined value", { var AST_Arrow = DEFNODE("Arrow", "value", {
$documentation: "An arrow function expression", $documentation: "An arrow function expression",
$propdoc: { $propdoc: {
value: "[AST_Node?] simple return expression, or null if using function body.", value: "[AST_Node?] simple return expression, or null if using function body.",
@@ -610,13 +661,9 @@ var AST_Arrow = DEFNODE("Arrow", "inlined value", {
if (this.body.length) throw new Error("body must be empty if value exists"); if (this.body.length) throw new Error("body must be empty if value exists");
} }
}, },
}, AST_Lambda); }, AST_LambdaExpression);
function is_async(node) { var AST_AsyncArrow = DEFNODE("AsyncArrow", "value", {
return node instanceof AST_AsyncArrow || node instanceof AST_AsyncDefun || node instanceof AST_AsyncFunction;
}
var AST_AsyncArrow = DEFNODE("AsyncArrow", "inlined value", {
$documentation: "An asynchronous arrow function expression", $documentation: "An asynchronous arrow function expression",
$propdoc: { $propdoc: {
value: "[AST_Node?] simple return expression, or null if using function body.", value: "[AST_Node?] simple return expression, or null if using function body.",
@@ -643,9 +690,9 @@ var AST_AsyncArrow = DEFNODE("AsyncArrow", "inlined value", {
if (this.body.length) throw new Error("body must be empty if value exists"); if (this.body.length) throw new Error("body must be empty if value exists");
} }
}, },
}, AST_Lambda); }, AST_LambdaExpression);
var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined name", { var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", {
$documentation: "An asynchronous function expression", $documentation: "An asynchronous function expression",
$propdoc: { $propdoc: {
name: "[AST_SymbolLambda?] the name of this function", name: "[AST_SymbolLambda?] the name of this function",
@@ -655,9 +702,21 @@ var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined name", {
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
} }
}, },
}, AST_Lambda); }, AST_LambdaExpression);
var AST_Function = DEFNODE("Function", "inlined name", { var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", {
$documentation: "An asynchronous generator function expression",
$propdoc: {
name: "[AST_SymbolLambda?] the name of this function",
},
_validate: function() {
if (this.name != null) {
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
}
},
}, AST_LambdaExpression);
var AST_Function = DEFNODE("Function", "name", {
$documentation: "A function expression", $documentation: "A function expression",
$propdoc: { $propdoc: {
name: "[AST_SymbolLambda?] the name of this function", name: "[AST_SymbolLambda?] the name of this function",
@@ -667,36 +726,55 @@ var AST_Function = DEFNODE("Function", "inlined name", {
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
} }
}, },
}, AST_LambdaExpression);
var AST_GeneratorFunction = DEFNODE("GeneratorFunction", "name", {
$documentation: "A generator function expression",
$propdoc: {
name: "[AST_SymbolLambda?] the name of this function",
},
_validate: function() {
if (this.name != null) {
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
}
},
}, AST_LambdaExpression);
var AST_LambdaDefinition = DEFNODE("LambdaDefinition", "inlined name", {
$documentation: "Base class for function definitions",
$propdoc: {
inlined: "[boolean/S] whether this function has been inlined",
name: "[AST_SymbolDefun] the name of this function",
},
_validate: function() {
if (this.TYPE == "LambdaDefinition") throw new Error("should not instantiate AST_LambdaDefinition");
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
},
}, AST_Lambda); }, AST_Lambda);
function is_defun(node) { var AST_AsyncDefun = DEFNODE("AsyncDefun", null, {
return node instanceof AST_AsyncDefun || node instanceof AST_Defun;
}
var AST_AsyncDefun = DEFNODE("AsyncDefun", "inlined name", {
$documentation: "An asynchronous function definition", $documentation: "An asynchronous function definition",
$propdoc: { }, AST_LambdaDefinition);
name: "[AST_SymbolDefun] the name of this function",
},
_validate: function() {
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
},
}, AST_Lambda);
var AST_Defun = DEFNODE("Defun", "inlined name", { var AST_AsyncGeneratorDefun = DEFNODE("AsyncGeneratorDefun", null, {
$documentation: "An asynchronous generator function definition",
}, AST_LambdaDefinition);
var AST_Defun = DEFNODE("Defun", null, {
$documentation: "A function definition", $documentation: "A function definition",
$propdoc: { }, AST_LambdaDefinition);
name: "[AST_SymbolDefun] the name of this function",
}, var AST_GeneratorDefun = DEFNODE("GeneratorDefun", null, {
_validate: function() { $documentation: "A generator function definition",
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun"); }, AST_LambdaDefinition);
},
}, AST_Lambda);
/* -----[ JUMPS ]----- */ /* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, { var AST_Jump = DEFNODE("Jump", null, {
$documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)" $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)",
_validate: function() {
if (this.TYPE == "Jump") throw new Error("should not instantiate AST_Jump");
},
}, AST_Statement); }, AST_Statement);
var AST_Exit = DEFNODE("Exit", "value", { var AST_Exit = DEFNODE("Exit", "value", {
@@ -709,7 +787,10 @@ var AST_Exit = DEFNODE("Exit", "value", {
visitor.visit(node, function() { visitor.visit(node, function() {
if (node.value) node.value.walk(visitor); if (node.value) node.value.walk(visitor);
}); });
} },
_validate: function() {
if (this.TYPE == "Exit") throw new Error("should not instantiate AST_Exit");
},
}, AST_Jump); }, AST_Jump);
var AST_Return = DEFNODE("Return", null, { var AST_Return = DEFNODE("Return", null, {
@@ -738,6 +819,7 @@ var AST_LoopControl = DEFNODE("LoopControl", "label", {
}); });
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "LoopControl") throw new Error("should not instantiate AST_LoopControl");
if (this.label != null) { if (this.label != null) {
if (!(this.label instanceof AST_LabelRef)) throw new Error("label must be AST_LabelRef"); if (!(this.label instanceof AST_LabelRef)) throw new Error("label must be AST_LabelRef");
} }
@@ -772,7 +854,7 @@ var AST_If = DEFNODE("If", "condition alternative", {
must_be_expression(this, "condition"); must_be_expression(this, "condition");
if (this.alternative != null) { if (this.alternative != null) {
if (!(this.alternative instanceof AST_Statement)) throw new Error("alternative must be AST_Statement"); if (!(this.alternative instanceof AST_Statement)) throw new Error("alternative must be AST_Statement");
if (is_function(this.alternative)) throw new error("alternative cannot be AST_Function"); if (this.alternative instanceof AST_LambdaExpression) throw new error("alternative cannot be AST_LambdaExpression");
} }
}, },
}, AST_StatementWithBody); }, AST_StatementWithBody);
@@ -801,6 +883,9 @@ var AST_Switch = DEFNODE("Switch", "expression", {
var AST_SwitchBranch = DEFNODE("SwitchBranch", null, { var AST_SwitchBranch = DEFNODE("SwitchBranch", null, {
$documentation: "Base class for `switch` branches", $documentation: "Base class for `switch` branches",
_validate: function() {
if (this.TYPE == "SwitchBranch") throw new Error("should not instantiate AST_SwitchBranch");
},
}, AST_Block); }, AST_Block);
var AST_Default = DEFNODE("Default", null, { var AST_Default = DEFNODE("Default", null, {
@@ -889,6 +974,7 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
}); });
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "Definitions") throw new Error("should not instantiate AST_Definitions");
if (this.definitions.length < 1) throw new Error("must have at least one definition"); if (this.definitions.length < 1) throw new Error("must have at least one definition");
}, },
}, AST_Statement); }, AST_Statement);
@@ -1036,6 +1122,7 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
return p; return p;
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "PropAccess") throw new Error("should not instantiate AST_PropAccess");
must_be_expression(this, "expression"); must_be_expression(this, "expression");
}, },
}); });
@@ -1096,6 +1183,7 @@ var AST_Unary = DEFNODE("Unary", "operator expression", {
}); });
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "Unary") throw new Error("should not instantiate AST_Unary");
if (typeof this.operator != "string") throw new Error("operator must be string"); if (typeof this.operator != "string") throw new Error("operator must be string");
must_be_expression(this, "expression"); must_be_expression(this, "expression");
}, },
@@ -1183,6 +1271,27 @@ var AST_Await = DEFNODE("Await", "expression", {
}, },
}); });
var AST_Yield = DEFNODE("Yield", "expression nested", {
$documentation: "A yield expression",
$propdoc: {
expression: "[AST_Node?] return value for iterator, or null if undefined",
nested: "[boolean] whether to iterate over expression as generator",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.expression) node.expression.walk(visitor);
});
},
_validate: function() {
if (this.expression != null) {
must_be_expression(this, "expression");
} else if (this.nested) {
throw new Error("yield* must contain expression");
}
},
});
/* -----[ LITERALS ]----- */ /* -----[ LITERALS ]----- */
var AST_Array = DEFNODE("Array", "elements", { var AST_Array = DEFNODE("Array", "elements", {
@@ -1208,6 +1317,9 @@ var AST_Destructured = DEFNODE("Destructured", "rest", {
$propdoc: { $propdoc: {
rest: "[(AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)?] rest parameter, or null if absent", rest: "[(AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)?] rest parameter, or null if absent",
}, },
_validate: function() {
if (this.TYPE == "Destructured") throw new Error("should not instantiate AST_Destructured");
},
}); });
function validate_destructured(node, check, allow_default) { function validate_destructured(node, check, allow_default) {
@@ -1319,6 +1431,7 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
}); });
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "ObjectProperty") throw new Error("should not instantiate AST_ObjectProperty");
if (typeof this.key != "string") { if (typeof this.key != "string") {
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node"); 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, "key");
@@ -1356,6 +1469,7 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
thedef: "[SymbolDef/S] the definition of this symbol" thedef: "[SymbolDef/S] the definition of this symbol"
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "Symbol") throw new Error("should not instantiate AST_Symbol");
if (typeof this.name != "string") throw new Error("name must be string"); if (typeof this.name != "string") throw new Error("name must be string");
}, },
}); });
@@ -1418,8 +1532,39 @@ var AST_This = DEFNODE("This", null, {
}, },
}, AST_Symbol); }, AST_Symbol);
var AST_Template = DEFNODE("Template", "expressions strings tag", {
$documentation: "A template literal, i.e. tag`str1${expr1}...strN${exprN}strN+1`",
$propdoc: {
expressions: "[AST_Node*] the placeholder expressions",
strings: "[string*] the raw text segments",
tag: "[AST_Node] tag function, or null if absent",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
if (node.tag) node.tag.walk(visitor);
node.expressions.forEach(function(expr) {
expr.walk(visitor);
});
});
},
_validate: function() {
if (this.expressions.length + 1 != this.strings.length) {
throw new Error("malformed template with " + this.expressions.length + " placeholder(s) but " + this.strings.length + " text segment(s)");
}
must_be_expressions(this, "expressions");
this.strings.forEach(function(string) {
if (typeof string != "string") throw new Error("strings must contain string");
});
if (this.tag != null) must_be_expression(this, "tag");
},
});
var AST_Constant = DEFNODE("Constant", null, { var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants", $documentation: "Base class for all constants",
_validate: function() {
if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant");
},
}); });
var AST_String = DEFNODE("String", "value quote", { var AST_String = DEFNODE("String", "value quote", {
@@ -1440,6 +1585,19 @@ var AST_Number = DEFNODE("Number", "value", {
}, },
_validate: function() { _validate: function() {
if (typeof this.value != "number") throw new Error("value must be number"); if (typeof this.value != "number") throw new Error("value must be number");
if (!isFinite(this.value)) throw new Error("value must be finite");
if (this.value < 0) throw new Error("value cannot be negative");
},
}, AST_Constant);
var AST_BigInt = DEFNODE("BigInt", "value", {
$documentation: "A BigInt literal",
$propdoc: {
value: "[string] the numeric representation",
},
_validate: function() {
if (typeof this.value != "string") throw new Error("value must be string");
if (this.value[0] == "-") throw new Error("value cannot be negative");
}, },
}, AST_Constant); }, AST_Constant);
@@ -1455,6 +1613,9 @@ var AST_RegExp = DEFNODE("RegExp", "value", {
var AST_Atom = DEFNODE("Atom", null, { var AST_Atom = DEFNODE("Atom", null, {
$documentation: "Base class for atoms", $documentation: "Base class for atoms",
_validate: function() {
if (this.TYPE == "Atom") throw new Error("should not instantiate AST_Atom");
},
}, AST_Constant); }, AST_Constant);
var AST_Null = DEFNODE("Null", null, { var AST_Null = DEFNODE("Null", null, {
@@ -1484,6 +1645,9 @@ var AST_Infinity = DEFNODE("Infinity", null, {
var AST_Boolean = DEFNODE("Boolean", null, { var AST_Boolean = DEFNODE("Boolean", null, {
$documentation: "Base class for booleans", $documentation: "Base class for booleans",
_validate: function() {
if (this.TYPE == "Boolean") throw new Error("should not instantiate AST_Boolean");
},
}, AST_Atom); }, AST_Atom);
var AST_False = DEFNODE("False", null, { var AST_False = DEFNODE("False", null, {

File diff suppressed because it is too large Load Diff

View File

@@ -673,7 +673,9 @@ function OutputStream(options) {
} }
} }
PARENS(AST_AsyncFunction, needs_parens_function); PARENS(AST_AsyncFunction, needs_parens_function);
PARENS(AST_AsyncGeneratorFunction, needs_parens_function);
PARENS(AST_Function, needs_parens_function); PARENS(AST_Function, needs_parens_function);
PARENS(AST_GeneratorFunction, needs_parens_function);
// 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.
@@ -682,51 +684,69 @@ function OutputStream(options) {
} }
PARENS(AST_Object, needs_parens_obj); PARENS(AST_Object, needs_parens_obj);
PARENS(AST_Unary, function(output) { function needs_parens_unary(output) {
var p = output.parent(); var p = output.parent();
return (p instanceof AST_Call || p instanceof AST_PropAccess) && p.expression === this; // (-x) ** y
}); if (p instanceof AST_Binary) return p.operator == "**" && p.left === this;
// (await x)(y)
// new (await x)
if (p instanceof AST_Call) return p.expression === this;
// (x++)[y]
// (typeof x).y
if (p instanceof AST_PropAccess) return p.expression === this;
}
PARENS(AST_Await, needs_parens_unary);
PARENS(AST_Unary, needs_parens_unary);
PARENS(AST_Sequence, function(output) { PARENS(AST_Sequence, function(output) {
var p = output.parent(); var p = output.parent();
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] // [ 1, (2, 3), 4 ] ---> [ 1, 3, 4 ]
return p instanceof AST_Array return p instanceof AST_Array
// () => (foo, bar) // () ---> (foo, bar)
|| is_arrow(p) && p.value === this || is_arrow(p) && p.value === this
// await (foo, bar) // await (foo, bar)
|| p instanceof AST_Await || p instanceof AST_Await
// 1 + (2, 3) + 4 ==> 8 // 1 + (2, 3) + 4 ---> 8
|| p instanceof AST_Binary || p instanceof AST_Binary
// new (foo, bar) or foo(1, (2, 3), 4) // new (foo, bar) or foo(1, (2, 3), 4)
|| p instanceof AST_Call || p instanceof AST_Call
// (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
// [ a = (1, 2) ] = [] ==> a == 2 // [ a = (1, 2) ] = [] ---> a == 2
|| p instanceof AST_DefaultValue || p instanceof AST_DefaultValue
// { [(1, 2)]: 3 }[2] ==> 3 // { [(1, 2)]: foo } = bar
// { foo: (1, 2) }.foo ==> 2 // { 1: (2, foo) } = bar
|| p instanceof AST_DestructuredKeyVal || p instanceof AST_DestructuredKeyVal
// for (foo of (bar, baz));
|| p instanceof AST_ForOf
// { [(1, 2)]: 3 }[2] ---> 3
// { foo: (1, 2) }.foo ---> 2
|| 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
// ...(foo, bar, baz) // ...(foo, bar, baz)
|| p instanceof AST_Spread || p instanceof AST_Spread
// !(foo, bar, baz) // !(foo, bar, baz)
|| p instanceof AST_Unary || p instanceof AST_Unary
// var a = (1, 2), b = a + a; ==> b == 4 // var a = (1, 2), b = a + a; ---> b == 4
|| p instanceof AST_VarDef; || p instanceof AST_VarDef
// yield (foo, bar)
|| p instanceof AST_Yield;
}); });
PARENS(AST_Binary, function(output) { PARENS(AST_Binary, function(output) {
var p = output.parent(); var p = output.parent();
// await (foo && bar) // await (foo && bar)
if (p instanceof AST_Await) return true; if (p instanceof AST_Await) return true;
// this deals with precedence: 3 * (2 + 1) // this deals with precedence:
// 3 * (2 + 1)
// 3 - (2 - 1)
// (1 ** 2) ** 3
if (p instanceof AST_Binary) { if (p instanceof AST_Binary) {
var po = p.operator, pp = PRECEDENCE[po]; var po = p.operator, pp = PRECEDENCE[po];
var so = this.operator, sp = PRECEDENCE[so]; var so = this.operator, sp = PRECEDENCE[so];
return pp > sp || (pp == sp && this === p.right); return pp > sp || (pp == sp && this === p[po == "**" ? "left" : "right"]);
} }
// (foo && bar)() // (foo && bar)()
if (p instanceof AST_Call) return p.expression === this; if (p instanceof AST_Call) return p.expression === this;
@@ -773,18 +793,16 @@ function OutputStream(options) {
// (new foo)(bar) // (new foo)(bar)
if (p instanceof AST_Call) return p.expression === this; if (p instanceof AST_Call) return p.expression === this;
// (new Date).getTime(), (new Date)["getTime"]() // (new Date).getTime(), (new Date)["getTime"]()
return p instanceof AST_PropAccess; if (p instanceof AST_PropAccess) return true;
// (new foo)`bar`
if (p instanceof AST_Template) return p.tag === this;
}); });
PARENS(AST_Number, function(output) { PARENS(AST_Number, function(output) {
var p = output.parent(); if (!output.option("galio")) return false;
if (p instanceof AST_PropAccess && p.expression === this) {
var value = this.value;
// https://github.com/mishoo/UglifyJS/issues/115
return value < 0
// https://github.com/mishoo/UglifyJS/pull/1009 // https://github.com/mishoo/UglifyJS/pull/1009
|| output.option("galio") && /^0/.test(make_num(value)); var p = output.parent();
} return p instanceof AST_PropAccess && p.expression === this && /^0/.test(make_num(this.value));
}); });
function needs_parens_assign_cond(self, output) { function needs_parens_assign_cond(self, output) {
@@ -799,6 +817,8 @@ function OutputStream(options) {
if (p instanceof AST_Conditional) return p.condition === self; 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 === self; if (p instanceof AST_PropAccess) return p.expression === self;
// (a = foo)`bar`
if (p instanceof AST_Template) return p.tag === self;
// !(a = false) → true // !(a = false) → true
if (p instanceof AST_Unary) return true; if (p instanceof AST_Unary) return true;
} }
@@ -807,8 +827,8 @@ function OutputStream(options) {
}); });
PARENS(AST_Assign, function(output) { PARENS(AST_Assign, function(output) {
if (needs_parens_assign_cond(this, output)) return true; if (needs_parens_assign_cond(this, output)) return true;
// v8 parser bug => workaround // v8 parser bug ---> workaround
// f([1], [a] = []) => f([1], ([a] = [])) // f([1], [a] = []) ---> f([1], ([a] = []))
if (output.option("v8")) return this.left instanceof AST_Destructured; if (output.option("v8")) return this.left instanceof AST_Destructured;
// ({ p: a } = o); // ({ p: a } = o);
if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output); if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output);
@@ -819,15 +839,8 @@ function OutputStream(options) {
PARENS(AST_Conditional, function(output) { PARENS(AST_Conditional, function(output) {
return needs_parens_assign_cond(this, output); return needs_parens_assign_cond(this, output);
}); });
PARENS(AST_Yield, function(output) {
PARENS(AST_Await, function(output) { return needs_parens_assign_cond(this, output);
var p = output.parent();
// new (await foo)
// (await foo)(bar)
if (p instanceof AST_Call) return p.expression === this;
// (await foo).prop
// (await foo)["prop"]
if (p instanceof AST_PropAccess) return p.expression === this;
}); });
/* -----[ PRINTERS ]----- */ /* -----[ PRINTERS ]----- */
@@ -969,20 +982,25 @@ function OutputStream(options) {
output.space(); output.space();
force_statement(self.body, output); force_statement(self.body, output);
}); });
DEFPRINT(AST_ForIn, function(output) { function print_for_enum(prefix, infix) {
return function(output) {
var self = this; var self = this;
output.print("for"); output.print(prefix);
output.space(); output.space();
output.with_parens(function() { output.with_parens(function() {
self.init.print(output); self.init.print(output);
output.space(); output.space();
output.print("in"); output.print(infix);
output.space(); output.space();
self.object.print(output); self.object.print(output);
}); });
output.space(); output.space();
force_statement(self.body, output); force_statement(self.body, output);
}); };
}
DEFPRINT(AST_ForAwaitOf, print_for_enum("for await", "of"));
DEFPRINT(AST_ForIn, print_for_enum("for", "in"));
DEFPRINT(AST_ForOf, print_for_enum("for", "of"));
DEFPRINT(AST_With, function(output) { DEFPRINT(AST_With, function(output) {
var self = this; var self = this;
output.print("with"); output.print("with");
@@ -1052,6 +1070,20 @@ function OutputStream(options) {
} }
DEFPRINT(AST_AsyncDefun, print_async); DEFPRINT(AST_AsyncDefun, print_async);
DEFPRINT(AST_AsyncFunction, print_async); DEFPRINT(AST_AsyncFunction, print_async);
function print_async_generator(output) {
output.print("async");
output.space();
output.print("function*");
print_lambda(this, output);
}
DEFPRINT(AST_AsyncGeneratorDefun, print_async_generator);
DEFPRINT(AST_AsyncGeneratorFunction, print_async_generator);
function print_generator(output) {
output.print("function*");
print_lambda(this, output);
}
DEFPRINT(AST_GeneratorDefun, print_generator);
DEFPRINT(AST_GeneratorFunction, print_generator);
/* -----[ jumps ]----- */ /* -----[ jumps ]----- */
function print_jump(kind, prop) { function print_jump(kind, prop) {
@@ -1203,7 +1235,7 @@ function OutputStream(options) {
def.print(output); def.print(output);
}); });
var p = output.parent(); var p = output.parent();
if (p && p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon(); if (!(p instanceof AST_IterationStatement && p.init === self)) output.semicolon();
}; };
} }
DEFPRINT(AST_Const, print_definitinos("const")); DEFPRINT(AST_Const, print_definitinos("const"));
@@ -1230,7 +1262,7 @@ function OutputStream(options) {
output.print("="); output.print("=");
output.space(); output.space();
var p = output.parent(1); var p = output.parent(1);
var noin = p instanceof AST_For || p instanceof AST_ForIn; var noin = p instanceof AST_For || p instanceof AST_ForEnumeration;
parenthesize_for_noin(self.value, output, noin); parenthesize_for_noin(self.value, output, noin);
} }
}); });
@@ -1351,6 +1383,13 @@ function OutputStream(options) {
output.space(); output.space();
this.expression.print(output); this.expression.print(output);
}); });
DEFPRINT(AST_Yield, function(output) {
output.print(this.nested ? "yield*" : "yield");
if (this.expression) {
output.space();
this.expression.print(output);
}
});
/* -----[ literals ]----- */ /* -----[ literals ]----- */
DEFPRINT(AST_Array, function(output) { DEFPRINT(AST_Array, function(output) {
@@ -1481,6 +1520,19 @@ function OutputStream(options) {
DEFPRINT(AST_This, function(output) { DEFPRINT(AST_This, function(output) {
output.print("this"); output.print("this");
}); });
DEFPRINT(AST_Template, function(output) {
var self = this;
if (self.tag) self.tag.print(output);
output.print("`");
for (var i = 0; i < self.expressions.length; i++) {
output.print(self.strings[i]);
output.print("${");
self.expressions[i].print(output);
output.print("}");
}
output.print(self.strings[i]);
output.print("`");
});
DEFPRINT(AST_Constant, function(output) { DEFPRINT(AST_Constant, function(output) {
output.print(this.value); output.print(this.value);
}); });

View File

@@ -81,6 +81,7 @@ var OPERATORS = makePredicate([
"*", "*",
"/", "/",
"%", "%",
"**",
">>", ">>",
"<<", "<<",
">>>", ">>>",
@@ -111,13 +112,18 @@ var OPERATORS = makePredicate([
var NEWLINE_CHARS = "\n\r\u2028\u2029"; var NEWLINE_CHARS = "\n\r\u2028\u2029";
var OPERATOR_CHARS = "+-*&%=<>!?|~^"; var OPERATOR_CHARS = "+-*&%=<>!?|~^";
var PUNC_BEFORE_EXPRESSION = "[{(,;:"; var PUNC_OPENERS = "[{(";
var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + ")}]"; var PUNC_SEPARATORS = ",;:";
var PUNC_CLOSERS = ")}]";
var PUNC_AFTER_EXPRESSION = PUNC_SEPARATORS + PUNC_CLOSERS;
var PUNC_BEFORE_EXPRESSION = PUNC_OPENERS + PUNC_SEPARATORS;
var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + "`" + PUNC_CLOSERS;
var WHITESPACE_CHARS = NEWLINE_CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF"; var WHITESPACE_CHARS = NEWLINE_CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF";
var NON_IDENTIFIER_CHARS = makePredicate(characters("./'\"" + OPERATOR_CHARS + PUNC_CHARS + WHITESPACE_CHARS)); var NON_IDENTIFIER_CHARS = makePredicate(characters("./'\"" + OPERATOR_CHARS + PUNC_CHARS + WHITESPACE_CHARS));
NEWLINE_CHARS = makePredicate(characters(NEWLINE_CHARS)); NEWLINE_CHARS = makePredicate(characters(NEWLINE_CHARS));
OPERATOR_CHARS = makePredicate(characters(OPERATOR_CHARS)); OPERATOR_CHARS = makePredicate(characters(OPERATOR_CHARS));
PUNC_AFTER_EXPRESSION = makePredicate(characters(PUNC_AFTER_EXPRESSION));
PUNC_BEFORE_EXPRESSION = makePredicate(characters(PUNC_BEFORE_EXPRESSION)); PUNC_BEFORE_EXPRESSION = makePredicate(characters(PUNC_BEFORE_EXPRESSION));
PUNC_CHARS = makePredicate(characters(PUNC_CHARS)); PUNC_CHARS = makePredicate(characters(PUNC_CHARS));
WHITESPACE_CHARS = makePredicate(characters(WHITESPACE_CHARS)); WHITESPACE_CHARS = makePredicate(characters(WHITESPACE_CHARS));
@@ -144,6 +150,43 @@ function is_identifier_string(str) {
return /^[a-z_$][a-z0-9_$]*$/i.test(str); return /^[a-z_$][a-z0-9_$]*$/i.test(str);
} }
function decode_escape_sequence(seq) {
switch (seq[0]) {
case "b": return "\b";
case "f": return "\f";
case "n": return "\n";
case "r": return "\r";
case "t": return "\t";
case "u":
var code;
if (seq.length == 5) {
code = seq.slice(1);
} else if (seq[1] == "{" && seq.slice(-1) == "}") {
code = seq.slice(2, -1);
} else {
return;
}
var num = parseInt(code, 16);
if (num < 0 || isNaN(num)) return;
if (num < 0x10000) return String.fromCharCode(num);
if (num > 0x10ffff) return;
return String.fromCharCode((num >> 10) + 0xd7c0) + String.fromCharCode((num & 0x03ff) + 0xdc00);
case "v": return "\u000b";
case "x":
if (seq.length != 3) return;
var num = parseInt(seq.slice(1), 16);
if (num < 0 || isNaN(num)) return;
return String.fromCharCode(num);
case "\r":
case "\n":
return "";
default:
if (seq == "0") return "\0";
if (seq[0] >= "0" && seq[0] <= "9") return;
return seq;
}
}
function parse_js_number(num) { function parse_js_number(num) {
var match; var match;
if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2); if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2);
@@ -190,7 +233,29 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
regex_allowed : false, regex_allowed : false,
comments_before : [], comments_before : [],
directives : {}, directives : {},
directive_stack : [] directive_stack : [],
read_template : with_eof_error("Unterminated template literal", function(strings) {
var s = "";
for (;;) {
var ch = next(true, true);
switch (ch) {
case "\\":
ch += next(true, true);
break;
case "`":
strings.push(s);
return;
case "$":
if (peek() == "{") {
next();
strings.push(s);
S.regex_allowed = true;
return true;
}
}
s += ch;
}
}),
}; };
var prev_was_dot = false; var prev_was_dot = false;
@@ -280,9 +345,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
} }
function read_while(pred) { function read_while(pred) {
var ret = "", ch, i = 0; var ret = "", ch;
while ((ch = peek()) && pred(ch, i++)) while ((ch = peek()) && pred(ch)) ret += next();
ret += next();
return ret; return ret;
} }
@@ -292,16 +356,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function read_num(prefix) { function read_num(prefix) {
var has_e = false, after_e = false, has_x = false, has_dot = prefix == "."; var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
var num = read_while(function(ch, i) { var num = read_while(function(ch) {
var code = ch.charCodeAt(0); var code = ch.charCodeAt(0);
switch (code) { switch (code) {
case 120: case 88: // xX case 120: case 88: // xX
return has_x ? false : (has_x = true); return has_x ? false : (has_x = true);
case 101: case 69: // eE case 101: case 69: // eE
return has_x ? true : has_e ? false : (has_e = after_e = true); return has_x ? true : has_e ? false : (has_e = after_e = true);
case 45: // - case 43: case 45: // +-
return after_e || (i == 0 && !prefix);
case 43: // +
return after_e; return after_e;
case (after_e = false, 46): // . case (after_e = false, 46): // .
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
@@ -315,41 +377,29 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1"); num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
} }
var valid = parse_js_number(num); var valid = parse_js_number(num);
if (!isNaN(valid)) return token("num", valid); if (isNaN(valid)) parse_error("Invalid syntax: " + num);
parse_error("Invalid syntax: " + num); if (has_dot || has_e || peek() != "n") return token("num", valid);
return token("bigint", num.toLowerCase() + next());
} }
function read_escaped_char(in_string) { function read_escaped_char(in_string) {
var seq = next(true, in_string);
if (seq >= "0" && seq <= "7") return read_octal_escape_sequence(seq);
if (seq == "u") {
var ch = next(true, in_string); var ch = next(true, in_string);
switch (ch.charCodeAt(0)) { seq += ch;
case 110: return "\n"; if (ch != "{") {
case 114: return "\r"; seq += next(true, in_string) + next(true, in_string) + next(true, in_string);
case 116: return "\t"; } else do {
case 98: return "\b"; ch = next(true, in_string);
case 118: return "\u000b"; // \v seq += ch;
case 102: return "\f"; } while (ch != "}");
case 120: return String.fromCharCode(hex_bytes(2)); // \x } else if (seq == "x") {
case 117: // \u seq += next(true, in_string) + next(true, in_string);
if (peek() != "{") return String.fromCharCode(hex_bytes(4));
next();
var num = 0;
do {
var digit = parseInt(next(true), 16);
if (isNaN(digit)) parse_error("Invalid hex-character pattern in string");
num = num * 16 + digit;
} while (peek() != "}");
next();
if (num < 0x10000) return String.fromCharCode(num);
if (num > 0x10ffff) parse_error("Invalid character code: " + num);
return String.fromCharCode((num >> 10) + 0xd7c0) + String.fromCharCode((num & 0x03ff) + 0xdc00);
case 13: // \r
// DOS newline
if (peek() == "\n") next(true, in_string);
case 10: return ""; // \n
} }
if (ch >= "0" && ch <= "7") var str = decode_escape_sequence(seq);
return read_octal_escape_sequence(ch); if (typeof str != "string") parse_error("Invalid escape sequence: \\" + seq);
return ch; return str;
} }
function read_octal_escape_sequence(ch) { function read_octal_escape_sequence(ch) {
@@ -368,17 +418,6 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return String.fromCharCode(parseInt(ch, 8)); return String.fromCharCode(parseInt(ch, 8));
} }
function hex_bytes(n) {
var num = 0;
for (; n > 0; --n) {
var digit = parseInt(next(true), 16);
if (isNaN(digit))
parse_error("Invalid hex-character pattern in string");
num = (num << 4) | digit;
}
return num;
}
var read_string = with_eof_error("Unterminated string constant", function(quote_char) { var read_string = with_eof_error("Unterminated string constant", function(quote_char) {
var quote = next(), ret = ""; var quote = next(), ret = "";
for (;;) { for (;;) {
@@ -632,10 +671,11 @@ var PRECEDENCE = function(a, ret) {
["<", ">", "<=", ">=", "in", "instanceof"], ["<", ">", "<=", ">=", "in", "instanceof"],
[">>", "<<", ">>>"], [">>", "<<", ">>>"],
["+", "-"], ["+", "-"],
["*", "/", "%"] ["*", "/", "%"],
["**"],
], {}); ], {});
var ATOMIC_START_TOKEN = makePredicate("atom num regexp string"); var ATOMIC_START_TOKEN = makePredicate("atom bigint num regexp string");
/* -----[ Parser ]----- */ /* -----[ Parser ]----- */
@@ -658,6 +698,7 @@ function parse($TEXT, options) {
in_directives : true, in_directives : true,
in_funarg : -1, in_funarg : -1,
in_function : 0, in_function : 0,
in_generator : false,
in_loop : 0, in_loop : 0,
labels : [], labels : [],
peeked : null, peeked : null,
@@ -783,6 +824,7 @@ function parse($TEXT, options) {
semicolon(); semicolon();
return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body }); return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body });
case "num": case "num":
case "bigint":
case "regexp": case "regexp":
case "operator": case "operator":
case "atom": case "atom":
@@ -794,12 +836,17 @@ function parse($TEXT, options) {
if (is_token(peek(), "keyword", "function")) { if (is_token(peek(), "keyword", "function")) {
next(); next();
next(); next();
return function_(AST_AsyncDefun); if (!is("operator", "*")) return function_(AST_AsyncDefun);
next();
return function_(AST_AsyncGeneratorDefun);
} }
break; break;
case "await": case "await":
if (S.in_async) return simple_statement(); if (S.in_async) return simple_statement();
break; break;
case "yield":
if (S.in_generator) return simple_statement();
break;
} }
return is_token(peek(), "punc", ":") return is_token(peek(), "punc", ":")
? labeled_statement() ? labeled_statement()
@@ -815,6 +862,7 @@ function parse($TEXT, options) {
}); });
case "[": case "[":
case "(": case "(":
case "`":
return simple_statement(); return simple_statement();
case ";": case ";":
S.in_directives = false; S.in_directives = false;
@@ -869,7 +917,9 @@ function parse($TEXT, options) {
case "function": case "function":
next(); next();
return function_(AST_Defun); if (!is("operator", "*")) return function_(AST_Defun);
next();
return function_(AST_GeneratorDefun);
case "if": case "if":
next(); next();
@@ -990,6 +1040,7 @@ function parse($TEXT, options) {
} }
function for_() { function for_() {
var await = is("name", "await") && next();
expect("("); expect("(");
var init = null; var init = null;
if (!is("punc", ";")) { if (!is("punc", ";")) {
@@ -1000,16 +1051,29 @@ function parse($TEXT, options) {
: is("keyword", "var") : is("keyword", "var")
? (next(), var_(true)) ? (next(), var_(true))
: expression(true); : expression(true);
if (is("operator", "in")) { var ctor;
if (await) {
expect_token("name", "of");
ctor = AST_ForAwaitOf;
} else if (is("operator", "in")) {
next();
ctor = AST_ForIn;
} else if (is("name", "of")) {
next();
ctor = AST_ForOf;
}
if (ctor) {
if (init instanceof AST_Definitions) { if (init instanceof AST_Definitions) {
if (init.definitions.length > 1) { if (init.definitions.length > 1) {
token_error(init.start, "Only one variable declaration allowed in for..in loop"); token_error(init.start, "Only one variable declaration allowed in for..in/of loop");
}
if (ctor !== AST_ForIn && init.definitions[0].value) {
token_error(init.definitions[0].value.start, "No initializers allowed in for..of loop");
} }
} else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) { } else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) {
token_error(init.start, "Invalid left-hand side in for..in loop"); token_error(init.start, "Invalid left-hand side in for..in/of loop");
} }
next(); return for_enum(ctor, init);
return for_in(init);
} }
} }
return regular_for(init); return regular_for(init);
@@ -1029,10 +1093,10 @@ function parse($TEXT, options) {
}); });
} }
function for_in(init) { function for_enum(ctor, init) {
var obj = expression(); var obj = expression();
expect(")"); expect(")");
return new AST_ForIn({ return new ctor({
init : init, init : init,
object : obj, object : obj,
body : in_loop(statement) body : in_loop(statement)
@@ -1102,7 +1166,9 @@ function parse($TEXT, options) {
function arrow(exprs, start, async) { function arrow(exprs, start, async) {
var was_async = S.in_async; var was_async = S.in_async;
var was_gen = S.in_generator;
S.in_async = async; S.in_async = async;
S.in_generator = false;
var was_funarg = S.in_funarg; var was_funarg = S.in_funarg;
S.in_funarg = S.in_function; S.in_funarg = S.in_function;
var argnames = exprs.map(to_funarg); var argnames = exprs.map(to_funarg);
@@ -1132,6 +1198,7 @@ function parse($TEXT, options) {
--S.in_function; --S.in_function;
S.in_loop = loop; S.in_loop = loop;
S.labels = labels; S.labels = labels;
S.in_generator = was_gen;
S.in_async = was_async; S.in_async = was_async;
return new (async ? AST_AsyncArrow : AST_Arrow)({ return new (async ? AST_AsyncArrow : AST_Arrow)({
start: start, start: start,
@@ -1145,15 +1212,15 @@ function parse($TEXT, options) {
var function_ = function(ctor) { var function_ = function(ctor) {
var was_async = S.in_async; var was_async = S.in_async;
var was_gen = S.in_generator;
var name; var name;
if (ctor === AST_AsyncDefun) { if (/Defun$/.test(ctor.TYPE)) {
name = as_symbol(AST_SymbolDefun); name = as_symbol(AST_SymbolDefun);
S.in_async = true; S.in_async = /^Async/.test(ctor.TYPE);
} else if (ctor === AST_Defun) { S.in_generator = /Generator/.test(ctor.TYPE);
name = as_symbol(AST_SymbolDefun);
S.in_async = false;
} else { } else {
S.in_async = ctor === AST_AsyncFunction; S.in_async = /^Async/.test(ctor.TYPE);
S.in_generator = /Generator/.test(ctor.TYPE);
name = as_symbol(AST_SymbolLambda, true); name = as_symbol(AST_SymbolLambda, true);
} }
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration)) if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
@@ -1182,6 +1249,7 @@ function parse($TEXT, options) {
--S.in_function; --S.in_function;
S.in_loop = loop; S.in_loop = loop;
S.labels = labels; S.labels = labels;
S.in_generator = was_gen;
S.in_async = was_async; S.in_async = was_async;
return new ctor({ return new ctor({
name: name, name: name,
@@ -1361,6 +1429,9 @@ function parse($TEXT, options) {
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 });
break; break;
case "bigint":
ret = new AST_BigInt({ start: tok, end: tok, value: tok.value });
break;
case "string": case "string":
ret = new AST_String({ ret = new AST_String({
start : tok, start : tok,
@@ -1397,6 +1468,11 @@ function parse($TEXT, options) {
var start = S.token; var start = S.token;
if (is("punc")) { if (is("punc")) {
switch (start.value) { switch (start.value) {
case "`":
var tmpl = template(null);
tmpl.start = start;
tmpl.end = prev();
return subscripts(tmpl, allow_calls);
case "(": case "(":
next(); next();
if (is("punc", ")")) { if (is("punc", ")")) {
@@ -1437,7 +1513,13 @@ function parse($TEXT, options) {
} }
if (is("keyword", "function")) { if (is("keyword", "function")) {
next(); next();
var func = function_(AST_Function); var func;
if (is("operator", "*")) {
next();
func = function_(AST_GeneratorFunction);
} else {
func = function_(AST_Function);
}
func.start = start; func.start = start;
func.end = prev(); func.end = prev();
return subscripts(func, allow_calls); return subscripts(func, allow_calls);
@@ -1448,12 +1530,18 @@ function parse($TEXT, options) {
if (sym.name == "async") { if (sym.name == "async") {
if (is("keyword", "function")) { if (is("keyword", "function")) {
next(); next();
var func = function_(AST_AsyncFunction); var func;
if (is("operator", "*")) {
next();
func = function_(AST_AsyncGeneratorFunction);
} else {
func = function_(AST_AsyncFunction);
}
func.start = start; func.start = start;
func.end = prev(); func.end = prev();
return subscripts(func, allow_calls); return subscripts(func, allow_calls);
} }
if (is("name")) { if (is("name") && is_token(peek(), "punc", "=>")) {
start = S.token; start = S.token;
sym = _make_symbol(AST_SymbolRef, start); sym = _make_symbol(AST_SymbolRef, start);
next(); next();
@@ -1523,6 +1611,21 @@ function parse($TEXT, options) {
// allow trailing comma // allow trailing comma
if (!options.strict && is("punc", "}")) break; if (!options.strict && is("punc", "}")) break;
var start = S.token; var start = S.token;
if (is("operator", "*")) {
next();
var key = as_property_key();
var gen_start = S.token;
var gen = function_(AST_GeneratorFunction);
gen.start = gen_start;
gen.end = prev();
a.push(new AST_ObjectKeyVal({
start: start,
key: key,
value: gen,
end: prev(),
}));
continue;
}
if (is("operator", "...")) { if (is("operator", "...")) {
next(); next();
a.push(new AST_Spread({ a.push(new AST_Spread({
@@ -1584,9 +1687,10 @@ function parse($TEXT, options) {
} }
if (start.type == "name") switch (key) { if (start.type == "name") switch (key) {
case "async": case "async":
var is_gen = is("operator", "*") && next();
key = as_property_key(); key = as_property_key();
var func_start = S.token; var func_start = S.token;
var func = function_(AST_AsyncFunction); var func = function_(is_gen ? AST_AsyncGeneratorFunction : AST_AsyncFunction);
func.start = func_start; func.start = func_start;
func.end = prev(); func.end = prev();
a.push(new AST_ObjectKeyVal({ a.push(new AST_ObjectKeyVal({
@@ -1650,6 +1754,7 @@ function parse($TEXT, options) {
function _make_symbol(type, token) { function _make_symbol(type, token) {
var name = token.value; var name = token.value;
if (name === "await" && S.in_async) unexpected(token); if (name === "await" && S.in_async) unexpected(token);
if (name === "yield" && S.in_generator) unexpected(token);
return new (name === "this" ? AST_This : type)({ return new (name === "this" ? AST_This : type)({
name: "" + name, name: "" + name,
start: token, start: token,
@@ -1767,6 +1872,23 @@ function parse($TEXT, options) {
} }
} }
function template(tag) {
var read = S.input.context().read_template;
var strings = [];
var expressions = [];
while (read(strings)) {
next();
expressions.push(expression());
if (!is("punc", "}")) unexpected();
}
next();
return new AST_Template({
expressions: expressions,
strings: strings,
tag: tag,
});
}
var subscripts = function(expr, allow_calls) { var subscripts = function(expr, allow_calls) {
var start = expr.start; var start = expr.start;
if (is("punc", ".")) { if (is("punc", ".")) {
@@ -1800,15 +1922,51 @@ function parse($TEXT, options) {
mark_pure(call); mark_pure(call);
return subscripts(call, true); return subscripts(call, true);
} }
if (is("punc", "`")) {
var tmpl = template(expr);
tmpl.start = expr.start;
tmpl.end = prev();
return subscripts(tmpl, allow_calls);
}
return expr; return expr;
}; };
function maybe_unary() { function maybe_unary(no_in) {
var start = S.token; var start = S.token;
if (S.in_async && is("name", "await")) {
if (S.in_funarg === S.in_function) croak("Invalid use of await in function argument");
S.input.context().regex_allowed = true;
next();
return new AST_Await({
start: start,
expression: maybe_unary(no_in),
end: prev(),
});
}
if (S.in_generator && is("name", "yield")) {
if (S.in_funarg === S.in_function) croak("Invalid use of yield in function argument");
S.input.context().regex_allowed = true;
next();
var exp = null;
var nested = false;
if (is("operator", "*")) {
next();
exp = maybe_assign(no_in);
nested = true;
} else if (is("punc") ? !PUNC_AFTER_EXPRESSION[S.token.value] : !can_insert_semicolon()) {
exp = maybe_assign(no_in);
}
return new AST_Yield({
start: start,
expression: exp,
nested: nested,
end: prev(),
});
}
if (is("operator") && UNARY_PREFIX[start.value]) { if (is("operator") && UNARY_PREFIX[start.value]) {
next(); next();
handle_regexp(); handle_regexp();
var ex = make_unary(AST_UnaryPrefix, start, maybe_await()); var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(no_in));
ex.start = start; ex.start = start;
ex.end = prev(); ex.end = prev();
return ex; return ex;
@@ -1839,26 +1997,13 @@ function parse($TEXT, options) {
return new ctor({ operator: op, expression: expr }); return new ctor({ operator: op, expression: expr });
} }
function maybe_await() {
var start = S.token;
if (!(S.in_async && is("name", "await"))) return maybe_unary();
if (S.in_funarg === S.in_function) croak("Invalid use of await in function argument");
S.input.context().regex_allowed = true;
next();
return new AST_Await({
start: start,
expression: maybe_await(),
end: prev(),
});
}
var expr_op = function(left, min_prec, no_in) { var expr_op = function(left, min_prec, no_in) {
var op = is("operator") ? S.token.value : null; var op = is("operator") ? S.token.value : null;
if (op == "in" && no_in) op = null; if (op == "in" && no_in) op = null;
var prec = op != null ? PRECEDENCE[op] : null; var prec = op != null ? PRECEDENCE[op] : null;
if (prec != null && prec > min_prec) { if (prec != null && prec > min_prec) {
next(); next();
var right = expr_op(maybe_await(), prec, no_in); var right = expr_op(maybe_unary(no_in), op == "**" ? prec - 1 : prec, no_in);
return expr_op(new AST_Binary({ return expr_op(new AST_Binary({
start : left.start, start : left.start,
left : left, left : left,
@@ -1871,7 +2016,7 @@ function parse($TEXT, options) {
}; };
function expr_ops(no_in) { function expr_ops(no_in) {
return expr_op(maybe_await(), 0, no_in); return expr_op(maybe_unary(no_in), 0, no_in);
} }
var maybe_conditional = function(no_in) { var maybe_conditional = function(no_in) {

View File

@@ -228,7 +228,7 @@ function mangle_properties(ast, options) {
var mangled = cache.get(name); var mangled = cache.get(name);
if (!mangled) { if (!mangled) {
if (debug) { if (debug) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_. // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo ---> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_suffix + "_"; var debug_mangled = "_$" + name + "$" + debug_suffix + "_";
if (can_mangle(debug_mangled)) mangled = debug_mangled; if (can_mangle(debug_mangled)) mangled = debug_mangled;
} }

View File

@@ -101,6 +101,14 @@ SymbolDef.prototype = {
var unary_side_effects = makePredicate("delete ++ --"); var unary_side_effects = makePredicate("delete ++ --");
function is_lhs(node, parent) {
if (parent instanceof AST_Assign) return parent.left === node && node;
if (parent instanceof AST_DefaultValue) return parent.name === node && node;
if (parent instanceof AST_Destructured) return node;
if (parent instanceof AST_DestructuredKeyVal) return node;
if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression;
}
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
options = defaults(options, { options = defaults(options, {
cache: null, cache: null,
@@ -113,7 +121,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
var next_def_id = 0; var next_def_id = 0;
var scope = self.parent_scope = null; var scope = self.parent_scope = null;
var tw = new TreeWalker(function(node, descend) { var tw = new TreeWalker(function(node, descend) {
if (is_defun(node)) { if (node instanceof AST_LambdaDefinition) {
node.name.walk(tw); node.name.walk(tw);
walk_scope(function() { walk_scope(function() {
node.argnames.forEach(function(argname) { node.argnames.forEach(function(argname) {
@@ -269,8 +277,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
sym = self.def_global(node); sym = self.def_global(node);
} else if (name == "arguments" && is_arguments(sym)) { } else if (name == "arguments" && is_arguments(sym)) {
var parent = tw.parent(); var parent = tw.parent();
if (parent instanceof AST_Assign && parent.left === node if (is_lhs(node, parent)) {
|| parent instanceof AST_Unary && unary_side_effects[parent.operator]) {
sym.scope.uses_arguments = 3; sym.scope.uses_arguments = 3;
} else if (sym.scope.uses_arguments < 2 } else if (sym.scope.uses_arguments < 2
&& !(parent instanceof AST_PropAccess && parent.expression === node)) { && !(parent instanceof AST_PropAccess && parent.expression === node)) {
@@ -432,7 +439,7 @@ AST_BlockScope.DEFMETHOD("find_variable", function(name) {
AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) { AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) {
var def = this.def_variable(symbol, init); var def = this.def_variable(symbol, init);
if (!def.init || is_defun(def.init)) def.init = init; if (!def.init || def.init instanceof AST_LambdaDefinition) def.init = init;
this.functions.set(symbol.name, def); this.functions.set(symbol.name, def);
return def; return def;
}); });
@@ -441,7 +448,7 @@ AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) {
var def = this.variables.get(symbol.name); var def = this.variables.get(symbol.name);
if (def) { if (def) {
def.orig.push(symbol); def.orig.push(symbol);
if (is_function(def.init)) def.init = init; if (def.init instanceof AST_LambdaExpression) def.init = init;
} else { } else {
def = this.make_def(symbol, init); def = this.make_def(symbol, init);
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);

View File

@@ -82,7 +82,7 @@ TreeTransformer.prototype = new TreeWalker;
if (self.step) self.step = self.step.transform(tw); if (self.step) self.step = self.step.transform(tw);
self.body = self.body.transform(tw); self.body = self.body.transform(tw);
}); });
DEF(AST_ForIn, function(self, tw) { DEF(AST_ForEnumeration, function(self, tw) {
self.init = self.init.transform(tw); self.init = self.init.transform(tw);
self.object = self.object.transform(tw); self.object = self.object.transform(tw);
self.body = self.body.transform(tw); self.body = self.body.transform(tw);
@@ -157,6 +157,9 @@ TreeTransformer.prototype = new TreeWalker;
DEF(AST_Await, function(self, tw) { DEF(AST_Await, function(self, tw) {
self.expression = self.expression.transform(tw); self.expression = self.expression.transform(tw);
}); });
DEF(AST_Yield, function(self, tw) {
if (self.expression) self.expression = self.expression.transform(tw);
});
DEF(AST_Dot, function(self, tw) { DEF(AST_Dot, function(self, tw) {
self.expression = self.expression.transform(tw); self.expression = self.expression.transform(tw);
}); });
@@ -201,6 +204,10 @@ TreeTransformer.prototype = new TreeWalker;
if (self.key instanceof AST_Node) self.key = self.key.transform(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);
}); });
DEF(AST_Template, function(self, tw) {
if (self.tag) self.tag = self.tag.transform(tw);
self.expressions = do_list(self.expressions, tw);
});
})(function(node, descend) { })(function(node, descend) {
node.DEFMETHOD("transform", function(tw, in_list) { node.DEFMETHOD("transform", function(tw, in_list) {
var x, y; var x, y;

View File

@@ -255,6 +255,8 @@ function first_in_statement(stack, arrow) {
if (p.expressions[0] === node) continue; if (p.expressions[0] === node) continue;
} else if (p instanceof AST_Statement) { } else if (p instanceof AST_Statement) {
return p.body === node; return p.body === node;
} else if (p instanceof AST_Template) {
if (p.tag === node) continue;
} else if (p instanceof AST_UnaryPostfix) { } else if (p instanceof AST_UnaryPostfix) {
if (p.expression === node) continue; if (p.expression === node) continue;
} }

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

View File

@@ -209,7 +209,7 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
} else { } else {
var toplevel = sandbox.has_toplevel(options); var toplevel = sandbox.has_toplevel(options);
var expected = stdout[toplevel ? 1 : 0]; var expected = stdout[toplevel ? 1 : 0];
var actual = run_code(result.code, toplevel); var actual = sandbox.run_code(result.code, toplevel);
if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) { if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) {
actual = expected; actual = expected;
} }
@@ -244,11 +244,6 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
return true; return true;
} }
function run_code(code, toplevel) {
var result = sandbox.run_code(code, toplevel);
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
}
function test_case(test) { function test_case(test) {
log(" Running test [{name}]", { name: test.name }); log(" Running test [{name}]", { name: test.name });
U.AST_Node.enable_validation(); U.AST_Node.enable_validation();
@@ -380,7 +375,7 @@ function test_case(test) {
} }
} }
if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) { if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = [ run_code(input_code), run_code(input_code, true) ]; var stdout = [ sandbox.run_code(input_code), sandbox.run_code(input_code, true) ];
var toplevel = sandbox.has_toplevel({ var toplevel = sandbox.has_toplevel({
compress: test.options, compress: test.options,
mangle: test.mangle mangle: test.mangle
@@ -409,7 +404,7 @@ function test_case(test) {
}); });
return false; return false;
} }
actual = run_code(output_code, toplevel); actual = sandbox.run_code(output_code, toplevel);
if (!sandbox.same_stdout(test.expect_stdout, actual)) { if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([ log([
"!!! failed", "!!! failed",

View File

@@ -13,9 +13,10 @@ holes_and_undefined: {
} }
} }
constant_join: { constant_join_1: {
options = { options = {
evaluate: true, evaluate: true,
side_effects: true,
strings: true, strings: true,
unsafe: true, unsafe: true,
} }
@@ -57,7 +58,7 @@ constant_join: {
var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join(); var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join();
var c6 = [ "1,2,,,foo,bar", baz() ].join(); var c6 = [ "1,2,,,foo,bar", baz() ].join();
var d = "foo-3bar-baz"; var d = "foo-3bar-baz";
var e = [].join(foo + bar); var e = (foo, bar, "");
var f = ""; var f = "";
var g = ""; var g = "";
} }

View File

@@ -434,6 +434,62 @@ collapse_value: {
node_version: ">=4" node_version: ">=4"
} }
collapse_property_lambda: {
options = {
collapse_vars: true,
pure_getters: "strict",
}
input: {
console.log(function f() {
f.g = () => 42;
return f.g();
}());
}
expect: {
console.log(function f() {
return (f.g = () => 42)();
}());
}
expect_stdout: "42"
node_version: ">=4"
}
drop_return: {
options = {
arrows: true,
side_effects: true,
}
input: {
(a => {
while (!console);
return console.log(a);
})(42);
}
expect: {
(a => {
while (!console);
console.log(a);
})(42);
}
expect_stdout: "42"
node_version: ">=4"
}
drop_value: {
options = {
arrows: true,
side_effects: true,
}
input: {
((a, b) => a + b)(console.log(42));
}
expect: {
((a, b) => {})(console.log(42));
}
expect_stdout: "42"
node_version: ">=4"
}
reduce_iife_1: { reduce_iife_1: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -519,6 +519,160 @@ collapse_vars_3: {
node_version: ">=8" node_version: ">=8"
} }
collapse_property_lambda: {
options = {
collapse_vars: true,
pure_getters: "strict",
}
input: {
(async function f() {
f.g = () => 42;
return f.g();
})().then(console.log);
}
expect: {
(async function f() {
return (f.g = () => 42)();
})().then(console.log);
}
expect_stdout: "42"
node_version: ">=8"
}
drop_return: {
options = {
side_effects: true,
}
input: {
(async function(a) {
while (!console);
return console.log(a);
})(42);
}
expect: {
(async function(a) {
while (!console);
console.log(a);
})(42);
}
expect_stdout: "42"
node_version: ">=8"
}
functions: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
!async function() {
var a = async function a() {
return a && "a";
};
var b = async function x() {
return !!x;
};
var c = async function(c) {
return c;
};
if (await c(await b(await a()))) {
var d = async function() {};
var e = async function y() {
return typeof y;
};
var f = async function(f) {
return f;
};
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
}
}();
}
expect: {
!async function() {
async function a() {
return a && "a";
}
async function b() {
return !!b;
}
var c = async function(c) {
return c;
};
if (await c(await b(await a()))) {
async function d() {}
async function e() {
return typeof e;
}
var f = async function(f) {
return f;
};
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
}
}();
}
expect_stdout: "a true 42 function function function"
node_version: ">=8"
}
functions_use_strict: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
"use strict";
!async function() {
var a = async function a() {
return a && "a";
};
var b = async function x() {
return !!x;
};
var c = async function(c) {
return c;
};
if (await c(await b(await a()))) {
var d = async function() {};
var e = async function y() {
return typeof y;
};
var f = async function(f) {
return f;
};
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
}
}();
}
expect: {
"use strict";
!async function() {
async function a() {
return a && "a";
}
async function b() {
return !!b;
}
var c = async function(c) {
return c;
};
if (await c(await b(await a()))) {
var d = async function() {};
var e = async function y() {
return typeof y;
};
var f = async function(f) {
return f;
};
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
}
}();
}
expect_stdout: "a true 42 function function function"
node_version: ">=8"
}
issue_4335_1: { issue_4335_1: {
options = { options = {
inline: true, inline: true,
@@ -612,22 +766,32 @@ issue_4340: {
call_expression: { call_expression: {
input: { input: {
console.log(typeof async function(log) { console.log(typeof async function(log) {
(await log)("FAIL"); (await log)("foo");
}(console.log).then); }(console.log).then);
console.log("bar");
} }
expect_exact: 'console.log(typeof async function(log){(await log)("FAIL")}(console.log).then);' expect_exact: 'console.log(typeof async function(log){(await log)("foo")}(console.log).then);console.log("bar");'
expect_stdout: "function" expect_stdout: [
"function",
"bar",
"foo",
]
node_version: ">=8" node_version: ">=8"
} }
property_access_expression: { property_access_expression: {
input: { input: {
console.log(typeof async function(con) { console.log(typeof async function(con) {
(await con).log("FAIL"); (await con).log("foo");
}(console).then); }(console).then);
console.log("bar");
} }
expect_exact: 'console.log(typeof async function(con){(await con).log("FAIL")}(console).then);' expect_exact: 'console.log(typeof async function(con){(await con).log("foo")}(console).then);console.log("bar");'
expect_stdout: "function" expect_stdout: [
"function",
"bar",
"foo",
]
node_version: ">=8" node_version: ">=8"
} }
@@ -685,20 +849,18 @@ reduce_iife_3: {
input: { input: {
var a = "foo"; var a = "foo";
(async function() { (async function() {
console.log(a); console.log(a, await a, a, await a);
console.log(await a);
})(); })();
a = "bar"; a = "bar";
} }
expect: { expect: {
var a = "foo"; var a = "foo";
(async function() { (async function() {
console.log(a); console.log(a, await a, a, await a);
console.log(await a);
})(); })();
a = "bar"; a = "bar";
} }
expect_stdout: "foo" expect_stdout: "foo foo bar bar"
node_version: ">=8" node_version: ">=8"
} }
@@ -995,3 +1157,92 @@ issue_4534: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=8" node_version: ">=8"
} }
issue_4581: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
(async () => (A, a = "FAIL"))();
console.log(a);
}
expect: {
var a = "PASS";
(async () => (A, a = "FAIL"))();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4595: {
options = {
awaits: true,
inline: true,
}
input: {
(async function() {
await async function f() {
console.log(f.length);
}();
})();
}
expect: {
(async function() {
await async function f() {
console.log(f.length);
}();
})();
}
expect_stdout: "0"
node_version: ">=8"
}
issue_4598: {
options = {
conditionals: true,
}
input: {
if (console.log("PASS")) {
async function f() {}
}
}
expect: {
async function f() {}
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_4618: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
var await = async function f() {
console || f();
};
console.log;
return await;
}());
}
expect: {
console.log(typeof function() {
var await = async function f() {
console || f();
};
console.log;
return await;
}());
}
expect_stdout: "function"
node_version: ">=8"
}

62
test/compress/bigint.js Normal file
View File

@@ -0,0 +1,62 @@
arithmetic: {
input: {
console.log(((1n + 0x2n) * (0o3n - -4n)) >> (5n - 6n));
}
expect_exact: "console.log((1n+0x2n)*(0o3n- -4n)>>5n-6n);"
expect_stdout: "42n"
node_version: ">=10"
}
minus_dot: {
input: {
console.log(typeof -42n.toString(), typeof (-42n).toString());
}
expect_exact: "console.log(typeof-42n.toString(),typeof(-42n).toString());"
expect_stdout: "number string"
node_version: ">=10"
}
evaluate: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log((0xDEAD_BEEFn).toString(16));
}
expect: {
console.log(0xdeadbeefn.toString(16));
}
expect_stdout: "deadbeef"
node_version: ">=10"
}
Number: {
options = {
unsafe: true,
}
input: {
console.log(Number(-0xfeed_dead_beef_badn));
}
expect: {
console.log(+("" + -0xfeed_dead_beef_badn));
}
expect_stdout: "-1148098955808013200"
node_version: ">=10"
}
issue_4590: {
options = {
collapse_vars: true,
}
input: {
A = 1;
0n || console.log("PASS");
}
expect: {
A = 1;
0n || console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=10"
}

View File

@@ -5608,6 +5608,7 @@ collapse_rhs_array: {
collapse_rhs_boolean_1: { collapse_rhs_boolean_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
} }
input: { input: {
var a, b; var a, b;
@@ -5633,6 +5634,7 @@ collapse_rhs_boolean_1: {
collapse_rhs_boolean_2: { collapse_rhs_boolean_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
} }
input: { input: {
var a; var a;
@@ -5667,6 +5669,7 @@ collapse_rhs_boolean_3: {
booleans: true, booleans: true,
collapse_vars: true, collapse_vars: true,
conditionals: true, conditionals: true,
evaluate: true,
} }
input: { input: {
var a, f, g, h, i, n, s, t, x, y; var a, f, g, h, i, n, s, t, x, y;
@@ -5720,6 +5723,7 @@ collapse_rhs_function: {
collapse_rhs_number: { collapse_rhs_number: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
} }
input: { input: {
var a, b; var a, b;
@@ -5799,6 +5803,7 @@ collapse_rhs_regexp: {
collapse_rhs_string: { collapse_rhs_string: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
} }
input: { input: {
var a, b; var a, b;
@@ -5874,6 +5879,7 @@ collapse_rhs_this: {
collapse_rhs_undefined: { collapse_rhs_undefined: {
options = { options = {
collapse_vars: true, collapse_vars: true,
side_effects: true,
} }
input: { input: {
var a, b; var a, b;
@@ -8705,3 +8711,48 @@ collapse_or_assign: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_4586_1: {
options = {
collapse_vars: true,
}
input: {
var a = 42;
(function f(b) {
var b = a;
if (b === arguments[0])
console.log("PASS");
})(console);
}
expect: {
var a = 42;
(function f(b) {
var b = a;
if (b === arguments[0])
console.log("PASS");
})(console);
}
expect_stdout: "PASS"
}
issue_4586_2: {
options = {
collapse_vars: true,
}
input: {
var a = 42;
(function f(b) {
b = a;
if (b === arguments[0])
console.log("PASS");
})(console);
}
expect: {
var a = 42;
(function f(b) {
if ((b = a) === arguments[0])
console.log("PASS");
})(console);
}
expect_stdout: "PASS"
}

View File

@@ -161,6 +161,7 @@ process_boolean_returns: {
collapse_value_1: { collapse_value_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
keep_fargs: false,
unused: true, unused: true,
} }
input: { input: {
@@ -169,7 +170,7 @@ collapse_value_1: {
}()); }());
} }
expect: { expect: {
console.log(function(a) { console.log(function() {
return "PASS"; return "PASS";
}()); }());
} }
@@ -180,6 +181,7 @@ collapse_value_1: {
collapse_value_2: { collapse_value_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
keep_fargs: false,
unused: true, unused: true,
} }
input: { input: {
@@ -188,7 +190,7 @@ collapse_value_2: {
})().log("PASS"); })().log("PASS");
} }
expect: { expect: {
(function(a) { (function() {
return console; return console;
})().log("PASS"); })().log("PASS");
} }
@@ -554,8 +556,9 @@ drop_fargs: {
"bar", "bar",
] ]
expect_warnings: [ expect_warnings: [
"WARN: Dropping unused function argument c [test/compress/default-values.js:1,61]", "WARN: Dropping unused default argument c [test/compress/default-values.js:1,61]",
"WARN: Side effects in default value of unused variable b [test/compress/default-values.js:1,37]", "WARN: Side effects in default value of unused variable b [test/compress/default-values.js:1,37]",
"WARN: Dropping unused default argument assignment a [test/compress/default-values.js:1,29]",
] ]
node_version: ">=6" node_version: ">=6"
} }
@@ -1596,3 +1599,65 @@ issue_4548: {
] ]
node_version: ">=6" node_version: ">=6"
} }
issue_4588_1_unused: {
options = {
unused: true,
}
input: {
console.log(function(a = 42) {}.length);
}
expect: {
console.log(function(a = 0) {}.length);
}
expect_stdout: "0"
node_version: ">=6"
}
issue_4588_2_unused: {
options = {
unused: true,
}
input: {
console.log(function(a, b = void 0, c, d = "foo") {}.length);
}
expect: {
console.log(function(a, b = 0, c, d) {}.length);
}
expect_stdout: "1"
expect_warnings: [
"WARN: Dropping unused default argument assignment d [test/compress/default-values.js:1,47]",
"WARN: Dropping unused default argument value b [test/compress/default-values.js:1,32]",
]
node_version: ">=6"
}
issue_4588_1_evaluate: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(function(a = 42) {}.length);
}
expect: {
console.log(0);
}
expect_stdout: "0"
node_version: ">=6"
}
issue_4588_2_evaluate: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(function(a, b = void 0, c, d = "foo") {}.length);
}
expect: {
console.log(1);
}
expect_stdout: "1"
node_version: ">=6"
}

View File

@@ -2487,3 +2487,72 @@ issue_4554: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=6" node_version: ">=6"
} }
issue_4584: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
try {
(function f({
[console.log(a = "FAIL")]: a,
}) {})(0);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
(function f({
[console.log(a = "FAIL")]: a,
}) {})(0);
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_4608_1: {
options = {
arguments: true,
keep_fargs: false,
}
input: {
(function() {
[ arguments ] = [ "foo" ];
console.log(arguments[0]);
})();
}
expect: {
(function() {
[ arguments ] = [ "foo" ];
console.log(arguments[0]);
})();
}
expect_stdout: "f"
node_version: ">=6"
}
issue_4608_2: {
options = {
arguments: true,
reduce_vars: true,
}
input: {
(function(a) {
[ arguments ] = [ "foo" ];
console.log(arguments[0]);
})();
}
expect: {
(function(a) {
[ arguments ] = [ "foo" ];
console.log(arguments[0]);
})();
}
expect_stdout: "f"
node_version: ">=6"
}

View File

@@ -2138,6 +2138,7 @@ issue_3497: {
issue_3515_1: { issue_3515_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }
@@ -2189,6 +2190,7 @@ issue_3515_2: {
issue_3515_3: { issue_3515_3: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
unused: true, unused: true,
} }
input: { input: {
@@ -2256,6 +2258,7 @@ function_assign: {
issue_3598: { issue_3598: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
reduce_vars: true, reduce_vars: true,
unused: true, unused: true,
} }

View File

@@ -703,6 +703,7 @@ prototype_function: {
var g = 0(); var g = 0();
var h = 0(); var h = 0();
} }
expect_stdout: true
} }
call_args: { call_args: {
@@ -2800,7 +2801,7 @@ operator_in: {
console.log("PASS" in { }); console.log("PASS" in { });
console.log("FAIL" in { }); console.log("FAIL" in { });
console.log("toString" in { }); console.log("toString" in { });
console.log(true); console.log("toString" in { toString: 3 });
} }
expect_stdout: [ expect_stdout: [
"true", "true",

View File

@@ -0,0 +1,58 @@
precedence_1: {
input: {
console.log(-4 ** 3 ** 2);
}
expect_exact: "console.log((-4)**3**2);"
expect_stdout: "-262144"
node_version: ">=8"
}
precedence_2: {
input: {
console.log(-4 ** (3 ** 2));
}
expect_exact: "console.log((-4)**3**2);"
expect_stdout: "-262144"
node_version: ">=8"
}
precedence_3: {
input: {
console.log(-(4 ** 3) ** 2);
}
expect_exact: "console.log((-(4**3))**2);"
expect_stdout: "4096"
node_version: ">=8"
}
precedence_4: {
input: {
console.log((-4 ** 3) ** 2);
}
expect_exact: "console.log(((-4)**3)**2);"
expect_stdout: "4096"
node_version: ">=8"
}
await: {
input: {
(async a => a * await a ** ++a % a)(2).then(console.log);
}
expect_exact: "(async a=>a*(await a)**++a%a)(2).then(console.log);"
expect_stdout: "1"
node_version: ">=8"
}
evaluate: {
options = {
evaluate: true,
}
input: {
console.log(1 + 2 ** 3 - 4);
}
expect: {
console.log(5);
}
expect_stdout: "5"
node_version: ">=8"
}

View File

@@ -5283,3 +5283,127 @@ issue_4471: {
"PASS", "PASS",
] ]
} }
issue_4612_1: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
console.log(function() {
function f() {
return g();
}
function g(a) {
return a || f();
}
return g("PASS");
}());
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4612_2: {
options = {
evaluate: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
console.log(function() {
function fn() {
return h();
}
function g() {
return fn();
}
function h(a) {
return a || fn();
}
return h("PASS");
}());
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4612_3: {
options = {
inline: true,
reduce_vars: true,
}
input: {
console.log(typeof function() {
return g();
function f() {
return g;
}
function g() {
{
return f;
}
}
}());
}
expect: {
console.log(typeof function() {
return g();
function f() {
return g;
}
function g() {
return f;
}
}());
}
expect_stdout: "function"
}
issue_4612_4: {
options = {
booleans: true,
evaluate: true,
reduce_vars: true,
}
input: {
console.log(function() {
function f() {
return h();
}
function g() {
{
return h();
}
}
function h() {
{
return g();
}
}
}());
}
expect: {
console.log(function() {
function f() {
return h();
}
function g() {
return h();
}
function h() {
return g();
}
}());
}
expect_stdout: "undefined"
}

View File

@@ -17,7 +17,7 @@ issue_269_1: {
expect: { expect: {
var x = {}; var x = {};
console.log( console.log(
x + "", +x, !!x, "" + x, +("" + x), !!x,
"", 0, false "", 0, false
); );
} }

View File

@@ -1055,3 +1055,75 @@ issue_3916: {
} }
expect_stdout: "object PASS true PASS" expect_stdout: "object PASS true PASS"
} }
assign_var: {
options = {
join_vars: true,
}
input: {
b = "foo";
var a = [ , "bar" ];
console.log(b);
for (var b in a)
console.log(b, a[b]);
}
expect: {
var b = "foo", a = [ , "bar" ], b;
console.log(b);
for (b in a)
console.log(b, a[b]);
}
expect_stdout: [
"foo",
"1 bar",
]
}
assign_for_var: {
options = {
join_vars: true,
}
input: {
i = "foo",
a = new Array(i, "bar");
for (var i = 2; --i >= 0;) {
console.log(a[i]);
for (var a in i);
}
}
expect: {
for (var i = "foo", a = new Array(i, "bar"), i = 2; --i >= 0;) {
console.log(a[i]);
for (var a in i);
}
}
expect_stdout: [
"bar",
"foo",
]
}
assign_sequence_var: {
options = {
join_vars: true,
}
input: {
var a = 0, b = 1;
console.log(a),
a++,
b = 2;
var c = 3;
console.log(a, b, c);
}
expect: {
var a = 0, b = 1;
console.log(a),
a++;
var b = 2, c = 3;
console.log(a, b, c);
}
expect_stdout: [
"0",
"1 2 3",
]
}

View File

@@ -828,6 +828,21 @@ empty_for_in_prop_init: {
] ]
} }
for_of: {
input: {
var async = [ "PASS", 42 ];
async.p = "FAIL";
for (async of (null, async))
console.log(async);
}
expect_exact: 'var async=["PASS",42];async.p="FAIL";for(async of(null,async))console.log(async);'
expect_stdout: [
"PASS",
"42",
]
node_version: ">=0.12"
}
issue_3631_1: { issue_3631_1: {
options = { options = {
dead_code: true, dead_code: true,

View File

@@ -3183,3 +3183,32 @@ issue_4257: {
"1", "1",
] ]
} }
issue_4628: {
options = {
merge_vars: true,
}
input: {
(function() {
try {
console;
} finally {
var b = a;
}
for (var a in "foo");
console.log(b);
})();
}
expect: {
(function() {
try {
console;
} finally {
var b = a;
}
for (var a in "foo");
console.log(b);
})();
}
expect_stdout: "undefined"
}

View File

@@ -338,7 +338,7 @@ evaluate_3: {
console.log(1 + Number(x) + 2); console.log(1 + Number(x) + 2);
} }
expect: { expect: {
console.log(+x + 3); console.log(+("" + x) + 3);
} }
} }

View File

@@ -248,6 +248,35 @@ issue_2110_2: {
expect_stdout: "function" expect_stdout: "function"
} }
issue_2110_3: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
function g() {
return this;
}
console.log(typeof function() {
function f() {}
f.g = g;
return f.g();
}());
}
expect: {
function g() {
return this;
}
console.log(typeof function() {
function f() {}
f.g = g;
return f.g();
}());
}
expect_stdout: "function"
}
set_immutable_1: { set_immutable_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -979,6 +1008,7 @@ collapse_vars_2_strict: {
collapse_rhs_true: { collapse_rhs_true: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
pure_getters: true, pure_getters: true,
} }
input: { input: {
@@ -1015,6 +1045,7 @@ collapse_rhs_true: {
collapse_rhs_false: { collapse_rhs_false: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
pure_getters: false, pure_getters: false,
} }
input: { input: {
@@ -1051,6 +1082,7 @@ collapse_rhs_false: {
collapse_rhs_strict: { collapse_rhs_strict: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
pure_getters: "strict", pure_getters: "strict",
} }
input: { input: {
@@ -1087,6 +1119,7 @@ collapse_rhs_strict: {
collapse_rhs_setter: { collapse_rhs_setter: {
options = { options = {
collapse_vars: true, collapse_vars: true,
evaluate: true,
pure_getters: "strict", pure_getters: "strict",
} }
input: { input: {

View File

@@ -2460,6 +2460,7 @@ delay_def: {
evaluate: true, evaluate: true,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
side_effects: true,
unused: true, unused: true,
} }
input: { input: {

View File

@@ -663,3 +663,103 @@ issue_4562: {
expect_stdout: "f" expect_stdout: "f"
node_version: ">=6" node_version: ">=6"
} }
issue_4575: {
options = {
collapse_vars: true,
ie8: true,
reduce_vars: true,
rests: true,
unused: true,
}
input: {
A = "PASS";
(function() {
var a = 0, b = a;
var c = function a(...b) {
A;
var d = A;
console.log(d, b.length);
}();
})();
}
expect: {
A = "PASS";
(function() {
(function(b) {
A;
var d = A;
console.log(d, b.length);
})([]);
})();
}
expect_stdout: "PASS 0"
node_version: ">=6"
}
issue_4621: {
options = {
side_effects: true,
}
input: {
(function f(a, ...{
[console.log(a)]: b,
}) {})("PASS");
}
expect: {
(function f(a, ...{
[console.log(a)]: b,
}) {})("PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_4644_1: {
options = {
evaluate: true,
}
input: {
var a = "FAIL";
(function f(b, ...{
[a = "PASS"]: c,
}) {
return b;
})();
console.log(a);
}
expect: {
var a = "FAIL";
(function f(b, ...{
[a = "PASS"]: c,
}) {
return b;
})();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_4644_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(function(...a) {
return a[1];
}("FAIL", "PASS"), function(...b) {
return b.length;
}(), function(c, ...d) {
return d[0];
}("FAIL"));
}
expect: {
console.log("PASS", 0, function(c, ...d) {
return d[0];
}("FAIL"));
}
expect_stdout: "PASS 0 undefined"
node_version: ">=6"
}

View File

@@ -13,6 +13,23 @@ console_log: {
] ]
} }
console_log_console: {
input: {
var log = console.log;
log(console);
log(typeof console.log);
}
expect: {
var log = console.log;
log(console);
log(typeof console.log);
}
expect_stdout: [
"{ log: 'function(){}' }",
"function",
]
}
typeof_arguments: { typeof_arguments: {
options = { options = {
evaluate: true, evaluate: true,
@@ -81,6 +98,63 @@ log_global: {
expect_stdout: "[object global]" expect_stdout: "[object global]"
} }
log_nested: {
options = {
unused: true,
}
input: {
var o = { p: 42 };
for (var i = 0; i < 10; i++)
o = {
p: o,
q: function foo() {},
};
console.log(o);
}
expect: {
var o = { p: 42 };
for (var i = 0; i < 10; i++)
o = {
p: o,
q: function() {},
};
console.log(o);
}
expect_stdout: true
}
timers: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var count = 0, interval = 1000, duration = 3210;
var timer = setInterval(function() {
console.log(++count);
}, interval);
setTimeout(function() {
clearInterval(timer);
}, duration);
}
expect: {
var count = 0;
var timer = setInterval(function() {
console.log(++count);
}, 1000);
setTimeout(function() {
clearInterval(timer);
}, 3210);
}
expect_stdout: [
"1",
"2",
"3",
]
node_version: ">=0.12"
}
issue_4054: { issue_4054: {
input: { input: {
console.log({ console.log({

View File

@@ -918,3 +918,32 @@ issue_4560_3: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=6" node_version: ">=6"
} }
issue_4614: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
try {
(function(...[]) {
var arguments;
arguments[0];
})();
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
(function(...[]) {
var arguments;
arguments[0];
})();
} catch (e) {
console.log("PASS");
}
}
expect_stdout: true
node_version: ">=6"
}

300
test/compress/templates.js Normal file
View File

@@ -0,0 +1,300 @@
simple: {
input: {
console.log(`foo
bar\nbaz`);
}
expect_exact: "console.log(`foo\n bar\\nbaz`);"
expect_stdout: [
"foo",
" bar",
"baz",
]
node_version: ">=4"
}
placeholder: {
input: {
console.log(`foo ${ function(a, b) {
return a * b;
}(6, 7) }`);
}
expect_exact: "console.log(`foo ${function(a,b){return a*b}(6,7)}`);"
expect_stdout: "foo 42"
node_version: ">=4"
}
nested: {
input: {
console.log(`P${`A${"S"}`}S`);
}
expect_exact: 'console.log(`P${`A${"S"}`}S`);'
expect_stdout: "PASS"
node_version: ">=4"
}
tagged: {
input: {
console.log(String.raw`foo\nbar`);
}
expect_exact: "console.log(String.raw`foo\\nbar`);"
expect_stdout: "foo\\nbar"
node_version: ">=4"
}
tagged_chain: {
input: {
function f(strings) {
return strings.join("") || f;
}
console.log(f```${42}``pass`.toUpperCase());
}
expect_exact: 'function f(strings){return strings.join("")||f}console.log(f```${42}``pass`.toUpperCase());'
expect_stdout: "PASS"
node_version: ">=4"
}
tag_parenthesis_arrow: {
input: {
console.log((s => s.raw[0])`\tPASS`.slice(2));
}
expect_exact: "console.log((s=>s.raw[0])`\\tPASS`.slice(2));"
expect_stdout: "PASS"
node_version: ">=4"
}
tag_parenthesis_new: {
input: {
(new function() {
return console.log;
})`foo`;
}
expect_exact: "(new function(){return console.log})`foo`;"
expect_stdout: true
node_version: ">=4"
}
malformed_escape: {
input: {
(function(s) {
s.forEach((c, i) => console.log(i, c, s.raw[i]));
return () => console.log(arguments);
})`\uFo${42}`();
}
expect_exact: "(function(s){s.forEach((c,i)=>console.log(i,c,s.raw[i]));return()=>console.log(arguments)})`\\uFo${42}`();"
expect_stdout: true
node_version: ">=4"
}
evaluate: {
options = {
evaluate: true,
templates: false,
}
input: {
console.log(`foo ${ function(a, b) {
return a * b;
}(6, 7) }`);
}
expect: {
console.log(`foo ${42}`);
}
expect_stdout: "foo 42"
node_version: ">=4"
}
evaluate_templates: {
options = {
evaluate: true,
templates: true,
}
input: {
console.log(`foo ${ function(a, b) {
return a * b;
}(6, 7) }`);
}
expect: {
console.log("foo 42");
}
expect_stdout: "foo 42"
node_version: ">=4"
}
partial_evaluate: {
options = {
evaluate: true,
templates: true,
}
input: {
console.log(`${6 * 7} foo ${console ? `PA` + "SS" : `FA` + `IL`}`);
}
expect: {
console.log(`42 foo ${console ? "PASS" : "FAIL"}`);
}
expect_stdout: "42 foo PASS"
node_version: ">=4"
}
malformed_evaluate_1: {
options = {
evaluate: true,
templates: true,
}
input: {
console.log(`\67 ${6 * 7}`);
}
expect: {
console.log(`\67 42`);
}
expect_stdout: true
node_version: ">=4"
}
malformed_evaluate_2: {
options = {
evaluate: true,
templates: true,
}
input: {
console.log(`\u0${0}b${5}`);
}
expect: {
console.log(`\u0${0}b5`);
}
expect_stdout: true
node_version: ">=4"
}
malformed_evaluate_3: {
options = {
evaluate: true,
templates: true,
}
input: {
console.log(`\u${0}b${5}`);
}
expect: {
console.log(`\u0b5`);
}
expect_stdout: true
node_version: ">=4"
}
malformed_evaluate_4: {
options = {
evaluate: true,
templates: true,
unsafe: true,
}
input: {
console.log(String.raw`\u0${0}b${5}`);
}
expect: {
console.log("\\u00b5");
}
expect_stdout: "\\u00b5"
node_version: ">=8"
}
unsafe_evaluate: {
options = {
evaluate: true,
templates: true,
unsafe: true,
}
input: {
console.log(String.raw`\uFo`);
}
expect: {
console.log("\\uFo");
}
expect_stdout: "\\uFo"
node_version: ">=8"
}
side_effects: {
options = {
side_effects: true,
}
input: {
`42`;
`${console.log("foo")}`;
console.log`\nbar`;
}
expect: {
console.log("foo");
console.log`\nbar`;
}
expect_stdout: true
node_version: ">=4"
}
unsafe_side_effects: {
options = {
side_effects: true,
unsafe: true,
}
input: {
`42`;
`${console.log("foo")}`;
String.raw`\nbar`;
}
expect: {
console.log("foo");
}
expect_stdout: "foo"
node_version: ">=4"
}
issue_4604: {
options = {
collapse_vars: true,
}
input: {
var a = 0, log = console.log;
a = "FAIL";
(function() {
a = "PASS";
})``;
log(a);
}
expect: {
var a = 0, log = console.log;
a = "FAIL";
(function() {
a = "PASS";
})``;
log(a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4606: {
options = {
evaluate: true,
templates: true,
}
input: {
console.log(`${typeof A} ${"\r"} ${"\\"} ${"`"}`);
}
expect: {
console.log(`${typeof A} \r \\ \``);
}
expect_stdout: "undefined \r \\ `"
node_version: ">=4"
}
issue_4630: {
options = {
evaluate: true,
templates: true,
}
input: {
console.log(`${/PASS/}`);
}
expect: {
console.log("/PASS/");
}
expect_stdout: "/PASS/"
node_version: ">=4"
}

952
test/compress/yields.js Normal file
View File

@@ -0,0 +1,952 @@
binary: {
input: {
var a = function*() {
console.log(6 * (yield "PA" + "SS"));
}();
console.log(a.next("FAIL").value);
console.log(a.next(7).done);
}
expect_exact: 'var a=function*(){console.log(6*(yield"PA"+"SS"))}();console.log(a.next("FAIL").value);console.log(a.next(7).done);'
expect_stdout: [
"PASS",
"42",
"true",
]
node_version: ">=4"
}
empty_yield: {
input: {
var a = function*() {
yield;
console.log(yield);
yield
"FAIL 1";
}();
console.log(a.next("FAIL 2").value);
console.log(a.next("FAIL 3").value);
console.log(a.next("PASS").value);
console.log(a.next("FAIL 4").done);
}
expect_exact: 'var a=function*(){yield;console.log(yield);yield;"FAIL 1"}();console.log(a.next("FAIL 2").value);console.log(a.next("FAIL 3").value);console.log(a.next("PASS").value);console.log(a.next("FAIL 4").done);'
expect_stdout: [
"undefined",
"undefined",
"PASS",
"undefined",
"true",
]
node_version: ">=4"
}
empty_yield_conditional: {
input: {
var a = function*() {
console.log((yield) ? yield : yield);
}();
console.log(a.next("FAIL 1").value);
console.log(a.next("FAIL 2").value);
console.log(a.next("PASS").value);
console.log(a.next("FAIL 3").done);
}
expect_exact: 'var a=function*(){console.log((yield)?yield:yield)}();console.log(a.next("FAIL 1").value);console.log(a.next("FAIL 2").value);console.log(a.next("PASS").value);console.log(a.next("FAIL 3").done);'
expect_stdout: [
"undefined",
"undefined",
"PASS",
"undefined",
"true",
]
node_version: ">=4"
}
nested_yield: {
input: {
console.log(function*() {
(yield*
f())
function* f() {
return "FAIL";
}
yield*
f();
yield *f();
}().next().value || "PASS");
}
expect_exact: 'console.log(function*(){yield*f();function*f(){return"FAIL"}yield*f();yield*f()}().next().value||"PASS");'
expect_stdout: "PASS"
node_version: ">=4"
}
pause_resume: {
input: {
function* f() {
console.log(yield "PASS");
}
var a = f();
console.log(a.next("FAIL").value);
console.log(a.next(42).done);
}
expect_exact: 'function*f(){console.log(yield"PASS")}var a=f();console.log(a.next("FAIL").value);console.log(a.next(42).done);'
expect_stdout: [
"PASS",
"42",
"true",
]
node_version: ">=4"
}
arrow_yield: {
input: {
yield = "PASS";
console.log(function*() {
return () => yield || "FAIL";
}().next().value());
}
expect_exact: 'yield="PASS";console.log(function*(){return()=>yield||"FAIL"}().next().value());'
expect_stdout: "PASS"
node_version: ">=4"
}
for_of: {
input: {
function* f() {
if (yield "PASS") yield "FAIL 1";
yield 42;
return "FAIL 2";
}
for (var a of f())
console.log(a);
}
expect_exact: 'function*f(){if(yield"PASS")yield"FAIL 1";yield 42;return"FAIL 2"}for(var a of f())console.log(a);'
expect_stdout: [
"PASS",
"42",
]
node_version: ">=4"
}
for_await_of: {
input: {
async function* f() {
if (yield "PASS") yield "FAIL 1";
yield {
then: function(r) {
r(42);
},
};
return "FAIL 2";
}
(async function(a) {
for await (a of f())
console.log(a);
})();
}
expect_exact: 'async function*f(){if(yield"PASS")yield"FAIL 1";yield{then:function(r){r(42)}};return"FAIL 2"}(async function(a){for await(a of f())console.log(a)})();'
expect_stdout: [
"PASS",
"42",
]
node_version: ">=10"
}
collapse_vars_1: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
(function*() {
a = "PASS";
yield 42;
return "PASS";
})().next();
console.log(a);
}
expect: {
var a = "FAIL";
(function*() {
a = "PASS";
yield 42;
return "PASS";
})().next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
collapse_vars_2: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
(function*() {
yield (a = "PASS");
return "PASS";
})().next();
console.log(a);
}
expect: {
var a = "FAIL";
(function*() {
yield (a = "PASS");
return "PASS";
})().next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
collapse_vars_3: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
(function*() {
yield (a = "PASS", 42);
return "PASS";
})().next();
console.log(a);
}
expect: {
var a = "FAIL";
(function*() {
yield (a = "PASS", 42);
return "PASS";
})().next();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
collapse_vars_4: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = "FAIL";
var b = function*(c) {
return c;
}(a = "PASS");
console.log(a, b.next().done);
}
expect: {
var a = "FAIL";
var b = function*(c) {
return c;
}(a = "PASS");
console.log(a, b.next().done);
}
expect_stdout: "PASS true"
node_version: ">=4"
}
collapse_property_lambda: {
options = {
collapse_vars: true,
pure_getters: "strict",
}
input: {
console.log(function* f() {
f.g = () => 42;
return f.g();
}().next().value);
}
expect: {
console.log(function* f() {
return (f.g = () => 42)();
}().next().value);
}
expect_stdout: "42"
node_version: ">=4"
}
defun_name: {
input: {
function* yield() {
console.log("PASS");
}
yield().next();
}
expect: {
function* yield() {
console.log("PASS");
}
yield().next();
}
expect_stdout: "PASS"
node_version: ">=4"
}
drop_fname: {
rename = true
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {
toplevel: true,
}
input: {
function* yield() {
console.log("PASS");
}
yield().next();
}
expect: {
(function*() {
console.log("PASS");
})().next();
}
expect_stdout: "PASS"
node_version: ">=4"
}
keep_fname: {
options = {
keep_fnames: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function* yield() {
console.log("PASS");
}
yield().next();
}
expect: {
function* yield() {
console.log("PASS");
}
yield().next();
}
expect_stdout: "PASS"
node_version: ">=4"
}
evaluate: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function*() {}();
console.log(typeof a);
}
expect: {
var a = function*() {}();
console.log(typeof a);
}
expect_stdout: "object"
node_version: ">=4"
}
functions: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
!function*() {
var a = function* a() {
return a && "a";
};
var b = function* x() {
return !!x;
};
var c = function*(c) {
return c;
};
if (yield* c(yield* b(yield* a()))) {
var d = function*() {};
var e = function* y() {
return typeof y;
};
var f = function*(f) {
return f;
};
console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f);
}
}().next();
}
expect: {
!function*() {
function* a() {
return a && "a";
}
function* b() {
return !!b;
}
var c = function*(c) {
return c;
};
if (yield* c(yield* b(yield* a()))) {
function* d() {}
function* e() {
return typeof e;
}
var f = function*(f) {
return f;
};
console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f);
}
}().next();
}
expect_stdout: "a true 42 function function function"
node_version: ">=4"
}
functions_use_strict: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
"use strict";
!function*() {
var a = function* a() {
return a && "a";
};
var b = function* x() {
return !!x;
};
var c = function*(c) {
return c;
};
if (yield* c(yield* b(yield* a()))) {
var d = function*() {};
var e = function* y() {
return typeof y;
};
var f = function*(f) {
return f;
};
console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f);
}
}().next();
}
expect: {
"use strict";
!function*() {
function* a() {
return a && "a";
}
function* b() {
return !!b;
}
var c = function*(c) {
return c;
};
if (yield* c(yield* b(yield* a()))) {
var d = function*() {};
var e = function* y() {
return typeof y;
};
var f = function*(f) {
return f;
};
console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f);
}
}().next();
}
expect_stdout: "a true 42 function function function"
node_version: ">=4"
}
negate_iife: {
options = {
negate_iife: true,
side_effects: true,
}
input: {
(function*(a) {
console.log(a);
})("PASS").next();
}
expect: {
!function*(a) {
console.log(a);
}("PASS").next();
}
expect_stdout: "PASS"
node_version: ">=4"
}
reduce_iife_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
console.log(function*(a) {
yield a;
}(42).next().value);
}
expect: {
console.log(function*(a) {
yield 42;
}().next().value);
}
expect_stdout: "42"
node_version: ">=4"
}
reduce_iife_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
(function*() {
a = "FAIL";
})();
console.log(a);
}
expect: {
var a = "PASS";
(function*() {
a = "FAIL";
})();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
reduce_single_use_defun: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function* f(a) {
console.log(a);
}
f("PASS").next();
}
expect: {
(function*(a) {
console.log(a);
})("PASS").next();
}
expect_stdout: "PASS"
node_version: ">=4"
}
reduce_tagged: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
function* f() {
function g() {
h`foo`;
}
g();
function h(s) {
console.log(s[0]);
}
h([ "bar" ]);
}
f().next();
}
expect: {
function* f() {
(function() {
h`foo`;
})();
function h(s) {
console.log(s[0]);
}
h([ "bar" ]);
}
f().next();
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=4"
}
reduce_tagged_async: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
async function* f() {
function g() {
h`foo`;
}
g();
function h(s) {
console.log(s[0]);
}
h([ "bar" ]);
}
f().next();
}
expect: {
async function* f() {
(function() {
h`foo`;
})();
function h(s) {
console.log(s[0]);
}
h([ "bar" ]);
}
f().next();
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=10"
}
lift_sequence: {
options = {
sequences: true,
yields: true,
}
input: {
console.log(function*() {
yield (console, "PASS");
}().next().value);
}
expect: {
console.log(function*() {
console, yield "PASS";
}().next().value);
}
expect_stdout: "PASS"
node_version: ">=4"
}
inline_nested_yield: {
options = {
inline: true,
sequences: true,
yields: true,
}
input: {
var a = function*() {
yield* function*() {
yield "foo";
return "FAIL";
}();
}(), b;
do {
b = a.next();
console.log(b.value);
} while (!b.done);
}
expect: {
var a = function*() {
yield "foo",
"FAIL";
}(), b;
do {
b = a.next(),
console.log(b.value);
} while (!b.done);
}
expect_stdout: [
"foo",
"undefined",
]
node_version: ">=4"
}
drop_body: {
options = {
side_effects: true,
yields: true,
}
input: {
(function*([ , a = console.log("foo") ]) {
console.log("bar");
})([ console.log("baz") ]);
}
expect: {
[ [ , 0[0] = console.log("foo") ] ] = [ [ console.log("baz") ] ];
}
expect_stdout: [
"baz",
"foo",
]
node_version: ">=6"
}
drop_unused_call: {
options = {
inline: true,
side_effects: true,
toplevel: true,
unused: true,
yields: true,
}
input: {
var a = function*(){}(console.log("PASS"));
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4454_1: {
rename = false
options = {
merge_vars: true,
}
input: {
function f(a) {
(function*(b = console.log(a)) {})();
var yield = 42..toString();
console.log(yield);
}
f("PASS");
}
expect: {
function f(a) {
(function*(b = console.log(a)) {})();
var yield = 42..toString();
console.log(yield);
}
f("PASS");
}
expect_stdout: [
"PASS",
"42",
]
node_version: ">=6"
}
issue_4454_2: {
rename = true
options = {
merge_vars: true,
}
input: {
function f(a) {
(function*(b = console.log(a)) {})();
var yield = 42..toString();
console.log(yield);
}
f("PASS");
}
expect: {
function f(b) {
(function*(c = console.log(b)) {})();
var b = 42..toString();
console.log(b);
}
f("PASS");
}
expect_stdout: [
"PASS",
"42",
]
node_version: ">=6"
}
issue_4618: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
var yield = function* f() {
console || f();
};
console.log;
return yield;
}());
}
expect: {
console.log(typeof function() {
var yield = function* f() {
console || f();
};
console.log;
return yield;
}());
}
expect_stdout: "function"
node_version: ">=4"
}
issue_4623: {
options = {
conditionals: true,
}
input: {
if (console ? function*() {} : 0)
console.log("PASS");
}
expect: {
(console ? function*() {} : 0) && console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4633: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = function*() {
(function(log) {
log(typeof this);
})(yield "PASS");
}();
console.log(a.next().value);
a.next(console.log);
}
expect: {
var a = function*() {
(function(log) {
log(typeof this);
})(yield "PASS");
}();
console.log(a.next().value);
a.next(console.log);
}
expect_stdout: [
"PASS",
"object",
]
node_version: ">=4"
}
issue_4639_1: {
options = {
inline: true,
}
input: {
console.log(function*() {
return function() {
return yield => "PASS";
}();
}().next().value());
}
expect: {
console.log(function*() {
return function() {
return yield => "PASS";
}();
}().next().value());
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4639_2: {
options = {
inline: true,
}
input: {
(function*() {
console.log(function() {
return typeof yield;
}());
})().next();
}
expect: {
(function*() {
console.log(function() {
return typeof yield;
}());
})().next();
}
expect_stdout: "undefined"
node_version: ">=4"
}
issue_4641_1: {
options = {
sequences: true,
}
input: {
console.log(typeof async function*() {
try {
console.log("foo");
return;
} finally {
console.log("bar");
}
}().next().then);
}
expect: {
console.log(typeof async function*() {
try {
console.log("foo");
return;
} finally {
console.log("bar");
}
}().next().then);
}
expect_stdout: [
"foo",
"bar",
"function",
]
node_version: ">=10"
}
issue_4641_2: {
options = {
side_effects: true,
}
input: {
console.log(typeof async function*() {
try {
return void "FAIL";
} finally {
console.log("PASS");
}
}().next().then);
}
expect: {
console.log(typeof async function*() {
try {
return void 0;
} finally {
console.log("PASS");
}
}().next().then);
}
expect_stdout: [
"function",
"PASS",
]
node_version: ">=10"
}

View File

@@ -0,0 +1,3 @@
var a = [ 1 ], b;
for (b = 2 of a)
console.log(b);

View File

@@ -0,0 +1,3 @@
var a = [ 1 ];
for (var b = 2 of a)
console.log(b);

View File

@@ -56,7 +56,7 @@ describe("bin/uglifyjs", function() {
"--source-map", [ "--source-map", [
"names=true", "names=true",
"url=inline", "url=inline",
].join(","), ].join(),
].join(" "), function(err, stdout) { ].join(" "), function(err, stdout) {
if (err) throw err; if (err) throw err;
var expected = [ var expected = [
@@ -84,7 +84,7 @@ describe("bin/uglifyjs", function() {
"--source-map", [ "--source-map", [
"names=false", "names=false",
"url=inline", "url=inline",
].join(","), ].join(),
].join(" "), function(err, stdout) { ].join(" "), function(err, stdout) {
if (err) throw err; if (err) throw err;
var expected = [ var expected = [
@@ -171,7 +171,7 @@ describe("bin/uglifyjs", function() {
"content=" + mapFile, "content=" + mapFile,
"includeSources", "includeSources",
"url=inline", "url=inline",
].join(","), ].join(),
].join(" "); ].join(" ");
var child = exec(command, function(err, stdout) { var child = exec(command, function(err, stdout) {
@@ -333,11 +333,13 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/invalid/simple.js"; var command = uglifyjscmd + " test/input/invalid/simple.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); assert.strictEqual(stdout, "");
assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12"); assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
assert.strictEqual(lines[1], "function f(a{}"); "Parse error at test/input/invalid/simple.js:1,12",
assert.strictEqual(lines[2], " ^"); "function f(a{}",
assert.strictEqual(lines[3], "ERROR: Unexpected token: punc «{», expected: punc «,»"); " ^",
"ERROR: Unexpected token: punc «{», expected: punc «,»",
].join("\n"));
done(); done();
}); });
}); });
@@ -345,11 +347,13 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/invalid/tab.js"; var command = uglifyjscmd + " test/input/invalid/tab.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); assert.strictEqual(stdout, "");
assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12"); assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);"); "Parse error at test/input/invalid/tab.js:1,12",
assert.strictEqual(lines[2], "\t\t \t ^"); "\t\tfoo(\txyz, 0abc);",
assert.strictEqual(lines[3], "ERROR: Invalid syntax: 0abc"); "\t\t \t ^",
"ERROR: Invalid syntax: 0abc",
].join("\n"));
done(); done();
}); });
}); });
@@ -357,11 +361,13 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/invalid/eof.js"; var command = uglifyjscmd + " test/input/invalid/eof.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); assert.strictEqual(stdout, "");
assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0"); assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
assert.strictEqual(lines[1], "foo, bar("); "Parse error at test/input/invalid/eof.js:2,0",
assert.strictEqual(lines[2], " ^"); "foo, bar(",
assert.strictEqual(lines[3], "ERROR: Unexpected token: eof"); " ^",
"ERROR: Unexpected token: eof",
].join("\n"));
done(); done();
}); });
}); });
@@ -369,11 +375,13 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/invalid/loop-no-body.js"; var command = uglifyjscmd + " test/input/invalid/loop-no-body.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); assert.strictEqual(stdout, "");
assert.strictEqual(lines[0], "Parse error at test/input/invalid/loop-no-body.js:2,0"); assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
assert.strictEqual(lines[1], "for (var i = 0; i < 1; i++) "); "Parse error at test/input/invalid/loop-no-body.js:2,0",
assert.strictEqual(lines[2], " ^"); "for (var i = 0; i < 1; i++) ",
assert.strictEqual(lines[3], "ERROR: Unexpected token: eof"); " ^",
"ERROR: Unexpected token: eof",
].join("\n"));
done(); done();
}); });
}); });
@@ -386,7 +394,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/assign_1.js:1,18", "Parse error at test/input/invalid/assign_1.js:1,18",
"console.log(1 || 5--);", "console.log(1 || 5--);",
" ^", " ^",
"ERROR: Invalid use of -- operator" "ERROR: Invalid use of -- operator",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -400,7 +408,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/assign_2.js:1,32", "Parse error at test/input/invalid/assign_2.js:1,32",
"console.log(2 || (Math.random() /= 2));", "console.log(2 || (Math.random() /= 2));",
" ^", " ^",
"ERROR: Invalid assignment" "ERROR: Invalid assignment",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -414,7 +422,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/assign_3.js:1,17", "Parse error at test/input/invalid/assign_3.js:1,17",
"console.log(3 || ++this);", "console.log(3 || ++this);",
" ^", " ^",
"ERROR: Invalid use of ++ operator" "ERROR: Invalid use of ++ operator",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -428,7 +436,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/assign_4.js:1,0", "Parse error at test/input/invalid/assign_4.js:1,0",
"++null", "++null",
"^", "^",
"ERROR: Invalid use of ++ operator" "ERROR: Invalid use of ++ operator",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -442,7 +450,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/dot_1.js:1,2", "Parse error at test/input/invalid/dot_1.js:1,2",
"a.=", "a.=",
" ^", " ^",
"ERROR: Unexpected token: operator «=», expected: name" "ERROR: Unexpected token: operator «=», expected: name",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -456,7 +464,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/dot_2.js:1,0", "Parse error at test/input/invalid/dot_2.js:1,0",
"%.a;", "%.a;",
"^", "^",
"ERROR: Unexpected token: operator «%»" "ERROR: Unexpected token: operator «%»",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -470,7 +478,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/dot_3.js:1,2", "Parse error at test/input/invalid/dot_3.js:1,2",
"a./();", "a./();",
" ^", " ^",
"ERROR: Unexpected token: operator «/», expected: name" "ERROR: Unexpected token: operator «/», expected: name",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -484,7 +492,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/object.js:1,13", "Parse error at test/input/invalid/object.js:1,13",
"console.log({%: 1});", "console.log({%: 1});",
" ^", " ^",
"ERROR: Unexpected token: operator «%»" "ERROR: Unexpected token: operator «%»",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -498,7 +506,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/delete.js:13,11", "Parse error at test/input/invalid/delete.js:13,11",
" delete x;", " delete x;",
" ^", " ^",
"ERROR: Calling delete on expression not allowed in strict mode" "ERROR: Calling delete on expression not allowed in strict mode",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -512,7 +520,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/function_1.js:4,11", "Parse error at test/input/invalid/function_1.js:4,11",
"function g(arguments) {", "function g(arguments) {",
" ^", " ^",
"ERROR: Unexpected arguments in strict mode" "ERROR: Unexpected arguments in strict mode",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -526,7 +534,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/function_2.js:4,9", "Parse error at test/input/invalid/function_2.js:4,9",
"function eval() {", "function eval() {",
" ^", " ^",
"ERROR: Unexpected eval in strict mode" "ERROR: Unexpected eval in strict mode",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -540,7 +548,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/function_3.js:4,10", "Parse error at test/input/invalid/function_3.js:4,10",
"!function arguments() {", "!function arguments() {",
" ^", " ^",
"ERROR: Unexpected arguments in strict mode" "ERROR: Unexpected arguments in strict mode",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -554,7 +562,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/try.js:7,18", "Parse error at test/input/invalid/try.js:7,18",
" try {} catch (eval) {}", " try {} catch (eval) {}",
" ^", " ^",
"ERROR: Unexpected eval in strict mode" "ERROR: Unexpected eval in strict mode",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -568,7 +576,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/var.js:7,8", "Parse error at test/input/invalid/var.js:7,8",
" var eval;", " var eval;",
" ^", " ^",
"ERROR: Unexpected eval in strict mode" "ERROR: Unexpected eval in strict mode",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -582,7 +590,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/destructured_var.js:7,10", "Parse error at test/input/invalid/destructured_var.js:7,10",
" var { eval } = 42;", " var { eval } = 42;",
" ^", " ^",
"ERROR: Unexpected eval in strict mode" "ERROR: Unexpected eval in strict mode",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -596,7 +604,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/else.js:1,7", "Parse error at test/input/invalid/else.js:1,7",
"if (0) else 1;", "if (0) else 1;",
" ^", " ^",
"ERROR: Unexpected token: keyword «else»" "ERROR: Unexpected token: keyword «else»",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -610,7 +618,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/return.js:1,0", "Parse error at test/input/invalid/return.js:1,0",
"return 42;", "return 42;",
"^", "^",
"ERROR: 'return' outside of function" "ERROR: 'return' outside of function",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -624,7 +632,7 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/for-in_1.js:2,5", "Parse error at test/input/invalid/for-in_1.js:2,5",
"for (1, 2, a in b) {", "for (1, 2, a in b) {",
" ^", " ^",
"ERROR: Invalid left-hand side in for..in loop" "ERROR: Invalid left-hand side in for..in/of loop",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -638,7 +646,35 @@ describe("bin/uglifyjs", function() {
"Parse error at test/input/invalid/for-in_2.js:2,5", "Parse error at test/input/invalid/for-in_2.js:2,5",
"for (var a, b in c) {", "for (var a, b in c) {",
" ^", " ^",
"ERROR: Only one variable declaration allowed in for..in loop" "ERROR: Only one variable declaration allowed in for..in/of loop",
].join("\n"));
done();
});
});
it("Should throw syntax error (for-of init)", function(done) {
var command = uglifyjscmd + " test/input/invalid/for-of_1.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-of_1.js:2,5",
"for (b = 2 of a)",
" ^",
"ERROR: Invalid left-hand side in for..in/of loop",
].join("\n"));
done();
});
});
it("Should throw syntax error (for-of var)", function(done) {
var command = uglifyjscmd + " test/input/invalid/for-of_2.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-of_2.js:2,13",
"for (var b = 2 of a)",
" ^",
"ERROR: No initializers allowed in for..of loop",
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -647,11 +683,13 @@ describe("bin/uglifyjs", function() {
var command = uglifyjscmd + " test/input/invalid/switch.js"; var command = uglifyjscmd + " test/input/invalid/switch.js";
exec(command, function(err, stdout, stderr) { exec(command, function(err, stdout, stderr) {
assert.ok(err); assert.ok(err);
var lines = stderr.split(/\n/); assert.strictEqual(stdout, "");
assert.strictEqual(lines[0], "Parse error at test/input/invalid/switch.js:3,2"); assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
assert.strictEqual(lines[1], " default:"); "Parse error at test/input/invalid/switch.js:3,2",
assert.strictEqual(lines[2], " ^"); " default:",
assert.strictEqual(lines[3], "ERROR: More than one default clause in switch statement"); " ^",
"ERROR: More than one default clause in switch statement",
].join("\n"));
done(); done();
}); });
}); });

View File

@@ -294,6 +294,39 @@ describe("test/reduce.js", function() {
"// }", "// }",
]).join("\n")); ]).join("\n"));
}); });
it("Should maintain block-scope for const & let", function() {
if (semver.satisfies(process.version, "<4")) return;
var code = [
'"use strict";',
"",
"L: for (let a = (1 - .8).toString(); ;) {",
" if (!console.log(a)) {",
" break L;",
" }",
"}",
].join("\n");
var result = reduce_test(code, {
compress: {
unsafe_math: true,
},
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// (beautified)",
code,
"// output: 0.19999999999999996",
"// ",
"// minify: 0.2",
"// ",
"// options: {",
'// "compress": {',
'// "unsafe_math": true',
'// },',
'// "mangle": false',
"// }",
].join("\n"));
});
it("Should handle corner cases when intermediate case differs only in Error.message", function() { it("Should handle corner cases when intermediate case differs only in Error.message", function() {
if (semver.satisfies(process.version, "<=0.10")) return; if (semver.satisfies(process.version, "<=0.10")) return;
var result = reduce_test(read("test/input/reduce/diff_error.js"), { var result = reduce_test(read("test/input/reduce/diff_error.js"), {

View File

@@ -115,8 +115,8 @@ describe("String literals", function() {
UglifyJS.parse(test); UglifyJS.parse(test);
}, function(e) { }, function(e) {
return e instanceof UglifyJS.JS_Parse_Error return e instanceof UglifyJS.JS_Parse_Error
&& e.message === "Invalid hex-character pattern in string"; && /^Invalid escape sequence: \\u/.test(e.message);
}); }, test);
}); });
}); });
it("Should reject invalid code points in Unicode escape sequence", function() { it("Should reject invalid code points in Unicode escape sequence", function() {
@@ -130,8 +130,8 @@ describe("String literals", function() {
UglifyJS.parse(test); UglifyJS.parse(test);
}, function(e) { }, function(e) {
return e instanceof UglifyJS.JS_Parse_Error return e instanceof UglifyJS.JS_Parse_Error
&& /^Invalid character code: /.test(e.message); && /^Invalid escape sequence: \\u{1/.test(e.message);
}); }, test);
}); });
}); });
}); });

67
test/mocha/templates.js Normal file
View File

@@ -0,0 +1,67 @@
var assert = require("assert");
var run_code = require("../sandbox").run_code;
var semver = require("semver");
var UglifyJS = require("../node");
describe("Template literals", function() {
it("Should reject invalid literal", function() {
[
"`foo\\`",
"`foo${bar`",
"`foo${bar}",
].forEach(function(input) {
assert.throws(function() {
UglifyJS.parse(input);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error
&& e.message === "Unterminated template literal";
}, input);
});
});
it("Should reject invalid expression", function() {
[
"`foo${bar;}`",
"`foo${42bar}`",
].forEach(function(input) {
assert.throws(function() {
UglifyJS.parse(input);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, input);
});
});
it("Should process line-break characters correctly", function() {
[
// native line breaks
[ "`foo\nbar`", "`foo\nbar`" ],
[ "`foo\rbar`", "`foo\rbar`" ],
[ "`foo\r\nbar`", "`foo\nbar`" ],
[ "`foo\r\n\rbar`", "`foo\n\rbar`" ],
// escaped line breaks
[ "`foo\\nbar`", "`foo\\nbar`" ],
[ "`foo\\rbar`", "`foo\\rbar`" ],
[ "`foo\r\\nbar`", "`foo\r\\nbar`" ],
[ "`foo\\r\nbar`", "`foo\\r\nbar`" ],
[ "`foo\\r\\nbar`", "`foo\\r\\nbar`" ],
// continuation
[ "`foo\\\nbar`", "`foo\\\nbar`" ],
[ "`foo\\\rbar`", "`foo\\\rbar`" ],
[ "`foo\\\r\nbar`", "`foo\\\nbar`" ],
[ "`foo\\\r\n\rbar`", "`foo\\\n\rbar`" ],
[ "`foo\\\\nbar`", "`foo\\\\nbar`" ],
[ "`foo\\\\rbar`", "`foo\\\\rbar`" ],
[ "`foo\\\\r\nbar`", "`foo\\\\r\nbar`" ],
].forEach(function(test) {
var input = "console.log(" + test[0] + ");";
var result = UglifyJS.minify(input, {
compress: false,
mangle: false,
});
if (result.error) throw result.error;
var expected = "console.log(" + test[1] + ");";
assert.strictEqual(result.code, expected, test[0]);
if (semver.satisfies(process.version, "<4")) return;
assert.strictEqual(run_code(result.code), run_code(input), test[0]);
});
});
});

82
test/mocha/yields.js Normal file
View File

@@ -0,0 +1,82 @@
var assert = require("assert");
var UglifyJS = require("../node");
describe("generator", function() {
it("Should reject `yield` as symbol name within generator functions only", function() {
[
"function yield() {}",
"function(yield) {}",
"function() { yield:{} }",
"function() { var yield; }",
"function() { function yield() {} }",
"function() { try {} catch (yield) {} }",
].forEach(function(code) {
var ast = UglifyJS.parse("(" + code + ")();");
assert.strictEqual(ast.TYPE, "Toplevel");
assert.strictEqual(ast.body.length, 1);
assert.strictEqual(ast.body[0].TYPE, "SimpleStatement");
assert.strictEqual(ast.body[0].body.TYPE, "Call");
assert.strictEqual(ast.body[0].body.expression.TYPE, "Function");
assert.throws(function() {
UglifyJS.parse("(" + code.replace(/^function/, "function*") + ")();");
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
it("Should reject `yield` expression outside of generator functions", function() {
[
"yield 42;",
"function f() { yield 42; }",
"function* f() { function g() { yield 42; } }",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
it("Should reject `yield` expression directly on computed key of function argument", function() {
[
"function f({ [yield 42]: a }) {}",
"function* f({ [yield 42]: a }) {}",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
it("Should accept `yield` expression nested within computed key of function argument", function() {
[
"function f({ [function*() { yield 42; }()]: a }) {}",
"function* f({ [function*() { yield 42; }()]: a }) {}",
].forEach(function(code) {
var ast = UglifyJS.parse(code);
assert.strictEqual(ast.TYPE, "Toplevel");
assert.strictEqual(ast.body.length, 1);
assert.strictEqual(ast.body[0].argnames.length, 1);
assert.strictEqual(ast.body[0].argnames[0].TYPE, "DestructuredObject");
});
});
it("Should reject `yield*` without an expression", function() {
[
"yield*",
"yield*;",
"yield*,",
"(yield*)",
"[ yield* ]",
"42[yield*]",
"yield* && 42",
].forEach(function(code) {
code = "function* f() { " + code + " }";
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
});

View File

@@ -70,8 +70,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} else if (differs.error) { } else if (differs.error) {
differs.warnings = warnings; differs.warnings = warnings;
return differs; return differs;
} else if (is_error(differs.unminified_result) } else if (sandbox.is_error(differs.unminified_result)
&& is_error(differs.minified_result) && sandbox.is_error(differs.minified_result)
&& differs.unminified_result.name == differs.minified_result.name) { && differs.unminified_result.name == differs.minified_result.name) {
return { return {
code: [ code: [
@@ -131,10 +131,11 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
case "delete": case "delete":
return; return;
} }
if (parent instanceof U.AST_VarDef && parent.name === node) return;
// preserve for (var xxx; ...) // preserve for (var xxx; ...)
if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node; if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node;
// preserve for (xxx in ...) // preserve for (xxx in/of ...)
if (parent instanceof U.AST_ForIn && parent.init === node) return node; if (parent instanceof U.AST_ForEnumeration && parent.init === node) return node;
// node specific permutations with no parent logic // node specific permutations with no parent logic
@@ -299,10 +300,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
node.start._permute += step; node.start._permute += step;
if (expr && (expr !== node.body || !has_loopcontrol(expr, node, parent))) { if (expr && (expr !== node.body || !has_loopcontrol(expr, node, parent))) {
CHANGED = true; CHANGED = true;
return to_statement(expr); return to_statement_init(expr);
} }
} }
else if (node instanceof U.AST_ForIn) { else if (node instanceof U.AST_ForEnumeration) {
var expr; var expr;
switch ((node.start._permute * steps | 0) % 3) { switch ((node.start._permute * steps | 0) % 3) {
case 0: case 0:
@@ -321,7 +322,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
node.start._permute += step; node.start._permute += step;
if (expr) { if (expr) {
CHANGED = true; CHANGED = true;
return to_statement(expr); return to_statement_init(expr);
} }
} }
else if (node instanceof U.AST_If) { else if (node instanceof U.AST_If) {
@@ -558,8 +559,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
log(code); log(code);
log(diff.error.stack); log(diff.error.stack);
log("*** Discarding permutation and continuing."); log("*** Discarding permutation and continuing.");
} else if (is_error(diff.unminified_result) } else if (sandbox.is_error(diff.unminified_result)
&& is_error(diff.minified_result) && sandbox.is_error(diff.minified_result)
&& diff.unminified_result.name == diff.minified_result.name) { && diff.unminified_result.name == diff.minified_result.name) {
// ignore difference in error messages caused by minification // ignore difference in error messages caused by minification
diff_error_message = testcase; diff_error_message = testcase;
@@ -600,10 +601,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
var lines = [ "" ]; var lines = [ "" ];
if (isNaN(max_timeout)) { if (isNaN(max_timeout)) {
lines.push("// minify error: " + to_comment(strip_color_codes(differs.minified_result.stack))); lines.push("// minify error: " + to_comment(differs.minified_result.stack));
} else { } else {
var unminified_result = strip_color_codes(differs.unminified_result); var unminified_result = differs.unminified_result;
var minified_result = strip_color_codes(differs.minified_result); var minified_result = differs.minified_result;
if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) { if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) {
lines.push( lines.push(
"// (stringified)", "// (stringified)",
@@ -624,10 +625,6 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
}; };
function strip_color_codes(value) {
return ("" + value).replace(/\u001b\[\d+m/g, "");
}
function to_comment(value) { function to_comment(value) {
return ("" + value).replace(/\n/g, "\n// "); return ("" + value).replace(/\n/g, "\n// ");
} }
@@ -665,20 +662,12 @@ function has_loopcontrol(body, loop, label) {
return found; return found;
} }
function is_error(result) {
return result && typeof result.name == "string" && typeof result.message == "string";
}
function is_timed_out(result) { function is_timed_out(result) {
return is_error(result) && /timed out/.test(result.message); return sandbox.is_error(result) && /timed out/.test(result.message);
} }
function is_statement(node) { function is_statement(node) {
return node instanceof U.AST_Statement return node instanceof U.AST_Statement && !(node instanceof U.AST_LambdaExpression);
&& !(node instanceof U.AST_Arrow
|| node instanceof U.AST_AsyncArrow
|| node instanceof U.AST_AsyncFunction
|| node instanceof U.AST_Function);
} }
function merge_sequence(array, node) { function merge_sequence(array, node) {
@@ -706,6 +695,13 @@ function to_statement(node) {
}); });
} }
function to_statement_init(node) {
return node instanceof U.AST_Const || node instanceof U.AST_Let ? new U.AST_BlockStatement({
body: [ node ],
start: {},
}) : to_statement(node);;
}
function wrap_with_console_log(node) { function wrap_with_console_log(node) {
// wrap with console.log() // wrap with console.log()
return new U.AST_Call({ return new U.AST_Call({

View File

@@ -1,103 +1,39 @@
var readFileSync = require("fs").readFileSync;
var semver = require("semver"); var semver = require("semver");
var spawnSync = require("child_process").spawnSync;
var vm = require("vm"); var vm = require("vm");
var setupContext = new vm.Script([ setup_log();
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {", var setup_code = "(" + setup + ")(" + [
" f.toString = Function.prototype.toString;", "this",
"});", find_builtins(),
"Function.prototype.toString = function() {", setup_log,
" var id = 100000;", "function(process) {" + readFileSync(require.resolve("../tools/tty", "utf8")) + "}",
" return function() {", ].join(",\n") + ");\n";
" var n = this.name;", exports.has_toplevel = function(options) {
" if (!/^F[0-9]{6}N$/.test(n)) {", return options.toplevel
' n = "F" + ++id + "N";', || options.mangle && options.mangle.toplevel
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [ || options.compress && options.compress.toplevel;
' Object.defineProperty(this, "name", {', };
" get: function() {", exports.is_error = is_error;
" return n;",
" }",
" });",
] : [], [
" }",
' return "function(){}";',
" };",
"}();",
"this;",
]).join("\n"));
function createContext() {
var ctx = vm.createContext(Object.defineProperties({}, {
console: { value: { log: log } },
global: { get: self },
self: { get: self },
window: { get: self },
}));
var global = setupContext.runInContext(ctx);
return ctx;
function self() {
return this;
}
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
case "function":
return arg.toString();
case "object":
if (arg === global) return "[object global]";
if (/Error$/.test(arg.name)) return arg.toString();
if (typeof arg.then == "function") return "[object Promise]";
arg.constructor.toString();
if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get && !desc.set) arg[key] = safe_log(arg[key], level);
}
}
return arg;
}
function log(msg) {
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
}
function run_code(code, toplevel, timeout) {
timeout = timeout || 5000;
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
vm.runInContext(toplevel ? "(function(){" + code + "})()" : code, createContext(), { timeout: timeout });
return stdout;
} catch (ex) {
return ex;
} finally {
process.stdout.write = original_write;
}
}
exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) { exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) {
var stdout = run_code(code, toplevel, timeout); var stdout = run_code_vm(code, toplevel, timeout);
if (typeof stdout != "string" || !/arguments/.test(code)) return stdout; if (typeof stdout != "string" || !/arguments/.test(code)) return stdout;
do { do {
var prev = stdout; var prev = stdout;
stdout = run_code(code, toplevel, timeout); stdout = run_code_vm(code, toplevel, timeout);
} while (prev !== stdout); } while (prev !== stdout);
return stdout; return stdout;
} : run_code; } : semver.satisfies(process.version, "<0.12") ? run_code_vm : function(code, toplevel, timeout) {
if (/\basync([ \t]+[^\s()[\]{},.&|!~=*%/+-]+|[ \t]*\([\s\S]*?\))[ \t]*=>|\b(async[ \t]+function|setInterval|setTimeout)\b/.test(code)) {
function strip_func_ids(text) { return run_code_exec(code, toplevel, timeout);
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>"); } else {
} return run_code_vm(code, toplevel, timeout);
}
};
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) { exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false; if (typeof expected != typeof actual) return false;
if (typeof expected == "object" && typeof expected.name == "string" && typeof expected.message == "string") { if (is_error(expected)) {
if (expected.name !== actual.name) return false; if (expected.name !== actual.name) return false;
if (typeof actual.message != "string") return false; if (typeof actual.message != "string") return false;
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1); expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
@@ -107,8 +43,228 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
} : function(expected, actual) { } : function(expected, actual) {
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual); return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
}; };
exports.has_toplevel = function(options) {
return options.toplevel function is_error(result) {
|| options.mangle && options.mangle.toplevel return result && typeof result.name == "string" && typeof result.message == "string";
|| options.compress && options.compress.toplevel; }
};
function strip_color_codes(value) {
return value.replace(/\u001b\[\d+m/g, "");
}
function strip_func_ids(text) {
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
}
function setup_log() {
var inspect = require("util").inspect;
if (inspect.defaultOptions) {
var log_options = {
breakLength: Infinity,
colors: false,
compact: true,
customInspect: false,
depth: Infinity,
maxArrayLength: Infinity,
maxStringLength: Infinity,
showHidden: false,
};
for (var name in log_options) {
if (name in inspect.defaultOptions) inspect.defaultOptions[name] = log_options[name];
}
}
return inspect;
}
function find_builtins() {
setup_code = "console.log(Object.keys(this));";
var builtins = run_code_vm("");
if (semver.satisfies(process.version, ">=0.12")) builtins += ".concat(" + run_code_exec("") + ")";
return builtins;
}
function setup(global, builtins, setup_log, setup_tty) {
[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {
f.toString = Function.prototype.toString;
});
Function.prototype.toString = function() {
var configurable = Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable;
var id = 100000;
return function() {
var n = this.name;
if (!/^F[0-9]{6}N$/.test(n)) {
n = "F" + ++id + "N";
if (configurable) Object.defineProperty(this, "name", {
get: function() {
return n;
}
});
}
return "function(){}";
};
}();
var process = global.process;
if (process) {
setup_tty(process);
var inspect = setup_log();
process.on("uncaughtException", function(ex) {
var value = ex;
if (value instanceof Error) {
value = {};
for (var name in ex) {
value[name] = ex[name];
delete ex[name];
}
}
process.stderr.write(inspect(value) + "\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n");
throw ex;
}).on("unhandledRejection", function() {});
}
var log = console.log;
var safe_console = {
log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") return log("%s", msg);
return log.apply(null, [].map.call(arguments, function(arg) {
return safe_log(arg, {
level: 5,
original: [],
replaced: [],
});
}));
},
};
var props = {
// for Node.js v8
console: {
get: function() {
return safe_console;
},
},
global: { get: self },
self: { get: self },
window: { get: self },
};
[
// for Node.js v0.12
"Buffer",
"clearInterval",
"clearTimeout",
// for Node.js v0.12
"DTRACE_NET_STREAM_END",
// for Node.js v8
"process",
"setInterval",
"setTimeout",
].forEach(function(name) {
var value = global[name];
props[name] = {
get: function() {
return value;
},
};
});
builtins.forEach(function(name) {
try {
delete global[name];
} catch (e) {}
});
Object.defineProperties(global, props);
// for Node.js v8+
global.toString = function() {
return "[object global]";
};
function self() {
return this;
}
function safe_log(arg, cache) {
if (arg) switch (typeof arg) {
case "function":
return arg.toString();
case "object":
if (arg === global) return "[object global]";
if (/Error$/.test(arg.name)) return arg.toString();
if (typeof arg.then == "function") return "[object Promise]";
arg.constructor.toString();
var index = cache.original.indexOf(arg);
if (index >= 0) return cache.replaced[index];
if (--cache.level < 0) return "[object Object]";
var value = {};
cache.original.push(arg);
cache.replaced.push(value);
for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
if (desc && (desc.get || desc.set)) {
Object.defineProperty(value, key, desc);
} else {
value[key] = safe_log(arg[key], cache);
}
}
return value;
}
return arg;
}
}
function run_code_vm(code, toplevel, timeout) {
timeout = timeout || 5000;
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
var ctx = vm.createContext({ console: console });
// for Node.js v6
vm.runInContext(setup_code, ctx);
vm.runInContext(toplevel ? "(function(){" + code + "})();" : code, ctx, { timeout: timeout });
return strip_color_codes(stdout);
} catch (ex) {
return ex;
} finally {
process.stdout.write = original_write;
}
}
function run_code_exec(code, toplevel, timeout) {
if (toplevel) {
code = setup_code + "(function(){" + code + "})();";
} else {
code = code.replace(/^((["'])[^"']*\2(;|$))?/, function(directive) {
return directive + setup_code;
});
}
var result = spawnSync(process.argv[0], [ '--max-old-space-size=2048' ], {
encoding: "utf8",
input: code,
stdio: "pipe",
timeout: timeout || 5000,
});
if (result.status === 0) return result.stdout;
if (result.error && result.error.code == "ETIMEDOUT" || /FATAL ERROR:/.test(msg)) {
return new Error("Script execution timed out.");
}
if (result.error) return result.error;
var msg = result.stderr.replace(/\r\n/g, "\n");
var end = msg.indexOf("\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n");
var details;
if (end >= 0) {
details = msg.slice(0, end).replace(/<([1-9][0-9]*) empty items?>/g, function(match, count) {
return new Array(+count).join();
});
try {
details = vm.runInNewContext("(" + details + ")");
} catch (e) {}
}
var match = /\n([^:\s]*Error)(?:: ([\s\S]+?))?\n( at [\s\S]+)\n$/.exec(msg);
if (!match) return details;
var ex = new global[match[1]](match[2]);
ex.stack = ex.stack.slice(0, ex.stack.indexOf(" at ")) + match[3];
if (typeof details == "object") {
for (var name in details) ex[name] = details[name];
} else if (end >= 0) {
ex.details = details;
}
return ex;
}

View File

@@ -10,16 +10,17 @@ exports.init = function(url, auth, num) {
exports.should_stop = function(callback) { exports.should_stop = function(callback) {
read(base + "/actions/runs?per_page=100", function(reply) { read(base + "/actions/runs?per_page=100", function(reply) {
if (!reply || !Array.isArray(reply.workflow_runs)) return; if (!reply || !Array.isArray(reply.workflow_runs)) return;
var runs = reply.workflow_runs.filter(function(workflow) { var runs = reply.workflow_runs.sort(function(a, b) {
return workflow.status != "completed";
}).sort(function(a, b) {
return b.run_number - a.run_number; return b.run_number - a.run_number;
}); });
var found = false, remaining = 20; var found = false, remaining = 20;
(function next() { (function next() {
if (!runs.length) return; var workflow;
var workflow = runs.pop(); do {
workflow = runs.pop();
if (!workflow) return;
if (workflow.event == "schedule" && workflow.run_number == run_number) found = true; if (workflow.event == "schedule" && workflow.run_number == run_number) found = true;
} while (!found && workflow.status == "completed");
read(workflow.jobs_url, function(reply) { read(workflow.jobs_url, function(reply) {
if (!reply || !Array.isArray(reply.jobs)) return; if (!reply || !Array.isArray(reply.jobs)) return;
if (!reply.jobs.every(function(job) { if (!reply.jobs.every(function(job) {

View File

@@ -27,7 +27,7 @@ var STMT_IF_ELSE = STMT_("ifelse");
var STMT_DO_WHILE = STMT_("dowhile"); var STMT_DO_WHILE = STMT_("dowhile");
var STMT_WHILE = STMT_("while"); var STMT_WHILE = STMT_("while");
var STMT_FOR_LOOP = STMT_("forloop"); var STMT_FOR_LOOP = STMT_("forloop");
var STMT_FOR_IN = STMT_("forin"); var STMT_FOR_ENUM = STMT_("forenum");
var STMT_SEMI = STMT_("semi"); var STMT_SEMI = STMT_("semi");
var STMT_EXPR = STMT_("expr"); var STMT_EXPR = STMT_("expr");
var STMT_SWITCH = STMT_("switch"); var STMT_SWITCH = STMT_("switch");
@@ -134,16 +134,23 @@ var SUPPORT = function(matrix) {
}({ }({
arrow: "a => 0;", arrow: "a => 0;",
async: "async function f(){}", async: "async function f(){}",
async_generator: "async function* f(){}",
bigint: "42n",
catch_omit_var: "try {} catch {}", catch_omit_var: "try {} catch {}",
computed_key: "({[0]: 0});", computed_key: "({[0]: 0});",
const_block: "var a; { const a = 0; }", const_block: "var a; { const a = 0; }",
default_value: "[ a = 0 ] = [];", default_value: "[ a = 0 ] = [];",
destructuring: "[] = [];", destructuring: "[] = [];",
exponentiation: "0 ** 0",
for_await_of: "async function f(a) { for await (a of []); }",
for_of: "for (var a of []);",
generator: "function* f(){}",
let: "let a;", let: "let a;",
rest: "var [...a] = [];", rest: "var [...a] = [];",
rest_object: "var {...a} = {};", rest_object: "var {...a} = {};",
spread: "[...[]];", spread: "[...[]];",
spread_object: "({...0});", spread_object: "({...0});",
template: "``",
trailing_comma: "function f(a,) {}", trailing_comma: "function f(a,) {}",
}); });
@@ -167,7 +174,7 @@ var VALUES = [
"4", "4",
"5", "5",
"22", "22",
"-0", // 0/-0 !== 0 "(-0)", // 0/-0 !== 0
"23..toString()", "23..toString()",
"24 .toString()", "24 .toString()",
"25. ", "25. ",
@@ -188,6 +195,12 @@ var VALUES = [
'"function"', '"function"',
"this", "this",
]; ];
if (SUPPORT.bigint) VALUES = VALUES.concat([
"(!0o644n)",
"([3n][0] > 2)",
"(-42n).toString()",
"Number(0XDEADn << 16n | 0xbeefn)",
]);
var BINARY_OPS = [ var BINARY_OPS = [
" + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors) " + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
@@ -217,6 +230,7 @@ var BINARY_OPS = [
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
if (SUPPORT.exponentiation) BINARY_OPS.push("**");
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS.push(" in "); BINARY_OPS.push(" in ");
@@ -317,10 +331,9 @@ var VAR_NAMES = [
"NaN", "NaN",
"Infinity", "Infinity",
"arguments", "arguments",
"Math",
"parseInt",
"async", "async",
"await", "await",
"yield",
]; ];
var INITIAL_NAMES_LEN = VAR_NAMES.length; var INITIAL_NAMES_LEN = VAR_NAMES.length;
@@ -341,6 +354,7 @@ var avoid_vars = [];
var block_vars = []; var block_vars = [];
var unique_vars = []; var unique_vars = [];
var async = false; var async = false;
var generator = false;
var loops = 0; var loops = 0;
var funcs = 0; var funcs = 0;
var called = Object.create(null); var called = Object.create(null);
@@ -360,6 +374,7 @@ function createTopLevelCode() {
block_vars.length = 0; block_vars.length = 0;
unique_vars.length = 0; unique_vars.length = 0;
async = false; async = false;
generator = false;
loops = 0; loops = 0;
funcs = 0; funcs = 0;
called = Object.create(null); called = Object.create(null);
@@ -387,9 +402,11 @@ function addTrailingComma(list) {
return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list; return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list;
} }
function createParams(was_async, noDuplicate) { function createParams(was_async, was_generator, noDuplicate) {
var save_async = async; var save_async = async;
if (was_async) async = true; if (was_async) async = true;
var save_generator = generator;
if (was_generator) generator = true;
var len = unique_vars.length; var len = unique_vars.length;
var params = []; var params = [];
for (var n = rng(4); --n >= 0;) { for (var n = rng(4); --n >= 0;) {
@@ -398,12 +415,14 @@ function createParams(was_async, noDuplicate) {
params.push(name); params.push(name);
} }
unique_vars.length = len; unique_vars.length = len;
generator = save_generator;
async = save_async; async = save_async;
return addTrailingComma(params.join(", ")); return addTrailingComma(params.join(", "));
} }
function createArgs(recurmax, stmtDepth, canThrow) { function createArgs(recurmax, stmtDepth, canThrow, noTemplate) {
recurmax--; recurmax--;
if (SUPPORT.template && !noTemplate && rng(20) == 0) return createTemplateLiteral(recurmax, stmtDepth, canThrow);
var args = []; var args = [];
for (var n = rng(4); --n >= 0;) switch (SUPPORT.spread ? rng(50) : 3) { for (var n = rng(4); --n >= 0;) switch (SUPPORT.spread ? rng(50) : 3) {
case 0: case 0:
@@ -422,10 +441,10 @@ function createArgs(recurmax, stmtDepth, canThrow) {
args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow)); args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
break; break;
} }
return addTrailingComma(args.join(", ")); return "(" + addTrailingComma(args.join(", ")) + ")";
} }
function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async) { function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async, was_generator) {
var avoid = []; var avoid = [];
var len = unique_vars.length; var len = unique_vars.length;
var pairs = createPairs(recurmax, !nameLenBefore); var pairs = createPairs(recurmax, !nameLenBefore);
@@ -433,6 +452,22 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
unique_vars.length = len; unique_vars.length = len;
return pairs; return pairs;
function mapShuffled(values, fn) {
var declare_only = [];
var side_effects = [];
values.forEach(function(value, index) {
value = fn(value, index);
if (/]:|=/.test(value) ? canThrow && rng(10) == 0 : rng(5)) {
declare_only.splice(rng(declare_only.length + 1), 0, value);
} else if (canThrow && rng(5) == 0) {
side_effects.splice(rng(side_effects.length + 1), 0, value);
} else {
side_effects.push(value);
}
});
return declare_only.concat(side_effects);
}
function convertToRest(names) { function convertToRest(names) {
var last = names.length - 1; var last = names.length - 1;
if (last >= 0 && SUPPORT.rest && rng(20) == 0) { if (last >= 0 && SUPPORT.rest && rng(20) == 0) {
@@ -451,9 +486,18 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
async = false; async = false;
if (save_async || was_async) addAvoidVar("await"); if (save_async || was_async) addAvoidVar("await");
} }
var save_generator = generator;
if (was_generator != null) {
generator = false;
if (save_generator || was_generator) addAvoidVar("yield");
}
avoid.forEach(addAvoidVar); avoid.forEach(addAvoidVar);
var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore); var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore);
if (nameFn) nameFn(); if (nameFn) nameFn();
if (was_generator != null) {
generator = was_generator;
if (save_generator || was_generator) removeAvoidVar("yield");
}
if (was_async != null) { if (was_async != null) {
async = was_async; async = was_async;
if (save_async || was_async) removeAvoidVar("await"); if (save_async || was_async) removeAvoidVar("await");
@@ -461,6 +505,7 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
if (valueFn) valueFn(); if (valueFn) valueFn();
if (save_vars) [].push.apply(VAR_NAMES, save_vars); if (save_vars) [].push.apply(VAR_NAMES, save_vars);
avoid.forEach(removeAvoidVar); avoid.forEach(removeAvoidVar);
generator = save_generator;
async = save_async; async = save_async;
} }
@@ -484,7 +529,10 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity"); unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
var save_async = async; var save_async = async;
if (was_async) async = true; if (was_async) async = true;
var save_generator = generator;
if (was_generator) generator = true;
var name = createVarName(MANDATORY); var name = createVarName(MANDATORY);
generator = save_generator;
async = save_async; async = save_async;
unique_vars.length -= 6; unique_vars.length -= 6;
avoid.push(name); avoid.push(name);
@@ -563,19 +611,24 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
} }
}); });
fill(function() { fill(function() {
var last = pairs.names.length - 1, has_rest = false; var last = pairs.names.length - 1, rest;
var s = pairs.names.map(function(name, index) { if (last >= 0 && !(last in keys) && SUPPORT.rest_object && rng(20) == 0) {
if (index in keys) return keys[index] + ": " + name; rest = pairs.names.pop();
if (index == last && SUPPORT.rest_object && rng(20) == 0 && name.indexOf("=") < 0) { if (!/=/.test(rest)) rest = "..." + rest;
has_rest = true;
return "..." + name;
} }
var s = mapShuffled(pairs.names, function(name, index) {
if (index in keys) return keys[index] + ": " + name;
return rng(10) == 0 ? name : createKey(recurmax, keys) + ": " + name; return rng(10) == 0 ? name : createKey(recurmax, keys) + ": " + name;
}).join(", "); });
if (!has_rest) s = addTrailingComma(s); if (rest) {
s.push(rest);
s = s.join(", ");
} else {
s = addTrailingComma(s.join(", "));
}
names.unshift("{ " + s + " }" + createDefaultValue(recurmax, noDefault)); names.unshift("{ " + s + " }" + createDefaultValue(recurmax, noDefault));
}, function() { }, function() {
values.unshift("{ " + addTrailingComma(pairs.values.map(function(value, index) { values.unshift("{ " + addTrailingComma(mapShuffled(pairs.values, function(value, index) {
var key = index in keys ? keys[index] : createKey(recurmax, keys); var key = index in keys ? keys[index] : createKey(recurmax, keys);
return key + ": " + value; return key + ": " + value;
}).join(", ")) + " }"); }).join(", ")) + " }");
@@ -677,7 +730,25 @@ function mayCreateBlockVariables(recurmax, stmtDepth, canThrow, fn) {
} }
function makeFunction(name) { function makeFunction(name) {
return (async ? "async function " : "function ") + name; if (generator) {
name = "function* " + name;
} else {
name = "function " + name;
}
if (async) name = "async " + name;
return name;
}
function invokeGenerator(was_generator) {
if (generator && !was_generator) switch (rng(4)) {
case 0:
return ".next()";
case 1:
return ".next().done";
case 2:
return ".next().value";
}
return "";
} }
function createFunction(recurmax, allowDefun, canThrow, stmtDepth) { function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
@@ -688,6 +759,15 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
var save_async = async; var save_async = async;
async = SUPPORT.async && rng(50) == 0; async = SUPPORT.async && rng(50) == 0;
var save_generator = generator;
generator = SUPPORT.generator && rng(50) == 0;
if (async && generator && !SUPPORT.async_generator) {
if (rng(2)) {
async = false;
} else {
generator = false;
}
}
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
if (allowDefun || rng(5) > 0) { if (allowDefun || rng(5) > 0) {
name = "f" + funcs++; name = "f" + funcs++;
@@ -699,12 +779,12 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
var params; var params;
if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) { if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) {
called[name] = false; called[name] = false;
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async); var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async, save_generator);
params = pairs.names.join(", "); params = pairs.names.join(", ");
if (!pairs.has_rest) params = addTrailingComma(params); if (!pairs.has_rest) params = addTrailingComma(params);
args = addTrailingComma(pairs.values.join(", ")); args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
} else { } else {
params = createParams(save_async); params = createParams(save_async, save_generator);
} }
s.push(makeFunction(name) + "(" + params + "){", strictMode()); s.push(makeFunction(name) + "(" + params + "){", strictMode());
s.push(defns()); s.push(defns());
@@ -718,16 +798,20 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
s.push("}", ""); s.push("}", "");
s = filterDirective(s).join("\n"); s = filterDirective(s).join("\n");
}); });
var call_next = invokeGenerator(save_generator);
generator = save_generator;
async = save_async; async = save_async;
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
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 += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")"; s += args || createArgs(recurmax, stmtDepth, canThrow);
s += call_next;
} else if (!(name in called) || args || rng(3)) { } else if (!(name in called) || args || rng(3)) {
s += "var " + createVarName(MANDATORY) + " = " + name; s += "var " + createVarName(MANDATORY) + " = " + name;
s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")"; s += args || createArgs(recurmax, stmtDepth, canThrow);
s += call_next;
} }
return s + ";"; return s + ";";
@@ -820,21 +904,58 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
return label.target + "for (var brake" + loop + " = 5; " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && brake" + loop + " > 0; --brake" + loop + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth); return label.target + "for (var brake" + loop + " = 5; " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && brake" + loop + " > 0; --brake" + loop + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
case STMT_FOR_IN: case STMT_FOR_ENUM:
var label = createLabel(canBreak, canContinue); var label = createLabel(canBreak, canContinue);
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
var key = rng(10) ? "key" + loop : getVarName(NO_CONST); var key = rng(10) ? "key" + loop : getVarName(NO_CONST);
return [ var of = SUPPORT.for_of && rng(20) == 0;
"{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ", var init = "";
label.target + " for (", if (!/^key/.test(key)) {
!/^key/.test(key) ? rng(10) ? "" : "var " : !SUPPORT.let || rng(10) ? "var " : rng(2) ? "let " : "const ", if (!(of && bug_for_of_var) && rng(10) == 0) init = "var ";
!SUPPORT.destructuring || rng(10) ? key : rng(5) ? "[ " + key + " ]" : "{ length: " + key + " }", } else if (!SUPPORT.let || !(of && bug_for_of_var) && rng(10)) {
" in expr" + loop + ") {", init = "var ";
rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "", } else if (rng(2)) {
createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth), init = "let ";
"}}", } else {
].join(""); init = "const ";
}
if (!SUPPORT.destructuring || of && !(canThrow && rng(10) == 0) || rng(10)) {
init += key;
} else if (rng(5)) {
init += "[ " + key + " ]";
} else {
init += "{ length: " + key + " }";
}
var s = "var expr" + loop + " = ";
if (of) {
var await = SUPPORT.for_await_of && async && rng(20) == 0;
if (SUPPORT.generator && rng(20) == 0) {
var gen = getVarName();
if (canThrow && rng(10) == 0) {
s += gen + "; ";
} else {
s += gen + " && typeof " + gen + "[Symbol.";
s += await ? "asyncIterator" : "iterator";
s += '] == "function" ? ' + gen + " : " + createArrayLiteral(recurmax, stmtDepth, canThrow) + "; ";
}
} else if (rng(5)) {
s += createArrayLiteral(recurmax, stmtDepth, canThrow) + "; ";
} else if (canThrow && rng(10) == 0) {
s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ";
} else {
s += '"" + (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "); ";
}
s += label.target + " for ";
if (await) s += "await ";
s += "(" + init + " of expr" + loop + ") {";
} else {
s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ";
s += label.target + " for (" + init + " in expr" + loop + ") {";
}
if (rng(3)) s += "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; ";
s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
return "{" + s + "}";
case STMT_SEMI: case STMT_SEMI:
return use_strict && rng(20) === 0 ? '"use strict";' : ";"; return use_strict && rng(20) === 0 ? '"use strict";' : ";";
case STMT_EXPR: case STMT_EXPR:
@@ -989,7 +1110,9 @@ function createExpression(recurmax, noComma, stmtDepth, canThrow) {
return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented
default: default:
var expr = "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")"; var expr = "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")";
return async && rng(50) == 0 ? "(await" + expr + ")" : expr; if (async && rng(50) == 0) return "(await" + expr + ")";
if (generator && rng(50) == 0) return "(yield" + (canThrow && rng(20) == 0 ? "*" : "") + expr + ")";
return expr;
} }
} }
@@ -997,6 +1120,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
var p = 0; var p = 0;
switch (rng(_createExpression.N)) { switch (rng(_createExpression.N)) {
case p++: case p++:
if (generator && rng(50) == 0) return "yield";
case p++: case p++:
return createUnaryPrefix() + (rng(2) ? "a" : "b"); return createUnaryPrefix() + (rng(2) ? "a" : "b");
case p++: case p++:
@@ -1010,6 +1134,11 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
return rng(2) + " === 1 ? a : b"; return rng(2) + " === 1 ? a : b";
case p++: case p++:
if (SUPPORT.template && rng(20) == 0) {
var tmpl = createTemplateLiteral(recurmax, stmtDepth, canThrow);
if (rng(10) == 0) tmpl = "String.raw" + tmpl;
return tmpl;
}
case p++: case p++:
return createValue(); return createValue();
case p++: case p++:
@@ -1020,9 +1149,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case 0: case 0:
return [ return [
"[ ", "[ ",
new Array(rng(3)).join(","), new Array(rng(3)).join(),
getVarName(NO_CONST), getVarName(NO_CONST),
new Array(rng(3)).join(","), new Array(rng(3)).join(),
" ] = ", " ] = ",
createArrayLiteral(recurmax, stmtDepth, canThrow), createArrayLiteral(recurmax, stmtDepth, canThrow),
].join(""); ].join("");
@@ -1047,25 +1176,34 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
var save_async = async; var save_async = async;
async = SUPPORT.async && rng(50) == 0; async = SUPPORT.async && rng(50) == 0;
var save_generator = generator;
generator = SUPPORT.generator && rng(50) == 0;
if (async && generator && !SUPPORT.async_generator) {
if (rng(2)) {
async = false;
} else {
generator = false;
}
}
unique_vars.push("c"); unique_vars.push("c");
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that. var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
unique_vars.pop(); unique_vars.pop();
var s = []; var s = [];
switch (rng(5)) { switch (rng(5)) {
case 0: case 0:
if (SUPPORT.arrow && !name && rng(2)) { if (SUPPORT.arrow && !name && !generator && rng(2)) {
var args, suffix; var args, suffix;
(rng(2) ? createBlockVariables : function() { (rng(2) ? createBlockVariables : function() {
arguments[3](); arguments[3]();
})(recurmax, stmtDepth, canThrow, function(defns) { })(recurmax, stmtDepth, canThrow, function(defns) {
var params; var params;
if (SUPPORT.destructuring && rng(2)) { if (SUPPORT.destructuring && rng(2)) {
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async); var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async, save_generator);
params = pairs.names.join(", "); params = pairs.names.join(", ");
if (!pairs.has_rest) params = addTrailingComma(params); if (!pairs.has_rest) params = addTrailingComma(params);
args = addTrailingComma(pairs.values.join(", ")); args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
} else { } else {
params = createParams(save_async, NO_DUPLICATE); params = createParams(save_async, save_generator, NO_DUPLICATE);
} }
params = (async ? "async (" : "(") + params + ") => "; params = (async ? "async (" : "(") + params + ") => ";
if (defns) { if (defns) {
@@ -1092,17 +1230,18 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
suffix = ")"; suffix = ")";
} }
}); });
generator = save_generator;
async = save_async; async = save_async;
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow); if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow);
if (args) suffix += "(" + args + ")"; if (args) suffix += args;
s.push(suffix); s.push(suffix);
} else { } else {
s.push( s.push(
"(" + makeFunction(name) + "(){", "(" + makeFunction(name) + "(){",
strictMode(), strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
rng(2) ? "})" : "})()" rng(2) ? "})" : "})()" + invokeGenerator(save_generator)
); );
} }
break; break;
@@ -1111,7 +1250,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
"+" + makeFunction(name) + "(){", "+" + makeFunction(name) + "(){",
strictMode(), strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()" "}()" + invokeGenerator(save_generator)
); );
break; break;
case 2: case 2:
@@ -1119,7 +1258,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
"!" + makeFunction(name) + "(){", "!" + makeFunction(name) + "(){",
strictMode(), strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()" "}()" + invokeGenerator(save_generator)
); );
break; break;
case 3: case 3:
@@ -1127,15 +1266,16 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
"void " + makeFunction(name) + "(){", "void " + makeFunction(name) + "(){",
strictMode(), strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()" "}()" + invokeGenerator(save_generator)
); );
break; break;
default: default:
async = false; async = false;
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { generator = false;
var instantiate = rng(4) ? "new " : ""; var instantiate = rng(4) ? "new " : "";
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
s.push( s.push(
instantiate + "function " + name + "(" + createParams(save_async) + "){", instantiate + "function " + name + "(" + createParams(save_async, save_generator) + "){",
strictMode(), strictMode(),
defns() defns()
); );
@@ -1145,11 +1285,13 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
} }
s.push(_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)); s.push(_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
}); });
generator = save_generator;
async = save_async; async = save_async;
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
s.push(rng(2) ? "}" : "}(" + createArgs(recurmax, stmtDepth, canThrow) + ")"); s.push(rng(2) ? "}" : "}" + createArgs(recurmax, stmtDepth, canThrow, instantiate));
break; break;
} }
generator = save_generator;
async = save_async; async = save_async;
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
return filterDirective(s).join("\n"); return filterDirective(s).join("\n");
@@ -1225,7 +1367,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
var name = getVarName(); var name = getVarName();
var s = name + "." + getDotKey(); var s = name + "." + getDotKey();
s = "typeof " + s + ' == "function" && --_calls_ >= 0 && ' + s + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")"; s = "typeof " + s + ' == "function" && --_calls_ >= 0 && ' + s + createArgs(recurmax, stmtDepth, canThrow);
return canThrow && rng(8) == 0 ? s : name + " && " + s; return canThrow && rng(8) == 0 ? s : name + " && " + s;
case p++: case p++:
case p++: case p++:
@@ -1236,7 +1378,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2); name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
} while (name in called && !called[name]); } 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);
} }
_createExpression.N = p; _createExpression.N = p;
return _createExpression(recurmax, noComma, stmtDepth, canThrow); return _createExpression(recurmax, noComma, stmtDepth, canThrow);
@@ -1270,9 +1412,29 @@ function createArrayLiteral(recurmax, stmtDepth, canThrow) {
return "[" + arr.join(", ") + "]"; return "[" + arr.join(", ") + "]";
} }
function createTemplateLiteral(recurmax, stmtDepth, canThrow) {
recurmax--;
var s = [];
addText();
for (var i = rng(6); --i >= 0;) {
s.push("${", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "}");
addText();
}
return "`" + s.join(rng(5) ? "" : "\n") + "`";
function addText() {
while (rng(5) == 0) s.push([
" ",
"$",
"}",
"\\`",
"\\\\",
"tmpl",
][rng(6)]);
}
}
var SAFE_KEYS = [ var SAFE_KEYS = [
"length",
"foo",
"a", "a",
"b", "b",
"c", "c",
@@ -1280,7 +1442,13 @@ var SAFE_KEYS = [
"null", "null",
"NaN", "NaN",
"Infinity", "Infinity",
"done",
"foo",
"in", "in",
"length",
"next",
"then",
"value",
"var", "var",
]; ];
var KEYS = [ var KEYS = [
@@ -1310,12 +1478,15 @@ function createObjectKey(recurmax, stmtDepth, canThrow) {
function createObjectFunction(recurmax, stmtDepth, canThrow) { function createObjectFunction(recurmax, stmtDepth, canThrow) {
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
var save_async = async; var save_async = async;
var save_generator = generator;
var s; var s;
var name = createObjectKey(recurmax, stmtDepth, canThrow); var name = createObjectKey(recurmax, stmtDepth, canThrow);
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { var fn;
switch (rng(SUPPORT.computed_key ? 3 : 2)) { switch (rng(SUPPORT.computed_key ? 3 : 2)) {
case 0: case 0:
async = false; async = false;
generator = false;
fn = function(defns) {
s = [ s = [
"get " + name + "(){", "get " + name + "(){",
strictMode(), strictMode(),
@@ -1324,6 +1495,7 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
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),
"},", "},",
]; ];
};
break; break;
case 1: case 1:
var prop; var prop;
@@ -1331,6 +1503,8 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
prop = getDotKey(); prop = getDotKey();
} while (name == prop); } while (name == prop);
async = false; async = false;
generator = false;
fn = function(defns) {
s = [ s = [
"set " + name + "(" + createVarName(MANDATORY) + "){", "set " + name + "(" + createVarName(MANDATORY) + "){",
strictMode(), strictMode(),
@@ -1339,19 +1513,33 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
"this." + prop + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";", "this." + prop + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
"},", "},",
]; ];
};
break; break;
default: default:
async = SUPPORT.async && rng(50) == 0; async = SUPPORT.async && rng(50) == 0;
generator = SUPPORT.generator && rng(50) == 0;
if (async && generator && !SUPPORT.async_generator) {
if (rng(2)) {
async = false;
} else {
generator = false;
}
}
fn = function(defns) {
if (generator) name = "*" + name;
if (async) name = "async "+ name;
s = [ s = [
(async ? "async " : "") + name + "(" + createParams(save_async, NO_DUPLICATE) + "){", name + "(" + createParams(save_async, save_generator, NO_DUPLICATE) + "){",
strictMode(), strictMode(),
defns(), defns(),
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), _createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"},", "},",
] ]
};
break; break;
} }
}); createBlockVariables(recurmax, stmtDepth, canThrow, fn);
generator = save_generator;
async = save_async; async = save_async;
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
return filterDirective(s).join("\n"); return filterDirective(s).join("\n");
@@ -1500,8 +1688,11 @@ function createTypeofExpr(recurmax, stmtDepth, canThrow) {
function createVar() { function createVar() {
var save_async = async; var save_async = async;
var save_generator = generator;
if (!async && avoid_vars.indexOf("await") >= 0) async = true; if (!async && avoid_vars.indexOf("await") >= 0) async = true;
if (!generator && avoid_vars.indexOf("yield") >= 0) generator = true;
var name = createVarName(MANDATORY, DONT_STORE); var name = createVarName(MANDATORY, DONT_STORE);
generator = save_generator;
async = save_async; async = save_async;
return name; return name;
} }
@@ -1550,18 +1741,26 @@ function getVarName(noConst) {
do { do {
if (--tries < 0) return "a"; if (--tries < 0) return "a";
name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)]; name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)];
} while (!name || avoid_vars.indexOf(name) >= 0 || noConst && block_vars.indexOf(name) >= 0 || async && name == "await"); } while (!name
|| avoid_vars.indexOf(name) >= 0
|| noConst && block_vars.indexOf(name) >= 0
|| async && name == "await"
|| generator && name == "yield");
return name; return name;
} }
function createVarName(maybe, dontStore) { function createVarName(maybe, dontStore) {
if (!maybe || rng(2)) { if (!maybe || rng(2)) {
var suffix = rng(3); var suffix = rng(3);
var name; var name, tries = 10;
do { do {
name = VAR_NAMES[rng(VAR_NAMES.length)]; name = VAR_NAMES[rng(VAR_NAMES.length)];
if (--tries < 0) suffix++;
if (suffix) name += "_" + suffix; if (suffix) name += "_" + suffix;
} while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0 || async && name == "await"); } while (unique_vars.indexOf(name) >= 0
|| block_vars.indexOf(name) >= 0
|| async && name == "await"
|| generator && name == "yield");
if (!dontStore) VAR_NAMES.push(name); if (!dontStore) VAR_NAMES.push(name);
return name; return name;
} }
@@ -1743,24 +1942,52 @@ function log(options) {
} }
function sort_globals(code) { function sort_globals(code) {
var globals = sandbox.run_code("throw Object.keys(this).sort();" + code); var globals = sandbox.run_code("throw Object.keys(this).sort(" + function(global) {
return function(m, n) {
return (n == "toString") - (m == "toString")
|| (typeof global[n] == "function") - (typeof global[m] == "function")
|| (m < n ? -1 : m > n ? 1 : 0);
};
} + "(this));" + code);
if (!Array.isArray(globals)) {
errorln();
errorln();
errorln("//-------------------------------------------------------------");
errorln("// !!! sort_globals() failed !!!");
errorln("// expected Array, got:");
if (!sandbox.is_error(globals)) try {
globals = JSON.stringify(globals);
} catch (e) {}
errorln(globals);
errorln("//");
errorln(code);
errorln();
return code;
}
return globals.length ? "var " + globals.map(function(name) { return globals.length ? "var " + globals.map(function(name) {
return name + "=" + name; return name + "=" + name;
}).join(",") + ";" + code : code; }).join() + ";" + code : code;
} }
function fuzzy_match(original, uglified) { function fuzzy_match(original, uglified) {
uglified = uglified.split(" "); var m = [], n = [];
var i = uglified.length; if (collect(original, m) !== collect(uglified, n)) return false;
original = original.split(" ", i); for (var i = 0; i < m.length; i++) {
while (--i >= 0) { var a = m[i];
if (original[i] === uglified[i]) continue; var b = n[i];
var a = +original[i]; if (Math.abs((b - a) / a) > 1e-10) return false;
var b = +uglified[i];
if (Math.abs((b - a) / a) < 1e-10) continue;
return false;
} }
return true; return true;
function collect(input, nums) {
return input.replace(/-?([1-9][0-9]*(\.[0-9]+)?|0\.[0-9]+)(e-?[1-9][0-9]*)?/ig, function(num) {
return "<|" + nums.push(+num) + "|>";
});
}
}
function is_error_timeout(ex) {
return /timed out/.test(ex.message);
} }
function is_error_in(ex) { function is_error_in(ex) {
@@ -1768,13 +1995,17 @@ function is_error_in(ex) {
} }
function is_error_spread(ex) { function is_error_spread(ex) {
return ex.name == "TypeError" && /Found non-callable @@iterator|is not iterable|undefined is not a function/.test(ex.message); return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| is not a function/.test(ex.message);
} }
function is_error_recursion(ex) { function is_error_recursion(ex) {
return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message); return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message);
} }
function is_error_destructuring(ex) {
return ex.name == "TypeError" && /^Cannot destructure /.test(ex.message);
}
function patch_try_catch(orig, toplevel) { function patch_try_catch(orig, toplevel) {
var stack = [ { var stack = [ {
code: orig, code: orig,
@@ -1782,6 +2013,7 @@ function patch_try_catch(orig, toplevel) {
offset: 0, offset: 0,
tries: [], tries: [],
} ]; } ];
var tail_throw = '\nif (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;\n';
var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)[{]+)\)|}\s*finally)\s*(?={)/g; var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)[{]+)\)|}\s*finally)\s*(?={)/g;
while (stack.length) { while (stack.length) {
var code = stack[0].code; var code = stack[0].code;
@@ -1798,7 +2030,7 @@ function patch_try_catch(orig, toplevel) {
var insert; var insert;
if (/}\s*finally\s*$/.test(match[0])) { if (/}\s*finally\s*$/.test(match[0])) {
tries.shift(); tries.shift();
insert = 'if (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;'; insert = tail_throw;
} else { } else {
while (tries.length && tries[0].catch) tries.shift(); while (tries.length && tries[0].catch) tries.shift();
tries[0].catch = index - offset; tries[0].catch = index - offset;
@@ -1812,9 +2044,9 @@ function patch_try_catch(orig, toplevel) {
"throw " + match[1] + ";", "throw " + match[1] + ";",
].join("\n"); ].join("\n");
} }
var new_code = code.slice(0, index) + insert + code.slice(index); var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw;
var result = sandbox.run_code(new_code, toplevel); var result = sandbox.run_code(new_code, toplevel);
if (typeof result != "object" || typeof result.name != "string" || typeof result.message != "string") { if (!sandbox.is_error(result)) {
if (!stack.filled && match[1]) stack.push({ if (!stack.filled && match[1]) stack.push({
code: code, code: code,
index: index && index - 1, index: index && index - 1,
@@ -1832,6 +2064,9 @@ function patch_try_catch(orig, toplevel) {
} else if (is_error_recursion(result)) { } else if (is_error_recursion(result)) {
index = result.ufuzz_try; index = result.ufuzz_try;
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index); return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
} else if (is_error_destructuring(result)) {
index = result.ufuzz_catch;
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("cannot destructure");' + orig.slice(index);
} }
} }
stack.filled = true; stack.filled = true;
@@ -1853,12 +2088,13 @@ if (typeof sandbox.run_code("A:if (0) B:; else B:;") != "string") {
if (o.mangle) o.mangle.v8 = true; if (o.mangle) o.mangle.v8 = true;
}); });
} }
var is_bug_async_arrow_rest = function() {}; var bug_async_arrow_rest = function() {};
if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && typeof sandbox.run_code("async (a = f(...[], b)) => 0;") != "string") { if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && typeof sandbox.run_code("async (a = f(...[], b)) => 0;") != "string") {
is_bug_async_arrow_rest = function(ex) { bug_async_arrow_rest = function(ex) {
return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter"; return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter";
}; };
} }
var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && typeof sandbox.run_code("try {} catch (e) { for (var e of []); }") != "string";
if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") { if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") {
beautify_options.output.v8 = true; beautify_options.output.v8 = true;
minify_options.forEach(function(o) { minify_options.forEach(function(o) {
@@ -1875,37 +2111,56 @@ for (var round = 1; round <= num_iterations; round++) {
original_code = createTopLevelCode(); original_code = createTopLevelCode();
var orig_result = [ sandbox.run_code(original_code), sandbox.run_code(original_code, true) ]; var orig_result = [ sandbox.run_code(original_code), sandbox.run_code(original_code, true) ];
errored = typeof orig_result[0] != "string"; if (orig_result.some(function(result, toplevel) {
if (errored) { if (typeof result == "string") return;
println(); println();
println(); println();
println("//============================================================="); println("//=============================================================");
println("// original code"); println("// original code" + (toplevel ? " (toplevel)" : ""));
try_beautify(original_code, false, orig_result[0], println); try_beautify(original_code, toplevel, result, println);
println(); println();
println(); println();
println("original result:"); println("original result:");
println(orig_result[0]); println(result);
println(); println();
if (is_bug_async_arrow_rest(orig_result[0])) continue; // ignore v8 parser bug
} return bug_async_arrow_rest(result);
})) continue;
minify_options.forEach(function(options) { minify_options.forEach(function(options) {
var o = JSON.parse(options); var o = JSON.parse(options);
var toplevel = sandbox.has_toplevel(o); var toplevel = sandbox.has_toplevel(o);
o.validate = true; o.validate = true;
uglify_code = UglifyJS.minify(original_code, o); uglify_code = UglifyJS.minify(original_code, o);
original_result = orig_result[toplevel ? 1 : 0]; original_result = orig_result[toplevel ? 1 : 0];
errored = typeof original_result != "string";
if (!uglify_code.error) { if (!uglify_code.error) {
uglify_code = uglify_code.code; uglify_code = uglify_code.code;
uglify_result = sandbox.run_code(uglify_code, toplevel); uglify_result = sandbox.run_code(uglify_code, toplevel);
ok = sandbox.same_stdout(original_result, uglify_result); ok = sandbox.same_stdout(original_result, uglify_result);
// ignore v8 parser bug
if (!ok && bug_async_arrow_rest(uglify_result)) ok = true;
// handle difference caused by time-outs
if (!ok && errored && is_error_timeout(original_result)) {
if (is_error_timeout(uglify_result)) {
// ignore difference in error message
ok = true;
} else {
// ignore spurious time-outs
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 declaration order of global variables // ignore declaration order of global variables
if (!ok && !toplevel) { if (!ok && !toplevel) {
ok = sandbox.same_stdout(sandbox.run_code(sort_globals(original_code)), sandbox.run_code(sort_globals(uglify_code))); ok = sandbox.same_stdout(sandbox.run_code(sort_globals(original_code)), sandbox.run_code(sort_globals(uglify_code)));
} }
// ignore numerical imprecision caused by `unsafe_math` // ignore numerical imprecision caused by `unsafe_math`
if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == "string" && typeof uglify_result == "string") { if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == typeof uglify_result) {
if (typeof original_result == "string") {
ok = fuzzy_match(original_result, uglify_result); ok = fuzzy_match(original_result, uglify_result);
} else if (sandbox.is_error(original_result)) {
ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message);
}
if (!ok) { if (!ok) {
var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel); var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel);
ok = sandbox.same_stdout(fuzzy_result, uglify_result); ok = sandbox.same_stdout(fuzzy_result, uglify_result);
@@ -1913,11 +2168,6 @@ 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 && uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError") ok = true; 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`
if (!ok && errored && is_error_in(uglify_result) && is_error_in(original_result)) ok = true; if (!ok && errored && is_error_in(uglify_result) && is_error_in(original_result)) ok = true;
// ignore difference in error message caused by spread syntax // ignore difference in error message caused by spread syntax
@@ -1926,6 +2176,10 @@ for (var round = 1; round <= num_iterations; round++) {
if (!ok && errored && is_error_recursion(original_result)) { if (!ok && errored && is_error_recursion(original_result)) {
if (is_error_recursion(uglify_result) || typeof uglify_result == "string") ok = true; if (is_error_recursion(uglify_result) || typeof uglify_result == "string") ok = true;
} }
// ignore difference in error message caused by destructuring
if (!ok && errored && is_error_destructuring(uglify_result) && is_error_destructuring(original_result)) {
ok = true;
}
// ignore errors above when caught by try-catch // ignore errors above when caught by try-catch
if (!ok) { if (!ok) {
var orig_skipped = patch_try_catch(original_code, toplevel); var orig_skipped = patch_try_catch(original_code, toplevel);