Compare commits

...

119 Commits

Author SHA1 Message Date
Alex Lam S.L
fc7678c115 v3.16.3 2022-07-25 06:08:04 +08:00
Alex Lam S.L
b371dc2d1e fix corner case in collapse_vars (#5574)
fixes #5573
2022-07-23 07:18:26 +08:00
Alex Lam S.L
56e9454f1f fix corner case with spread syntax (#5572) 2022-07-23 03:07:04 +08:00
Alex Lam S.L
d67daa8314 support string namespace in import & export (#5570) 2022-07-20 05:55:38 +08:00
Alex Lam S.L
f0120e90b6 fix corner case collapse_vars (#5569)
fixes #5568
2022-07-18 09:04:51 +08:00
Alex Lam S.L
ec4558be29 fix corner cases with parameter scope (#5567)
fixes #5566
2022-07-17 16:03:12 +08:00
Alex Lam S.L
685ab357cc document v8 quirks (#5565)
closes #5564
2022-07-17 00:22:16 +08:00
Alex Lam S.L
5792f30175 fix corner case in evaluate (#5559)
fixes #5558
2022-07-15 20:36:52 +08:00
Alex Lam S.L
24443b6764 enhance collapse_vars (#5556) 2022-07-12 23:49:43 +08:00
Alex Lam S.L
154edf0427 enhance collapse_vars (#5555) 2022-07-11 23:10:40 +08:00
Alex Lam S.L
4778cf88e2 upgrade AST<->ESTree translation (#5554) 2022-07-11 07:18:25 +08:00
Alex Lam S.L
38bd4f65d0 fix corner cases in collapse_vars (#5553)
fixes #5552
2022-07-09 20:50:54 +08:00
Alex Lam S.L
0b808f6428 parse import expressions correctly (#5551)
fixes #5550
2022-07-08 19:25:30 +08:00
Alex Lam S.L
b2bc2e1173 parse export & import statements correctly (#5550)
fixes #5548
2022-07-08 04:04:56 +08:00
Alex Lam S.L
80787ff7ef minor cleanups (#5549) 2022-07-08 03:14:54 +08:00
Alex Lam S.L
b92a89f325 fix corner case in conditionals (#5548) 2022-07-07 21:28:33 +08:00
Alex Lam S.L
902292f776 fix corner case in conditionals (#5547)
fixes #5546
2022-07-07 15:49:33 +08:00
Alex Lam S.L
3dcf098468 fix corner cases in conditionals & switches (#5545)
fixes #5543
fixes #5544
2022-07-07 14:59:06 +08:00
Alex Lam S.L
d89f0965aa enhance conditionals (#5542) 2022-07-07 12:17:23 +08:00
Alex Lam S.L
c8d98f4787 enhance if_return (#5541) 2022-07-07 04:28:00 +08:00
Alex Lam S.L
0207b46d70 enhance if_return & inline (#5538) 2022-07-06 11:40:07 +08:00
Alex Lam S.L
aa2a9fbedb v3.16.2 2022-07-04 08:50:56 +08:00
Alex Lam S.L
3596b4feda fix corner case in inline (#5537)
fixes #5536
2022-07-02 00:10:02 +08:00
Alex Lam S.L
51deeff72e enhance inline (#5535) 2022-07-01 11:24:16 +08:00
Alex Lam S.L
4c227cc6bd fix corner cases in inline & unused (#5534)
fixes #5533
2022-06-30 15:34:45 +08:00
Alex Lam S.L
2426657daa fix corner case in inline (#5532)
fixes #5531
2022-06-30 04:09:53 +08:00
Alex Lam S.L
e1b03d0235 fix corner case in inline (#5529)
fixes #5528
2022-06-29 07:37:58 +08:00
Alex Lam S.L
f1b3e9df1e fix corner case in inline (#5527)
fixes #5526
2022-06-26 20:48:14 +08:00
Alex Lam S.L
fcc87edb71 fix corner cases in dead_code & if_return (#5525)
fixes #5521
fixes #5522
fixes #5523
fixes #5524
2022-06-26 18:40:56 +08:00
Alex Lam S.L
8b464331ba enhance dead_code & if_return (#5520) 2022-06-26 12:32:25 +08:00
Alex Lam S.L
9f57920566 enhance if_return (#5518) 2022-06-24 00:52:22 +08:00
Alex Lam S.L
933ca9ddd8 fix corner case in reduce_vars (#5517)
fixes #5516
2022-06-19 03:27:00 +08:00
Alex Lam S.L
74e36e4456 v3.16.1 2022-06-17 07:53:29 +08:00
Alex Lam S.L
4382bfe848 fix corner case in collapse_vars (#5513)
fixes #5512
2022-06-13 07:55:15 +08:00
Alex Lam S.L
b6f250f5c9 enhance unused (#5511) 2022-06-12 21:24:42 +08:00
Alex Lam S.L
5d69545299 enhance unsafe_comps (#5510) 2022-06-12 12:15:43 +08:00
Alex Lam S.L
139fad0c05 fix corner cases with instanceof (#5509)
- enhance `evaluate`
2022-06-12 10:01:54 +08:00
Alex Lam S.L
99946a3993 fix corner case in dead_code (#5507)
fixes #5506
2022-06-12 05:26:51 +08:00
Alex Lam S.L
053cb27fe3 fix corner case in collapse_vars (#5505)
fixes #5504
2022-06-10 09:12:59 +08:00
Alex Lam S.L
25017978e7 fix corner case in collapse_vars (#5503)
fixes #5502
2022-06-10 02:07:07 +08:00
Alex Lam S.L
f749863cb2 document ECMAScript quirks (#5501)
closes #5500
2022-06-09 03:01:00 +08:00
Alex Lam S.L
123f9cf987 fix corner case in hoist_props (#5499)
fixes #5498
2022-06-07 23:29:42 +08:00
Alex Lam S.L
a758b40e3f suppress false positives in ufuzz (#5497) 2022-06-07 23:28:06 +08:00
Alex Lam S.L
44e5e99aae parse directives within arrow functions correctly (#5496)
fixes #5495
2022-06-07 10:33:17 +08:00
Alex Lam S.L
be53c4838b fix corner case in collapse_vars (#5494)
fixes #5493
2022-06-06 23:36:19 +08:00
Alex Lam S.L
0c7b016fa7 fix corner case in inline & module (#5492)
fixes #5491
2022-06-06 22:52:22 +08:00
Alex Lam S.L
00665766da fix corner case in side_effects (#5490)
fixes #5489
2022-06-06 20:32:32 +08:00
Alex Lam S.L
88b4283200 support class static initialization block (#5488) 2022-06-06 12:01:15 +08:00
Alex Lam S.L
d2bd0d1c1c support top-level await (#5487) 2022-06-06 11:52:01 +08:00
Alex Lam S.L
25441d44f6 v3.16.0 2022-06-06 11:29:26 +08:00
Alex Lam S.L
a025392a30 fix corner case in comparisons (#5486)
fixes #5485
2022-06-05 00:47:38 +08:00
Alex Lam S.L
ad5f5ef2a3 fix corner case in webkit (#5483)
fixes #5480
2022-06-02 02:45:02 +08:00
Chen Yangjian
40e669eacb docs: toplevel webkit option sets compress.webkit as well (#5480) 2022-06-02 02:44:06 +08:00
Alex Lam S.L
ad3a331ca3 fix corner case in collapse_vars (#5482)
fixes #5481
2022-06-02 02:42:39 +08:00
Alex Lam S.L
3c9e1693d5 fix corner case in side_effects (#5479)
fixes #5478
2022-05-31 00:27:40 +08:00
Alex Lam S.L
8bc03dc6c4 fix corner case in keep_fargs (#5477)
fixes #5476
2022-05-29 12:10:19 +08:00
Alex Lam S.L
2152f00de2 enhance inline & module (#5475) 2022-05-27 11:57:05 +08:00
Alex Lam S.L
94aae05d45 fix corner case in merge_vars (#5472)
fixes #5471
2022-05-27 03:13:24 +08:00
Alex Lam S.L
0dbf2b1d3c suppress false positives in ufuzz (#5473) 2022-05-26 23:17:47 +08:00
Alex Lam S.L
59b23b8c13 fix corner case in booleans (#5470)
fixes #5469
2022-05-26 05:33:50 +08:00
Alex Lam S.L
5979b195fe suppress false positives in ufuzz (#5468) 2022-05-25 23:50:47 +08:00
Alex Lam S.L
a1cff23377 suppress false positives in ufuzz (#5467) 2022-05-25 06:35:59 +08:00
Alex Lam S.L
c82fc1ef71 implement --module (#5462) 2022-05-24 05:45:47 +08:00
Alex Lam S.L
740f93f5a9 fix corner case in merge_vars (#5466)
fixes #5465
2022-05-24 05:45:07 +08:00
Alex Lam S.L
d4caa97b88 fix corner case in reduce_vars (#5464)
fixes #5463
2022-05-23 11:08:12 +08:00
Alex Lam S.L
c2ca7b7659 drop unused extends properly (#5461) 2022-05-23 03:53:32 +08:00
Alex Lam S.L
59edda6ca5 suppress false positives in ufuzz (#5460) 2022-05-22 01:36:45 +08:00
Alex Lam S.L
1668bc33c3 improve ufuzz coverage (#5459) 2022-05-21 02:57:13 +08:00
Alex Lam S.L
01f1e3fef8 avoid v8 quirks in ufuzz (#5458) 2022-05-20 00:00:24 +08:00
Alex Lam S.L
27aa85f84b fix corner cases in merge_vars (#5457)
fixes #5456
2022-05-19 06:39:20 +08:00
Alex Lam S.L
33c9c48318 fix corner case in hoist_props & unused (#5455)
fixes #5454
2022-05-19 04:45:38 +08:00
Alex Lam S.L
63f16e4616 fix corner case in merge_vars (#5452)
fixes #5451
2022-05-18 02:41:05 +08:00
Alex Lam S.L
cb6dd34b98 avoid broken versions of Node.js (#5453) 2022-05-18 02:40:31 +08:00
Alex Lam S.L
27727e6926 fix corner cases in unused (#5449)
fixes #5448
2022-05-17 17:03:06 +08:00
Alex Lam S.L
a968ddc78c avoid webpack bug in web-tooling-benchmark (#5450) 2022-05-17 14:08:11 +08:00
Alex Lam S.L
f70462aeb2 fix corner case in merge_vars (#5445)
fixes #5444
2022-05-16 16:30:14 +08:00
Alex Lam S.L
3aa92c76cc avoid extends error in ufuzz (#5447) 2022-05-16 11:11:10 +08:00
Alex Lam S.L
fc6a66836a fix corner case in unused (#5446)
fixes #5444
2022-05-16 09:13:30 +08:00
Alex Lam S.L
31167da1a9 avoid extends error in test cases (#5443) 2022-05-16 06:50:22 +08:00
Alex Lam S.L
7db2ada880 fix corner case in hoist_props (#5442)
fixes #5441
2022-05-16 06:49:09 +08:00
Alex Lam S.L
e31bbe329a improve compatibility with use strict (#5440) 2022-05-14 12:15:54 +08:00
Alex Lam S.L
8946c87011 suppress invalid test case generation (#5439)
- document v8 quirks

closes #5438
2022-05-12 04:38:11 +08:00
Alex Lam S.L
a9ef659bcb v3.15.5 2022-05-11 05:26:10 +08:00
Alex Lam S.L
35c2149dbd fix corner case in merge_vars (#5437)
fixes #5436
2022-05-08 04:16:28 +08:00
Alex Lam S.L
89a35f9fcd fix corner case in reduce_vars (#5435)
fixes #5434
2022-05-06 09:32:47 +08:00
Alex Lam S.L
1a4e99dc2d avoid v8 quirks in ufuzz (#5431)
closes #5428
closes #5429
2022-04-25 21:33:31 +08:00
Alex Lam S.L
cb870f6fd6 document v8 quirks (#5430)
closes #5428
closes #5429
2022-04-21 02:51:53 +08:00
Alex Lam S.L
a0c0c294c5 fix corner case in assignments (#5426)
fixes #5425
2022-04-19 13:19:30 +08:00
Alex Lam S.L
fbdb7eeda3 fix corner case in merge_vars (#5424)
fixes #5423
2022-04-19 09:04:06 +08:00
Alex Lam S.L
1bc0fccc8c improve ufuzz resilience (#5422) 2022-04-18 13:03:01 +08:00
Alex Lam S.L
20252c6483 fix corner case in merge_vars (#5421)
fixes #5420
2022-04-18 06:38:08 +08:00
Alex Lam S.L
e396912ea2 suppress false positives with export & import (#5418) 2022-04-16 04:27:50 +08:00
Alex Lam S.L
5ebfa78f56 fix corner case with arguments (#5417)
fixes #5416
2022-04-15 06:52:10 +08:00
Alex Lam S.L
950609f578 fix corner case in inline (#5415)
fixes #5414
2022-04-13 06:19:37 +08:00
Alex Lam S.L
4a44d95f09 v3.15.4 2022-04-10 01:16:11 +08:00
David Luhmer
36718948be rename reserved keyword await (#5413) 2022-04-08 00:31:29 +08:00
Alex Lam S.L
21bd4c4a9d fix corner cases in collapse_vars & hoist_vars (#5412)
fixes #5411
2022-04-07 04:12:03 +08:00
Alex Lam S.L
998c9792da fix corner case in inline (#5410)
fixes #5409
2022-04-06 12:23:47 +08:00
Alex Lam S.L
ccd77d70db fix corner case in reduce_vars (#5408)
fixes #5407
2022-04-05 10:09:25 +08:00
Alex Lam S.L
d75a946707 fix corner case in reduce_vars (#5406)
fixes #5405
2022-04-03 21:57:37 +08:00
Alex Lam S.L
696a20f10d patch export default within sandbox correctly (#5404)
fixes #5403
2022-04-03 19:56:19 +08:00
Alex Lam S.L
224c91b6c1 fix corner case in inline (#5402)
fixes #5401
2022-04-03 01:12:53 +08:00
Alex Lam S.L
8065e27a7d patch export default within sandbox correctly (#5400)
fixes #5399
2022-04-02 21:59:28 +08:00
Alex Lam S.L
584e253f33 enahnce collapse_vars (#5398) 2022-04-01 20:26:27 +08:00
Alex Lam S.L
fb5e08e4ec fix corner case in collapse_vars (#5397)
fixes #5396
2022-03-31 20:02:56 +08:00
Alex Lam S.L
e3d328f741 fix corner case in collapse_vars (#5395)
fixes #5394
2022-03-30 01:22:57 +08:00
Alex Lam S.L
8922f08fbf fix corner cases in keep_fnames (#5393) 2022-03-29 03:01:01 +08:00
Alex Lam S.L
15a4074d1a fix corner case in unused (#5392)
fixes #5391
2022-03-28 05:46:43 +08:00
Alex Lam S.L
c624b43739 fix corner case in collapse_vars (#5390)
fixes #5389
2022-03-22 13:05:57 +08:00
Alex Lam S.L
a8e040b133 fix corner case in properties (#5388)
fixes #5387
2022-03-21 00:01:42 +08:00
Alex Lam S.L
5e30f3a48b fix corner case in inline (#5386)
fixes #5385
2022-03-20 22:50:28 +08:00
Alex Lam S.L
46570a4eb6 fix corner case in side_effects (#5383)
fixes #5382
2022-03-12 14:14:30 +08:00
Alex Lam S.L
01b84074d7 fix corner case in evaluate (#5381)
fixes #5380
2022-03-12 13:08:29 +08:00
Alex Lam S.L
7aba2dc5f2 v3.15.3 2022-03-10 12:50:19 +08:00
Alex Lam S.L
12a6728c4e fix corner case in hoist_vars (#5379)
fixes #5378
2022-03-06 17:56:00 +08:00
Alex Lam S.L
042c228c7b fix corner case in inline (#5377)
fixes #5376
2022-03-06 03:29:56 +08:00
Alex Lam S.L
e2b00814a8 fix corner cases in inline (#5375) 2022-03-04 04:05:31 +08:00
Alex Lam S.L
104d385ba9 fix corner case in ie (#5372)
fixes #5370
2022-03-02 13:52:27 +08:00
Alex Lam S.L
fdbbef2991 enhance conditionals (#5371) 2022-03-02 11:00:37 +08:00
60 changed files with 8487 additions and 1274 deletions

View File

@@ -17,13 +17,13 @@ jobs:
fail-fast: false
matrix:
include:
- node: latest
- node: '16'
os: macos-latest
- node: '8'
- node: '12'
os: ubuntu-latest
- node: '8'
os: ubuntu-latest
- node: '8'
- node: '12'
os: windows-latest
- node: '8'
os: windows-latest

View File

@@ -118,6 +118,7 @@ a double dash to prevent input files being used as option arguments:
--keep-fargs Do not mangle/drop function arguments.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--module Process input as ES module (implies --toplevel)
--name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options:
@@ -146,7 +147,7 @@ a double dash to prevent input files being used as option arguments:
--warn Print warning messages.
--webkit Support non-standard Safari/Webkit.
Equivalent to setting `webkit: true` in `minify()`
for `mangle` and `output` options.
for `compress`, `mangle` and `output` options.
By default UglifyJS will not try to be Safari-proof.
--wrap <name> Embed everything in a big function, making the
“exports” and “global” variables available. You
@@ -517,6 +518,10 @@ if (result.error) throw result.error;
- `mangle.properties` (default: `false`) — a subcategory of the mangle option.
Pass an object to specify custom [mangle property options](#mangle-properties-options).
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` and support for top-level `await`,
alongside with `toplevel` enabled.
- `nameCache` (default: `null`) — pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
@@ -628,7 +633,13 @@ to be `false` and all symbol names will be omitted.
- `bare_returns` (default: `false`) — support top level `return` statements
- `html5_comments` (default: `true`)
- `expression` (default: `false`) — parse as a single expression, e.g. JSON
- `html5_comments` (default: `true`) — process HTML comment as workaround for
browsers which do not recognise `<script>` tags
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` and support for top-level `await`.
- `shebang` (default: `true`) — support `#!command` as the first line
@@ -728,6 +739,9 @@ to be `false` and all symbol names will be omitted.
- `merge_vars` (default: `true`) — combine and reuse variables.
- `module` (default: `false`) — set to `true` if you wish to process input as
ES module, i.e. implicit `"use strict";` alongside with `toplevel` enabled.
- `negate_iife` (default: `true`) — negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the
code generator would insert.
@@ -804,8 +818,9 @@ to be `false` and all symbol names will be omitted.
- `unsafe` (default: `false`) — apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: `false`) — compress expressions like `a <= b` assuming
none of the operands can be (coerced to) `NaN`.
- `unsafe_comps` (default: `false`) — assume operands cannot be (coerced to) `NaN`
in numeric comparisons, e.g. `a <= b`. In addition, expressions involving `in`
or `instanceof` would never throw.
- `unsafe_Function` (default: `false`) — compress and mangle `Function(args, code)`
when both `args` and `code` are string literals.
@@ -1331,10 +1346,8 @@ To allow for better optimizations, the compiler makes various assumptions:
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
var await;
async function f() {
class A {
static p = await;
}
class A {
static p = await;
}
// SyntaxError: Unexpected reserved word
```
@@ -1346,7 +1359,7 @@ To allow for better optimizations, the compiler makes various assumptions:
// SyntaxError: The left-hand side of a for-of loop may not be 'async'.
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
- Some versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
console.log({
@@ -1355,9 +1368,15 @@ To allow for better optimizations, the compiler makes various assumptions:
return "FAIL";
},
[42]: "PASS",
}[42], {
...console,
get 42() {
return "FAIL";
},
42: "PASS",
}[42]);
// Expected: "PASS"
// Actual: "FAIL"
// Expected: "PASS PASS"
// Actual: "PASS FAIL"
```
UglifyJS may modify the input which in turn may suppress those errors.
- Earlier versions of JavaScript will throw `TypeError` with the following:
@@ -1373,3 +1392,57 @@ To allow for better optimizations, the compiler makes various assumptions:
// TypeError: const 'a' has already been declared
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
try {
class A {
static 42;
static get 42() {}
}
console.log("PASS");
} catch (e) {
console.log("FAIL");
}
// Expected: "PASS"
// Actual: "FAIL"
```
UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
(async function(a) {
(function() {
var b = await => console.log("PASS");
b();
})();
})().catch(console.error);
// Expected: "PASS"
// Actual: SyntaxError: Unexpected reserved word
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
try {
f();
function f() {
throw 42;
}
} catch (e) {
console.log(typeof f, e);
}
// Expected: "function 42"
// Actual: "undefined 42"
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
"use strict";
console.log(function f() {
return f = "PASS";
}());
// Expected: "PASS"
// Actual: TypeError: invalid assignment to const 'f'
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -107,6 +107,7 @@ function process_option(name, no_value) {
" --ie Support non-standard Internet Explorer.",
" --keep-fargs Do not mangle/drop function arguments.",
" --keep-fnames Do not mangle/drop function names. Useful for code relying on Function.prototype.name.",
" --module Process input as ES module (implies --toplevel)",
" --name-cache <file> File to hold mangled name mappings.",
" --rename Force symbol expansion.",
" --no-rename Disable symbol expansion.",
@@ -152,6 +153,7 @@ function process_option(name, no_value) {
case "annotations":
case "ie":
case "ie8":
case "module":
case "timings":
case "toplevel":
case "v8":

View File

@@ -109,6 +109,9 @@ var AST_Node = DEFNODE("Node", "start end", {
start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last token of this node"
},
equals: function(node) {
return this.TYPE == node.TYPE && this._equals(node);
},
walk: function(visitor) {
visitor.visit(this);
},
@@ -138,6 +141,7 @@ var AST_Node = DEFNODE("Node", "start end", {
}, null);
DEF_BITPROPS(AST_Node, [
// AST_Node
"_optimized",
"_squeezed",
// AST_Call
@@ -172,6 +176,8 @@ DEF_BITPROPS(AST_Node, [
"pure",
// AST_Assign
"redundant",
// AST_Node
"single_use",
// AST_ClassProperty
"static",
// AST_Call
@@ -231,6 +237,24 @@ AST_Node.disable_validation = function() {
while (restore = restore_transforms.pop()) restore();
};
function all_equals(k, l) {
return k.length == l.length && all(k, function(m, i) {
return m.equals(l[i]);
});
}
function list_equals(s, t) {
return s.length == t.length && all(s, function(u, i) {
return u == t[i];
});
}
function prop_equals(u, v) {
if (u === v) return true;
if (u == null) return v == null;
return u instanceof AST_Node && v instanceof AST_Node && u.equals(v);
}
/* -----[ statements ]----- */
var AST_Statement = DEFNODE("Statement", null, {
@@ -242,6 +266,7 @@ var AST_Statement = DEFNODE("Statement", null, {
var AST_Debugger = DEFNODE("Debugger", null, {
$documentation: "Represents a debugger statement",
_equals: return_true,
}, AST_Statement);
var AST_Directive = DEFNODE("Directive", "quote value", {
@@ -250,6 +275,9 @@ var AST_Directive = DEFNODE("Directive", "quote value", {
quote: "[string?] the original quote character",
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
},
_equals: function(node) {
return this.value == node.value;
},
_validate: function() {
if (this.quote != null) {
if (typeof this.quote != "string") throw new Error("quote must be string");
@@ -260,7 +288,8 @@ var AST_Directive = DEFNODE("Directive", "quote value", {
}, AST_Statement);
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
$documentation: "The empty statement (empty block or simply a semicolon)"
$documentation: "The empty statement (empty block or simply a semicolon)",
_equals: return_true,
}, AST_Statement);
function is_statement(node) {
@@ -291,6 +320,9 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
$propdoc: {
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)",
},
_equals: function(node) {
return this.body.equals(node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -342,6 +374,9 @@ var AST_Block = DEFNODE("Block", "body", {
$propdoc: {
body: "[AST_Statement*] an array of statements"
},
_equals: function(node) {
return all_equals(this.body, node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -376,6 +411,10 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
$propdoc: {
label: "[AST_Label] a label definition"
},
_equals: function(node) {
return this.label.equals(node.label)
&& this.body.equals(node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -417,6 +456,10 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", {
$propdoc: {
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
},
_equals: function(node) {
return this.body.equals(node.body)
&& this.condition.equals(node.condition);
},
_validate: function() {
if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop");
must_be_expression(this, "condition");
@@ -431,7 +474,7 @@ var AST_Do = DEFNODE("Do", null, {
node.body.walk(visitor);
node.condition.walk(visitor);
});
}
},
}, AST_DWLoop);
var AST_While = DEFNODE("While", null, {
@@ -442,7 +485,7 @@ var AST_While = DEFNODE("While", null, {
node.condition.walk(visitor);
node.body.walk(visitor);
});
}
},
}, AST_DWLoop);
var AST_For = DEFNODE("For", "init condition step", {
@@ -452,6 +495,12 @@ var AST_For = DEFNODE("For", "init condition step", {
condition: "[AST_Node?] the `for` termination clause, or null if empty",
step: "[AST_Node?] the `for` update clause, or null if empty"
},
_equals: function(node) {
return prop_equals(this.init, node.init)
&& prop_equals(this.condition, node.condition)
&& prop_equals(this.step, node.step)
&& this.body.equals(node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -479,6 +528,11 @@ var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", {
init: "[AST_Node] the assignment target during iteration",
object: "[AST_Node] the object to iterate over"
},
_equals: function(node) {
return this.init.equals(node.init)
&& this.object.equals(node.object)
&& this.body.equals(node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -519,6 +573,10 @@ var AST_With = DEFNODE("With", "expression", {
$propdoc: {
expression: "[AST_Node] the `with` expression"
},
_equals: function(node) {
return this.expression.equals(node.expression)
&& this.body.equals(node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -534,7 +592,7 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "fn_defs may_call_this uses_eval uses_with", {
$documentation: "Base class for all statements introducing a lexical scope",
$documentation: "Base class for all statements introducing a lambda scope",
$propdoc: {
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
@@ -592,6 +650,10 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
}
}, AST_Scope);
var AST_ClassInitBlock = DEFNODE("ClassInitBlock", null, {
$documentation: "Value for `class` static initialization blocks",
}, AST_Scope);
var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_arguments", {
$documentation: "Base class for functions",
$propdoc: {
@@ -617,6 +679,13 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_argu
});
if (this.rest) this.rest.walk(tw);
},
_equals: function(node) {
return prop_equals(this.rest, node.rest)
&& prop_equals(this.name, node.name)
&& prop_equals(this.value, node.value)
&& all_equals(this.argnames, node.argnames)
&& all_equals(this.body, node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -827,6 +896,14 @@ var AST_Class = DEFNODE("Class", "extends name properties", {
extends: "[AST_Node?] the super class, or null if not specified",
properties: "[AST_ClassProperty*] array of class properties",
},
_equals: function(node) {
return prop_equals(this.name, node.name)
&& prop_equals(this.extends, node.extends)
&& all_equals(this.properties, node.properties);
},
resolve: function(def_class) {
return def_class ? this : this.parent_scope.resolve();
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -871,11 +948,17 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, {
var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
$documentation: "Base class for `class` properties",
$propdoc: {
key: "[string|AST_Node] property name (AST_Node for computed property)",
key: "[string|AST_Node?] property name (AST_Node for computed property, null for initialization block)",
private: "[boolean] whether this is a private property",
static: "[boolean] whether this is a static property",
value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)",
},
_equals: function(node) {
return !this.private == !node.private
&& !this.static == !node.static
&& prop_equals(this.key, node.key)
&& prop_equals(this.value, node.value);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -885,7 +968,9 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
},
_validate: function() {
if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty");
if (typeof this.key != "string") {
if (this instanceof AST_ClassInit) {
if (this.key != null) throw new Error("key must be null");
} else if (typeof this.key != "string") {
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
must_be_expression(this, "key");
}
@@ -925,6 +1010,17 @@ var AST_ClassMethod = DEFNODE("ClassMethod", null, {
},
}, AST_ClassProperty);
var AST_ClassInit = DEFNODE("ClassInit", null, {
$documentation: "A `class` static initialization block",
_validate: function() {
if (!this.static) throw new Error("static must be true");
if (!(this.value instanceof AST_ClassInitBlock)) throw new Error("value must be AST_ClassInitBlock");
},
initialize: function() {
this.static = true;
},
}, AST_ClassProperty);
/* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, {
@@ -939,6 +1035,9 @@ var AST_Exit = DEFNODE("Exit", "value", {
$propdoc: {
value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
},
_equals: function(node) {
return prop_equals(this.value, node.value);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -969,6 +1068,9 @@ var AST_LoopControl = DEFNODE("LoopControl", "label", {
$propdoc: {
label: "[AST_LabelRef?] the label, or null if none",
},
_equals: function(node) {
return prop_equals(this.label, node.label);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -999,6 +1101,11 @@ var AST_If = DEFNODE("If", "condition alternative", {
condition: "[AST_Node] the `if` condition",
alternative: "[AST_Statement?] the `else` part, or null if not present"
},
_equals: function(node) {
return this.body.equals(node.body)
&& this.condition.equals(node.condition)
&& prop_equals(this.alternative, node.alternative);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1022,6 +1129,10 @@ var AST_Switch = DEFNODE("Switch", "expression", {
$propdoc: {
expression: "[AST_Node] the `switch` “discriminant”"
},
_equals: function(node) {
return this.expression.equals(node.expression)
&& all_equals(this.body, node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1053,6 +1164,10 @@ var AST_Case = DEFNODE("Case", "expression", {
$propdoc: {
expression: "[AST_Node] the `case` expression"
},
_equals: function(node) {
return this.expression.equals(node.expression)
&& all_equals(this.body, node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1073,6 +1188,11 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
bcatch: "[AST_Catch?] the catch block, or null if not present",
bfinally: "[AST_Finally?] the finally block, or null if not present"
},
_equals: function(node) {
return all_equals(this.body, node.body)
&& prop_equals(this.bcatch, node.bcatch)
&& prop_equals(this.bfinally, node.bfinally);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1096,6 +1216,10 @@ var AST_Catch = DEFNODE("Catch", "argname", {
$propdoc: {
argname: "[(AST_Destructured|AST_SymbolCatch)?] symbol for the exception, or null if not present",
},
_equals: function(node) {
return prop_equals(this.argname, node.argname)
&& all_equals(this.body, node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1121,6 +1245,9 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
$propdoc: {
definitions: "[AST_VarDef*] array of variable definitions"
},
_equals: function(node) {
return all_equals(this.definitions, node.definitions);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1177,6 +1304,10 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
name: "[AST_Destructured|AST_SymbolVar] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer",
},
_equals: function(node) {
return this.name.equals(node.name)
&& prop_equals(this.value, node.value);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1196,6 +1327,9 @@ var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", {
$propdoc: {
body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export",
},
_equals: function(node) {
return this.body.equals(node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1216,6 +1350,9 @@ var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
$propdoc: {
body: "[AST_Node] the default export",
},
_equals: function(node) {
return this.body.equals(node.body);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1229,29 +1366,29 @@ var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
},
}, AST_Statement);
var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path quote", {
var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path", {
$documentation: "An `export ... from '...'` statement",
$propdoc: {
aliases: "[string*] array of aliases to export",
keys: "[string*] array of keys to import",
path: "[string] the path to import module",
quote: "[string?] the original quote character",
aliases: "[AST_String*] array of aliases to export",
keys: "[AST_String*] array of keys to import",
path: "[AST_String] the path to import module",
},
_equals: function(node) {
return this.path.equals(node.path)
&& all_equals(this.aliases, node.aliases)
&& all_equals(this.keys, node.keys);
},
_validate: function() {
if (this.aliases.length != this.keys.length) {
throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length);
}
this.aliases.forEach(function(name) {
if (typeof name != "string") throw new Error("aliases must contain string");
if (!(name instanceof AST_String)) throw new Error("aliases must contain AST_String");
});
this.keys.forEach(function(name) {
if (typeof name != "string") throw new Error("keys must contain string");
if (!(name instanceof AST_String)) throw new Error("keys must contain AST_String");
});
if (typeof this.path != "string") throw new Error("path must be string");
if (this.quote != null) {
if (typeof this.quote != "string") throw new Error("quote must be string");
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
}
if (!(this.path instanceof AST_String)) throw new Error("path must be AST_String");
},
}, AST_Statement);
@@ -1260,6 +1397,9 @@ var AST_ExportReferences = DEFNODE("ExportReferences", "properties", {
$propdoc: {
properties: "[AST_SymbolExport*] array of aliases to export",
},
_equals: function(node) {
return all_equals(this.properties, node.properties);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1275,14 +1415,20 @@ var AST_ExportReferences = DEFNODE("ExportReferences", "properties", {
},
}, AST_Statement);
var AST_Import = DEFNODE("Import", "all default path properties quote", {
var AST_Import = DEFNODE("Import", "all default path properties", {
$documentation: "An `import` statement",
$propdoc: {
all: "[AST_SymbolImport?] the imported namespace, or null if not specified",
default: "[AST_SymbolImport?] the alias for default `export`, or null if not specified",
path: "[string] the path to import module",
path: "[AST_String] the path to import module",
properties: "[(AST_SymbolImport*)?] array of aliases, or null if not specified",
quote: "[string?] the original quote character",
},
_equals: function(node) {
return this.path.equals(node.path)
&& prop_equals(this.all, node.all)
&& prop_equals(this.default, node.default)
&& !this.properties == !node.properties
&& (!this.properties || all_equals(this.properties, node.properties));
},
walk: function(visitor) {
var node = this;
@@ -1301,16 +1447,12 @@ var AST_Import = DEFNODE("Import", "all default path properties quote", {
}
if (this.default != null) {
if (!(this.default instanceof AST_SymbolImport)) throw new Error("default must be AST_SymbolImport");
if (this.default.key !== "") throw new Error("invalid default key: " + this.default.key);
if (this.default.key.value !== "") throw new Error("invalid default key: " + this.default.key.value);
}
if (typeof this.path != "string") throw new Error("path must be string");
if (!(this.path instanceof AST_String)) throw new Error("path must be AST_String");
if (this.properties != null) this.properties.forEach(function(node) {
if (!(node instanceof AST_SymbolImport)) throw new Error("properties must contain AST_SymbolImport");
});
if (this.quote != null) {
if (typeof this.quote != "string") throw new Error("quote must be string");
if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote);
}
},
}, AST_Statement);
@@ -1320,6 +1462,10 @@ var AST_DefaultValue = DEFNODE("DefaultValue", "name value", {
name: "[AST_Destructured|AST_SymbolDeclaration] name of the variable",
value: "[AST_Node] value to assign if variable is `undefined`",
},
_equals: function(node) {
return this.name.equals(node.name)
&& this.value.equals(node.value);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1347,6 +1493,11 @@ var AST_Call = DEFNODE("Call", "args expression optional pure terminal", {
pure: "[boolean/S] marker for side-effect-free call expression",
terminal: "[boolean] whether the chain has ended",
},
_equals: function(node) {
return !this.optional == !node.optional
&& this.expression.equals(node.expression)
&& all_equals(this.args, node.args);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1375,6 +1526,9 @@ var AST_Sequence = DEFNODE("Sequence", "expressions", {
$propdoc: {
expressions: "[AST_Node*] array of expressions (at least two)"
},
_equals: function(node) {
return all_equals(this.expressions, node.expressions);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1402,6 +1556,11 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression optional property termina
property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node",
terminal: "[boolean] whether the chain has ended",
},
_equals: function(node) {
return !this.optional == !node.optional
&& prop_equals(this.property, node.property)
&& this.expression.equals(node.expression);
},
get_property: function() {
var p = this.property;
if (p instanceof AST_Constant) return p.value;
@@ -1446,6 +1605,9 @@ var AST_Spread = DEFNODE("Spread", "expression", {
$propdoc: {
expression: "[AST_Node] expression to be expanded",
},
_equals: function(node) {
return this.expression.equals(node.expression);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1463,6 +1625,10 @@ var AST_Unary = DEFNODE("Unary", "operator expression", {
operator: "[string] the operator",
expression: "[AST_Node] expression that this unary operator applies to"
},
_equals: function(node) {
return this.operator == node.operator
&& this.expression.equals(node.expression);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1491,6 +1657,11 @@ var AST_Binary = DEFNODE("Binary", "operator left right", {
operator: "[string] the operator",
right: "[AST_Node] right-hand side expression"
},
_equals: function(node) {
return this.operator == node.operator
&& this.left.equals(node.left)
&& this.right.equals(node.right);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1512,6 +1683,11 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative",
consequent: "[AST_Node]",
alternative: "[AST_Node]"
},
_equals: function(node) {
return this.condition.equals(node.condition)
&& this.consequent.equals(node.consequent)
&& this.alternative.equals(node.alternative);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1553,6 +1729,9 @@ var AST_Await = DEFNODE("Await", "expression", {
$propdoc: {
expression: "[AST_Node] expression with Promise to resolve on",
},
_equals: function(node) {
return this.expression.equals(node.expression);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1570,6 +1749,10 @@ var AST_Yield = DEFNODE("Yield", "expression nested", {
expression: "[AST_Node?] return value for iterator, or null if undefined",
nested: "[boolean] whether to iterate over expression as generator",
},
_equals: function(node) {
return !this.nested == !node.nested
&& prop_equals(this.expression, node.expression);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1592,6 +1775,9 @@ var AST_Array = DEFNODE("Array", "elements", {
$propdoc: {
elements: "[AST_Node*] array of elements"
},
_equals: function(node) {
return all_equals(this.elements, node.elements);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1634,6 +1820,10 @@ var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
$propdoc: {
elements: "[(AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)*] array of elements",
},
_equals: function(node) {
return prop_equals(this.rest, node.rest)
&& all_equals(this.elements, node.elements);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1651,6 +1841,10 @@ var AST_DestructuredKeyVal = DEFNODE("DestructuredKeyVal", "key value", {
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
value: "[AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef] property value",
},
_equals: function(node) {
return prop_equals(this.key, node.key)
&& this.value.equals(node.value);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1672,6 +1866,10 @@ var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", {
$propdoc: {
properties: "[AST_DestructuredKeyVal*] array of properties",
},
_equals: function(node) {
return prop_equals(this.rest, node.rest)
&& all_equals(this.properties, node.properties);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1693,6 +1891,9 @@ var AST_Object = DEFNODE("Object", "properties", {
$propdoc: {
properties: "[(AST_ObjectProperty|AST_Spread)*] array of properties"
},
_equals: function(node) {
return all_equals(this.properties, node.properties);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1716,6 +1917,10 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.",
},
_equals: function(node) {
return prop_equals(this.key, node.key)
&& this.value.equals(node.value);
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
@@ -1770,6 +1975,9 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
thedef: "[SymbolDef/S] the definition of this symbol"
},
_equals: function(node) {
return this.thedef ? this.thedef === node.thedef : this.name == node.name;
},
_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");
@@ -1787,10 +1995,14 @@ var AST_SymbolConst = DEFNODE("SymbolConst", null, {
var AST_SymbolImport = DEFNODE("SymbolImport", "key", {
$documentation: "Symbol defined by an `import` statement",
$propdoc: {
key: "[string] the original `export` name",
key: "[AST_String] the original `export` name",
},
_equals: function(node) {
return this.name == node.name
&& this.key.equals(node.key);
},
_validate: function() {
if (typeof this.key != "string") throw new Error("key must be string");
if (!(this.key instanceof AST_String)) throw new Error("key must be AST_String");
},
}, AST_SymbolConst);
@@ -1834,7 +2046,7 @@ var AST_Label = DEFNODE("Label", "references", {
initialize: function() {
this.references = [];
this.thedef = this;
}
},
}, AST_Symbol);
var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", {
@@ -1844,10 +2056,14 @@ var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", {
var AST_SymbolExport = DEFNODE("SymbolExport", "alias", {
$documentation: "Reference in an `export` statement",
$propdoc: {
alias: "[string] the `export` alias",
alias: "[AST_String] the `export` alias",
},
_equals: function(node) {
return this.name == node.name
&& this.alias.equals(node.alias);
},
_validate: function() {
if (typeof this.alias != "string") throw new Error("alias must be string");
if (!(this.alias instanceof AST_String)) throw new Error("alias must be AST_String");
},
}, AST_SymbolRef);
@@ -1857,6 +2073,7 @@ var AST_LabelRef = DEFNODE("LabelRef", null, {
var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, {
$documentation: "Base class for `super` & `this`",
_equals: return_true,
_validate: function() {
if (this.TYPE == "ObjectIdentity") throw new Error("should not instantiate AST_ObjectIdentity");
},
@@ -1891,7 +2108,12 @@ var AST_Template = DEFNODE("Template", "expressions strings tag", {
$propdoc: {
expressions: "[AST_Node*] the placeholder expressions",
strings: "[string*] the raw text segments",
tag: "[AST_Node] tag function, or null if absent",
tag: "[AST_Node?] tag function, or null if absent",
},
_equals: function(node) {
return prop_equals(this.tag, node.tag)
&& list_equals(this.strings, node.strings)
&& all_equals(this.expressions, node.expressions);
},
walk: function(visitor) {
var node = this;
@@ -1916,6 +2138,9 @@ var AST_Template = DEFNODE("Template", "expressions strings tag", {
var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants",
_equals: function(node) {
return this.value === node.value;
},
_validate: function() {
if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant");
},
@@ -1964,6 +2189,9 @@ var AST_RegExp = DEFNODE("RegExp", "value", {
$propdoc: {
value: "[RegExp] the actual regexp"
},
_equals: function(node) {
return "" + this.value == "" + node.value;
},
_validate: function() {
if (!(this.value instanceof RegExp)) throw new Error("value must be RegExp");
},
@@ -1971,6 +2199,7 @@ var AST_RegExp = DEFNODE("RegExp", "value", {
var AST_Atom = DEFNODE("Atom", null, {
$documentation: "Base class for atoms",
_equals: return_true,
_validate: function() {
if (this.TYPE == "Atom") throw new Error("should not instantiate AST_Atom");
},
@@ -2036,16 +2265,21 @@ TreeWalker.prototype = {
return this.stack[this.stack.length - 2 - (n || 0)];
},
push: function(node) {
if (node instanceof AST_Lambda) {
var value;
if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
value = "use strict";
} else if (node instanceof AST_Directive) {
value = node.value;
} else if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node;
}
if (value && !this.directives[value]) this.directives[value] = node;
this.stack.push(node);
},
pop: function() {
var node = this.stack.pop();
if (node instanceof AST_Lambda) {
if (node instanceof AST_Class || node instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives);
}
},

File diff suppressed because it is too large Load Diff

View File

@@ -81,13 +81,14 @@ function minify(files, options) {
keep_fargs: false,
keep_fnames: false,
mangle: {},
module: false,
nameCache: null,
output: {},
parse: {},
rename: undefined,
sourceMap: false,
timings: false,
toplevel: false,
toplevel: !!(options && options["module"]),
v8: false,
validate: false,
warnings: false,
@@ -96,15 +97,15 @@ function minify(files, options) {
}, true);
if (options.validate) AST_Node.enable_validation();
var timings = options.timings && { start: Date.now() };
if (options.rename === undefined) options.rename = options.compress && options.mangle;
if (options.annotations !== undefined) set_shorthand("annotations", options, [ "compress", "output" ]);
if (options.ie8) options.ie = options.ie || options.ie8;
if (options.ie) set_shorthand("ie", options, [ "compress", "mangle", "output" ]);
if (options.keep_fargs) set_shorthand("keep_fargs", options, [ "compress", "mangle" ]);
if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle" ]);
if (options.v8) set_shorthand("v8", options, [ "mangle", "output" ]);
if (options.webkit) set_shorthand("webkit", options, [ "compress", "mangle", "output" ]);
if (options.ie) set_shorthand("ie", options, [ "compress", "mangle", "output", "rename" ]);
if (options.keep_fargs) set_shorthand("keep_fargs", options, [ "compress", "mangle", "rename" ]);
if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle", "rename" ]);
if (options.module) set_shorthand("module", options, [ "compress", "parse" ]);
if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle", "rename" ]);
if (options.v8) set_shorthand("v8", options, [ "mangle", "output", "rename" ]);
if (options.webkit) set_shorthand("webkit", options, [ "compress", "mangle", "output", "rename" ]);
var quoted_props;
if (options.mangle) {
options.mangle = defaults(options.mangle, {
@@ -135,6 +136,7 @@ function minify(files, options) {
init_cache(options.mangle.cache);
init_cache(options.mangle.properties.cache);
}
if (options.rename === undefined) options.rename = options.compress && options.mangle;
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
content: null,
@@ -190,8 +192,8 @@ function minify(files, options) {
if (options.validate) toplevel.validate_ast();
if (timings) timings.rename = Date.now();
if (options.rename) {
toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle);
toplevel.figure_out_scope(options.rename);
toplevel.expand_names(options.rename);
}
if (timings) timings.compress = Date.now();
if (options.compress) {

View File

@@ -192,6 +192,19 @@
value: from_moz(M.value),
});
},
StaticBlock: function(M) {
var start = my_start_token(M);
var end = my_end_token(M);
return new AST_ClassInit({
start: start,
end: end,
value: new AST_ClassInitBlock({
start: start,
end: end,
body: normalize_directives(M.body.map(from_moz)),
}),
});
},
ForOfStatement: function(M) {
return new (M.await ? AST_ForAwaitOf : AST_ForOf)({
start: my_start_token(M),
@@ -303,13 +316,22 @@
});
},
ExportAllDeclaration: function(M) {
var alias = M.exported ? read_name(M.exported) : "*";
var start = my_start_token(M);
var end = my_end_token(M);
return new AST_ExportForeign({
start: my_start_token(M),
end: my_end_token(M),
aliases: [ alias ],
keys: [ "*" ],
path: M.source.value,
start: start,
end: end,
aliases: [ M.exported ? from_moz_alias(M.exported) : new AST_String({
start: start,
value: "*",
end: end,
}) ],
keys: [ new AST_String({
start: start,
value: "*",
end: end,
}) ],
path: from_moz(M.source),
});
},
ExportDefaultDeclaration: function(M) {
@@ -346,15 +368,15 @@
if (M.source) {
var aliases = [], keys = [];
M.specifiers.forEach(function(prop) {
aliases.push(read_name(prop.exported));
keys.push(read_name(prop.local));
aliases.push(from_moz_alias(prop.exported));
keys.push(from_moz_alias(prop.local));
});
return new AST_ExportForeign({
start: my_start_token(M),
end: my_end_token(M),
aliases: aliases,
keys: keys,
path: M.source.value,
path: from_moz(M.source),
});
}
return new AST_ExportReferences({
@@ -362,38 +384,48 @@
end: my_end_token(M),
properties: M.specifiers.map(function(prop) {
var sym = new AST_SymbolExport(from_moz(prop.local));
sym.alias = read_name(prop.exported);
sym.alias = from_moz_alias(prop.exported);
return sym;
}),
});
},
ImportDeclaration: function(M) {
var start = my_start_token(M);
var end = my_end_token(M);
var all = null, def = null, props = null;
M.specifiers.forEach(function(prop) {
var sym = new AST_SymbolImport(from_moz(prop.local));
switch (prop.type) {
case "ImportDefaultSpecifier":
def = sym;
def.key = "";
def.key = new AST_String({
start: start,
value: "",
end: end,
});
break;
case "ImportNamespaceSpecifier":
all = sym;
all.key = "*";
all.key = new AST_String({
start: start,
value: "*",
end: end,
});
break;
default:
sym.key = prop.imported.name || syn.name;
sym.key = from_moz_alias(prop.imported);
if (!props) props = [];
props.push(sym);
break;
}
});
return new AST_Import({
start: my_start_token(M),
end: my_end_token(M),
start: start,
end: end,
all: all,
default: def,
properties: props,
path: M.source.value,
path: from_moz(M.source),
});
},
ImportExpression: function(M) {
@@ -714,6 +746,10 @@
};
});
def_to_moz(AST_ClassInit, function To_Moz_StaticBlock(M) {
return to_moz_scope("StaticBlock", M.value);
});
function To_Moz_ForOfStatement(is_await) {
return function(M) {
return {
@@ -780,38 +816,26 @@
});
def_to_moz(AST_ExportForeign, function To_Moz_ExportAllDeclaration_ExportNamedDeclaration(M) {
if (M.keys[0] == "*") return {
if (M.keys[0].value == "*") return {
type: "ExportAllDeclaration",
exported: M.aliases[0] == "*" ? null : {
type: "Identifier",
name: M.aliases[0],
},
source: {
type: "Literal",
value: M.path,
},
exported: M.aliases[0].value == "*" ? null : to_moz_alias(M.aliases[0]),
source: to_moz(M.path),
};
var specifiers = [];
for (var i = 0; i < M.aliases.length; i++) {
specifiers.push({
specifiers.push(set_moz_loc({
start: M.keys[i].start,
end: M.aliases[i].end,
}, {
type: "ExportSpecifier",
exported: {
type: "Identifier",
name: M.aliases[i],
},
local: {
type: "Identifier",
name: M.keys[i],
},
});
local: to_moz_alias(M.keys[i]),
exported: to_moz_alias(M.aliases[i]),
}));
}
return {
type: "ExportNamedDeclaration",
specifiers: specifiers,
source: {
type: "Literal",
value: M.path,
},
source: to_moz(M.path),
};
});
@@ -819,44 +843,41 @@
return {
type: "ExportNamedDeclaration",
specifiers: M.properties.map(function(prop) {
return {
return set_moz_loc({
start: prop.start,
end: prop.alias.end,
}, {
type: "ExportSpecifier",
local: to_moz(prop),
exported: {
type: "Identifier",
name: prop.alias,
},
};
exported: to_moz_alias(prop.alias),
});
}),
};
});
def_to_moz(AST_Import, function To_Moz_ImportDeclaration(M) {
var specifiers = M.properties ? M.properties.map(function(prop) {
return {
return set_moz_loc({
start: prop.key.start,
end: prop.end,
}, {
type: "ImportSpecifier",
local: to_moz(prop),
imported: {
type: "Identifier",
name: prop.key,
},
};
imported: to_moz_alias(prop.key),
});
}) : [];
if (M.all) specifiers.unshift({
if (M.all) specifiers.unshift(set_moz_loc(M.all, {
type: "ImportNamespaceSpecifier",
local: to_moz(M.all),
});
if (M.default) specifiers.unshift({
}));
if (M.default) specifiers.unshift(set_moz_loc(M.default, {
type: "ImportDefaultSpecifier",
local: to_moz(M.default),
});
}));
return {
type: "ImportDeclaration",
specifiers: specifiers,
source: {
type: "Literal",
value: M.path,
},
source: to_moz(M.path),
};
});
@@ -1203,6 +1224,14 @@
return node;
}
function from_moz_alias(moz) {
return new AST_String({
start: my_start_token(moz),
value: read_name(moz),
end: my_end_token(moz),
});
}
AST_Node.from_mozilla_ast = function(node) {
var save_stack = FROM_MOZ_STACK;
FROM_MOZ_STACK = [];
@@ -1254,6 +1283,13 @@
return node != null ? node.to_mozilla_ast() : null;
}
function to_moz_alias(alias) {
return is_identifier_string(alias.value) ? set_moz_loc(alias, {
type: "Identifier",
name: alias.value,
}) : to_moz(alias);
}
function to_moz_block(node) {
return {
type: "BlockStatement",

View File

@@ -260,6 +260,15 @@ function OutputStream(options) {
var require_semicolon = makePredicate("( [ + * / - , .");
function require_space(prev, ch, str) {
return is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| last == "--" && ch == ">"
|| last == "!" && str == "--"
|| prev == "/" && (str == "in" || str == "instanceof");
}
var print = options.beautify
|| options.comments
|| options.max_line_len
@@ -312,12 +321,7 @@ function OutputStream(options) {
}
if (might_need_space) {
if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") {
if (require_space(prev, ch, str)) {
output += " ";
current_col++;
}
@@ -355,14 +359,7 @@ function OutputStream(options) {
}
}
if (might_need_space) {
if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") {
output += " ";
}
if (require_space(prev, ch, str)) output += " ";
if (prev != "<" || str != "!") might_need_space = false;
}
output += str;
@@ -1064,6 +1061,14 @@ function OutputStream(options) {
}
output.semicolon();
});
function print_alias(alias, output) {
var value = alias.value;
if (value == "*" || is_identifier_string(value)) {
output.print_name(value);
} else {
output.print_string(value, alias.quote);
}
}
DEFPRINT(AST_ExportForeign, function(output) {
var self = this;
output.print("export");
@@ -1071,7 +1076,7 @@ function OutputStream(options) {
var len = self.keys.length;
if (len == 0) {
print_braced_empty(self, output);
} else if (self.keys[0] == "*") {
} else if (self.keys[0].value == "*") {
print_entry(0);
} else output.with_block(function() {
output.indent();
@@ -1087,18 +1092,18 @@ function OutputStream(options) {
output.space();
output.print("from");
output.space();
output.print_string(self.path, self.quote);
self.path.print(output);
output.semicolon();
function print_entry(index) {
var alias = self.aliases[index];
var key = self.keys[index];
output.print_name(key);
if (alias != key) {
print_alias(key, output);
if (alias.value != key.value) {
output.space();
output.print("as");
output.space();
output.print_name(alias);
print_alias(alias, output);
}
}
});
@@ -1127,7 +1132,7 @@ function OutputStream(options) {
output.print("from");
output.space();
}
output.print_string(self.path, self.quote);
self.path.print(output);
output.semicolon();
});
@@ -1257,6 +1262,11 @@ function OutputStream(options) {
}
print_method(self, output);
});
DEFPRINT(AST_ClassInit, function(output) {
output.print("static");
output.space();
print_braced(this.value, output);
});
/* -----[ jumps ]----- */
function print_jump(kind, prop) {
@@ -1732,19 +1742,19 @@ function OutputStream(options) {
var name = get_symbol_name(self);
output.print_name(name);
var alias = self.alias;
if (alias != name) {
if (alias.value != name) {
output.space();
output.print("as");
output.space();
output.print_name(alias);
print_alias(alias, output);
}
});
DEFPRINT(AST_SymbolImport, function(output) {
var self = this;
var name = get_symbol_name(self);
var key = self.key;
if (key && key != name) {
output.print_name(key);
if (key.value && key.value != name) {
print_alias(key, output);
output.space();
output.print("as");
output.space();
@@ -1814,9 +1824,6 @@ function OutputStream(options) {
case "\u2029": return "\\u2029";
}
}));
var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === this)
output.print(" ");
});
function force_statement(stat, output) {

View File

@@ -237,8 +237,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
newline_before : false,
regex_allowed : false,
comments_before : [],
directives : {},
directive_stack : [],
directives : Object.create(null),
read_template : with_eof_error("Unterminated template literal", function(strings) {
var s = "";
for (;;) {
@@ -553,16 +552,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function handle_dot() {
next();
var ch = peek();
if (ch == ".") {
var op = ".";
do {
op += ".";
next();
} while (peek() == ".");
return token("operator", op);
}
return is_digit(ch.charCodeAt(0)) ? read_num(".") : token("punc", ".");
if (looking_at("..")) return token("operator", "." + next() + next());
return is_digit(peek().charCodeAt(0)) ? read_num(".") : token("punc", ".");
}
function read_word() {
@@ -635,24 +626,19 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
};
next_token.add_directive = function(directive) {
S.directive_stack[S.directive_stack.length - 1].push(directive);
if (S.directives[directive]) S.directives[directive]++;
else S.directives[directive] = 1;
S.directives[directive] = true;
}
next_token.push_directives_stack = function() {
S.directive_stack.push([]);
S.directives = Object.create(S.directives);
}
next_token.pop_directives_stack = function() {
var directives = S.directive_stack.pop();
for (var i = directives.length; --i >= 0;) {
S.directives[directives[i]]--;
}
S.directives = Object.getPrototypeOf(S.directives);
}
next_token.has_directive = function(directive) {
return S.directives[directive] > 0;
return !!S.directives[directive];
}
return next_token;
@@ -699,6 +685,7 @@ function parse($TEXT, options) {
expression : false,
filename : null,
html5_comments : true,
module : false,
shebang : true,
strict : false,
toplevel : null,
@@ -820,7 +807,7 @@ function parse($TEXT, options) {
}
}
var statement = embed_tokens(function() {
var statement = embed_tokens(function(toplevel) {
handle_regexp();
switch (S.token.type) {
case "string":
@@ -859,15 +846,15 @@ function parse($TEXT, options) {
if (S.in_async) return simple_statement();
break;
case "export":
if (!toplevel && options.module !== "") unexpected();
next();
return export_();
case "import":
var token = peek();
if (!(token.type == "punc" && /^[(.]$/.test(token.value))) {
next();
return import_();
}
break;
if (token.type == "punc" && /^[(.]$/.test(token.value)) break;
if (!toplevel && options.module !== "") unexpected();
next();
return import_();
case "let":
if (is_vardefs()) {
next();
@@ -1124,6 +1111,18 @@ function parse($TEXT, options) {
}));
continue;
}
if (fixed && is("punc", "{")) {
props.push(new AST_ClassInit({
start: start,
value: new AST_ClassInitBlock({
start: start,
body: block_(),
end: prev(),
}),
end: prev(),
}));
continue;
}
var internal = is("name") && /^#/.test(S.token.value);
var key = as_property_key();
if (is("punc", "(")) {
@@ -1194,10 +1193,10 @@ function parse($TEXT, options) {
}
function for_() {
var await = is("name", "await") && next();
var await_token = is("name", "await") && next();
expect("(");
var init = null;
if (await || !is("punc", ";")) {
if (await_token || !is("punc", ";")) {
init = is("keyword", "const")
? (next(), const_(true))
: is("name", "let") && is_vardefs()
@@ -1206,7 +1205,7 @@ function parse($TEXT, options) {
? (next(), var_(true))
: expression(true);
var ctor;
if (await) {
if (await_token) {
expect_token("name", "of");
ctor = AST_ForAwaitOf;
} else if (is("operator", "in")) {
@@ -1340,11 +1339,11 @@ function parse($TEXT, options) {
var loop = S.in_loop;
var labels = S.labels;
++S.in_function;
S.in_directives = true;
S.input.push_directives_stack();
S.in_loop = 0;
S.labels = [];
if (is("punc", "{")) {
S.in_directives = true;
body = block_();
value = null;
} else {
@@ -1412,7 +1411,7 @@ function parse($TEXT, options) {
name: name,
argnames: argnames,
rest: argnames.rest || null,
body: body
body: body,
});
if (is_strict) {
if (name) strict_verify_symbol(name);
@@ -1435,28 +1434,41 @@ function parse($TEXT, options) {
}
function is_alias() {
return is("name") || is_identifier_string(S.token.value);
return is("name") || is("string") || is_identifier_string(S.token.value);
}
function make_string(token) {
return new AST_String({
start: token,
quote: token.quote,
value: token.value,
end: token,
});
}
function as_path() {
var path = S.token;
expect_token("string");
semicolon();
return make_string(path);
}
function export_() {
if (is("operator", "*")) {
var key = S.token;
var alias = key;
next();
var alias = "*";
if (is("name", "as")) {
next();
if (!is_alias()) expect_token("name");
alias = S.token.value;
alias = S.token;
next();
}
expect_token("name", "from");
var path = S.token;
expect_token("string");
semicolon();
return new AST_ExportForeign({
aliases: [ alias ],
keys: [ "*" ],
path: path.value,
quote: path.quote,
aliases: [ make_string(alias) ],
keys: [ make_string(key) ],
path: as_path(),
});
}
if (is("punc", "{")) {
@@ -1470,26 +1482,20 @@ function parse($TEXT, options) {
if (is("name", "as")) {
next();
if (!is_alias()) expect_token("name");
aliases.push(S.token.value);
aliases.push(S.token);
next();
} else {
aliases.push(key.value);
aliases.push(key);
}
if (!is("punc", "}")) expect(",");
}
expect("}");
if (is("name", "from")) {
next();
var path = S.token;
expect_token("string");
semicolon();
return new AST_ExportForeign({
aliases: aliases,
keys: keys.map(function(token) {
return token.value;
}),
path: path.value,
quote: path.quote,
aliases: aliases.map(make_string),
keys: keys.map(make_string),
path: as_path(),
});
}
semicolon();
@@ -1497,7 +1503,7 @@ function parse($TEXT, options) {
properties: keys.map(function(token, index) {
if (!is_token(token, "name")) token_error(token, "Name expected");
var sym = _make_symbol(AST_SymbolExport, token);
sym.alias = aliases[index];
sym.alias = make_string(aliases[index]);
return sym;
}),
});
@@ -1587,26 +1593,42 @@ function parse($TEXT, options) {
var all = null;
var def = as_symbol(AST_SymbolImport, true);
var props = null;
if (def ? (def.key = "", is("punc", ",") && next()) : !is("string")) {
var cont;
if (def) {
def.key = new AST_String({
start: def.start,
value: "",
end: def.end,
});
if (cont = is("punc", ",")) next();
} else {
cont = !is("string");
}
if (cont) {
if (is("operator", "*")) {
var key = S.token;
next();
expect_token("name", "as");
all = as_symbol(AST_SymbolImport);
all.key = "*";
all.key = make_string(key);
} else {
expect("{");
props = [];
while (is_alias()) {
var alias;
if (is_token(peek(), "name", "as")) {
var key = S.token.value;
var key = S.token;
next();
next();
alias = as_symbol(AST_SymbolImport);
alias.key = key;
alias.key = make_string(key);
} else {
alias = as_symbol(AST_SymbolImport);
alias.key = alias.name;
alias.key = new AST_String({
start: alias.start,
value: alias.name,
end: alias.end,
});
}
props.push(alias);
if (!is("punc", "}")) expect(",");
@@ -1615,15 +1637,11 @@ function parse($TEXT, options) {
}
}
if (all || def || props) expect_token("name", "from");
var path = S.token;
expect_token("string");
semicolon();
return new AST_Import({
all: all,
default: def,
path: path.value,
path: as_path(),
properties: props,
quote: path.quote,
});
}
@@ -1801,7 +1819,7 @@ function parse($TEXT, options) {
ret = new AST_BigInt({ value: value });
break;
case "string":
ret = new AST_String({ value : value, quote : tok.quote });
ret = new AST_String({ value: value, quote: tok.quote });
break;
case "regexp":
ret = new AST_RegExp({ value: value });
@@ -2550,9 +2568,13 @@ function parse($TEXT, options) {
return function() {
var start = S.token;
var body = [];
if (options.module) {
S.in_async = true;
S.input.add_directive("use strict");
}
S.input.push_directives_stack();
while (!is("eof"))
body.push(statement());
body.push(statement(true));
S.input.pop_directives_stack();
var end = prev() || start;
var toplevel = options.toplevel;

View File

@@ -64,19 +64,20 @@ SymbolDef.prototype = {
this.references.forEach(fn);
},
mangle: function(options) {
var cache = options.cache && options.cache.props;
if (this.global && cache && cache.has(this.name)) {
if (this.mangled_name) return;
var cache = this.global && options.cache && options.cache.props;
if (cache && cache.has(this.name)) {
this.mangled_name = cache.get(this.name);
} else if (!this.mangled_name && !this.unmangleable(options)) {
} else if (this.unmangleable(options)) {
names_in_use(this.scope, options).set(this.name, true);
} else {
var def = this.redefined();
if (def) {
this.mangled_name = def.mangled_name || def.name;
} else {
this.mangled_name = next_mangled_name(this, options);
}
if (this.global && cache) {
cache.set(this.name, this.mangled_name);
}
if (cache) cache.set(this.name, this.mangled_name);
}
},
redefined: function() {
@@ -226,7 +227,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
defun.def_variable(node);
} else if (node instanceof AST_SymbolLambda) {
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
if (options.ie) def.defun = defun.parent_scope.resolve();
if (options.ie && node.name != "arguments") def.defun = defun.parent_scope.resolve();
} else if (node instanceof AST_SymbolLet) {
var def = scope.def_variable(node);
if (exported) def.exported = true;
@@ -469,6 +470,7 @@ AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
this.uses_arguments = false;
this.def_variable(new AST_SymbolFunarg({
name: "arguments",
scope: this,
start: this.start,
end: this.end,
}));
@@ -620,14 +622,10 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
var lname = -1;
var redefined = [];
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_LabeledStatement) {
// `lname` is incremented when we get to the `AST_Label`
var save_nesting = lname;
descend();
if (!options.v8 || !in_label(tw)) lname = save_nesting;
return true;
}
var save_nesting;
if (node instanceof AST_BlockScope) {
// `lname` is incremented when we get to the `AST_Label`
if (node instanceof AST_LabeledStatement) save_nesting = lname;
if (options.webkit && node instanceof AST_IterationStatement && node.init instanceof AST_Let) {
node.init.definitions.forEach(function(defn) {
defn.name.match_symbol(function(sym) {
@@ -673,6 +671,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
}));
}
to_mangle.forEach(mangle);
if (node instanceof AST_LabeledStatement && !(options.v8 && in_label(tw))) lname = save_nesting;
return true;
}
if (node instanceof AST_Label) {

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.15.2",
"version": "3.16.3",
"engines": {
"node": ">=0.8.0"
},
@@ -23,7 +23,7 @@
"LICENSE"
],
"devDependencies": {
"acorn": "~8.2.1",
"acorn": "~8.7.1",
"semver": "~6.3.0"
},
"scripts": {

View File

@@ -69,9 +69,7 @@ function make_code(ast, options) {
function parse_test(file) {
var script = fs.readFileSync(file, "utf8");
try {
var ast = U.parse(script, {
filename: file
});
var ast = U.parse(script, { filename: file, module: "" });
} catch (e) {
console.error("Caught error while parsing tests in " + file);
console.error(e);
@@ -98,14 +96,14 @@ function parse_test(file) {
file: file,
line: node.start.line,
col: node.start.col,
code: make_code(node, { beautify: false })
code: make_code(node, { beautify: false }),
}));
}
function read_string(stat) {
if (stat.TYPE == "SimpleStatement") {
var body = stat.body;
switch(body.TYPE) {
switch (body.TYPE) {
case "String":
return body.value;
case "Array":
@@ -142,7 +140,7 @@ function parse_test(file) {
].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name,
line: label.start.line,
col: label.start.col
col: label.start.col,
}));
var stat = node.body;
if (label.name == "expect_exact" || label.name == "node_version") {
@@ -155,12 +153,12 @@ function parse_test(file) {
var ctor = global[body.expression.name];
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
col: label.start.col,
}));
test[label.name] = ctor.apply(null, body.args.map(function(node) {
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
line: label.start.line,
col: label.start.col
col: label.start.col,
}));
return node.value;
}));
@@ -183,13 +181,11 @@ function parse_test(file) {
function reminify(orig_options, input_code, input_formatted, stdout) {
for (var i = 0; i < minify_options.length; i++) {
var options = JSON.parse(minify_options[i]);
if (options.compress) [
[
"keep_fargs",
"keep_fnames",
].forEach(function(name) {
if (name in orig_options) {
options.compress[name] = orig_options[name];
}
if (name in orig_options) options[name] = orig_options[name];
});
var options_formatted = JSON.stringify(options, null, 4);
options.validate = true;

View File

@@ -1018,3 +1018,197 @@ issue_5356: {
expect_stdout: "NaN"
node_version: ">=4"
}
issue_5414_1: {
options = {
arrows: true,
if_return: true,
inline: true,
toplevel: true,
}
input: {
(() => {
(() => {
if (!console)
var arguments = 42;
while (console.log(arguments));
})();
})();
}
expect: {
(() => {
if (!console)
var arguments = 42;
while (console.log(arguments));
})();
}
expect_stdout: true
node_version: ">=4"
}
issue_5414_2: {
options = {
arrows: true,
inline: true,
side_effects: true,
toplevel: true,
}
input: {
(() => {
(() => {
if (!console)
var arguments = 42;
while (console.log(arguments));
})();
})();
}
expect: {
(() => {
if (!console)
var arguments = 42;
while (console.log(arguments));
})();
}
expect_stdout: true
node_version: ">=4"
}
issue_5416_1: {
options = {
dead_code: true,
evaluate: true,
inline: true,
loops: true,
unused: true,
}
input: {
var f = () => {
while ((() => {
console;
var a = function g(arguments) {
console.log(arguments);
}();
})());
};
f();
}
expect: {
var f = () => {
{
console;
arguments = void 0,
console.log(arguments);
var arguments;
return;
}
};
f();
}
expect_stdout: "undefined"
node_version: ">=4"
}
issue_5416_2: {
options = {
dead_code: true,
evaluate: true,
inline: true,
loops: true,
unused: true,
}
input: {
var f = () => {
while ((() => {
console;
var a = function g(arguments) {
while (console.log(arguments));
}();
})());
};
f();
}
expect: {
var f = () => {
{
console;
var arguments = void 0;
for (; console.log(arguments););
return;
}
};
f();
}
expect_stdout: "undefined"
node_version: ">=4"
}
issue_5416_3: {
options = {
inline: true,
side_effects: true,
unused: true,
}
input: {
var f = () => {
(() => {
var a = function g(arguments) {
console.log(arguments);
}();
})();
};
f();
}
expect: {
var f = () => {
arguments = void 0,
console.log(arguments);
var arguments;
};
f();
}
expect_stdout: "undefined"
node_version: ">=4"
}
issue_5416_4: {
options = {
arrows: true,
inline: true,
side_effects: true,
unused: true,
}
input: {
var f = () => {
(() => {
var a = function g(arguments) {
while (console.log(arguments));
}();
})();
};
f();
}
expect: {
var f = () => {
var arguments = void 0;
while (console.log(arguments));
return;
};
f();
}
expect_stdout: "undefined"
node_version: ">=4"
}
issue_5495: {
input: {
console.log((() => {
"use strict";
return function() {
return this;
}();
})());
}
expect_exact: 'console.log((()=>{"use strict";return function(){return this}()})());'
expect_stdout: "undefined"
node_version: ">=4"
}

View File

@@ -1046,6 +1046,60 @@ collapse_vars_3: {
node_version: ">=8"
}
collapse_funarg_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
A = "FAIL";
var a = "PASS";
(async function({}, b) {
return b;
})(null, A = a);
console.log(A);
}
expect: {
A = "FAIL";
var a = "PASS";
(async function({}, b) {
return b;
})(null, A = a);
console.log(A);
}
expect_stdout: "PASS"
node_version: ">=8"
}
collapse_funarg_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
A = "FAIL";
B = "PASS";
(async function() {
console.log(function({}, a) {
return a;
}(null, A = B));
})();
console.log(A);
}
expect: {
A = "FAIL";
B = "PASS";
(async function() {
console.log(function({}, a) {
return a;
}(null, A = B));
})();
console.log(A);
}
expect_stdout: "PASS"
node_version: ">=8"
}
collapse_property_lambda: {
options = {
collapse_vars: true,
@@ -1293,6 +1347,21 @@ functions_inner_var: {
node_version: ">=8"
}
instanceof_lambda: {
options = {
evaluate: true,
side_effects: true,
}
input: {
console.log(42 instanceof async function() {});
}
expect: {
console.log(false);
}
expect_stdout: "false"
node_version: ">=8"
}
issue_4335_1: {
options = {
inline: true,
@@ -1748,8 +1817,8 @@ issue_4454_2: {
expect: {
function f(a) {
(async function(c = console.log(a)) {})();
var a = 42..toString();
console.log(a);
var b = 42..toString();
console.log(b);
}
f("PASS");
}
@@ -2427,80 +2496,6 @@ issue_5023_2: {
node_version: ">=8"
}
issue_5032_normal: {
options = {
merge_vars: true,
webkit: false,
}
input: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=8"
}
issue_5032_webkit: {
options = {
merge_vars: true,
webkit: true,
}
input: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=8"
}
issue_5034: {
options = {
functions: true,
@@ -2959,3 +2954,278 @@ issue_5305_3: {
expect_stdout: "PASS"
node_version: ">=8"
}
issue_5456: {
options = {
inline: true,
merge_vars: true,
}
input: {
var a = true;
(function() {
(function(b, c) {
var d = async function() {
c = await null;
}();
var e = function() {
if (c)
console.log(typeof d);
while (b);
}();
})(function(i) {
return console.log("foo") && i;
}(a));
})();
}
expect: {
var a = true;
(function() {
b = (i = a, console.log("foo") && i),
d = async function() {
c = await null;
}(),
e = function() {
if (c) console.log(typeof d);
while (b);
}(),
void 0;
var b, c, d, e;
var i;
})();
}
expect_stdout: "foo"
node_version: ">=8"
}
issue_5478: {
options = {
side_effects: true,
}
input: {
A = {
get then() {
a = "FAIL";
},
};
var a = "PASS";
(async function() {
for (var b in "foo")
return void A;
})();
console.log(a);
}
expect: {
A = {
get then() {
a = "FAIL";
},
};
var a = "PASS";
(async function() {
for (var b in "foo")
return !A;
})();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_5493: {
options = {
collapse_vars: true,
reduce_vars: true,
}
input: {
(async function(a) {
var b = await [ 42 || b, a = b ];
console.log(a);
})();
}
expect: {
(async function(a) {
var b = await [ 42 || b, a = b ];
console.log(a);
})();
}
expect_stdout: "undefined"
node_version: ">=8"
}
issue_5506: {
options = {
dead_code: true,
}
input: {
console.log(function(a) {
(async function() {
a = null in (a = "PASS");
})();
return a;
}("FAIL"));
}
expect: {
console.log(function(a) {
(async function() {
a = null in (a = "PASS");
})();
return a;
}("FAIL"));
}
expect_stdout: "PASS"
node_version: ">=8"
}
issue_5528_1: {
options = {
inline: true,
}
input: {
(async function() {
await function() {
try {
return;
} finally {
console.log("foo");
}
}();
})();
console.log("bar");
}
expect: {
(async function() {
await function() {
try {
return;
} finally {
console.log("foo");
}
}();
})();
console.log("bar");
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=8"
}
issue_5528_2: {
options = {
inline: true,
}
input: {
(async function() {
await function() {
try {
return 42;
} finally {
console.log("foo");
}
}();
})();
console.log("bar");
}
expect: {
(async function() {
await function() {
try {
return 42;
} finally {
console.log("foo");
}
}();
})();
console.log("bar");
}
expect_stdout: [
"foo",
"bar",
]
node_version: ">=8"
}
issue_5528_3: {
options = {
inline: true,
}
input: {
(async function() {
await function() {
try {
FAIL;
} catch (e) {
return console.log("foo");
} finally {
console.log("bar");
}
}();
})();
console.log("baz");
}
expect: {
(async function() {
await function() {
try {
FAIL;
} catch (e) {
return console.log("foo");
} finally {
console.log("bar");
}
}();
})();
console.log("baz");
}
expect_stdout: [
"foo",
"bar",
"baz",
]
node_version: ">=8"
}
issue_5528_4: {
options = {
inline: true,
}
input: {
(async function() {
await function() {
try {
return {
then() {
console.log("foo");
},
};
} finally {
console.log("bar");
}
}();
})();
console.log("baz");
}
expect: {
(async function() {
await function() {
try {
return {
then() {
console.log("foo");
},
};
} finally {
console.log("bar");
}
}();
})();
console.log("baz");
}
expect_stdout: [
"bar",
"baz",
"foo",
]
node_version: ">=8"
}

View File

@@ -762,3 +762,27 @@ issue_5228: {
}
expect_stdout: "true"
}
issue_5469: {
options = {
assignments: true,
booleans: true,
conditionals: true,
dead_code: true,
evaluate: true,
pure_getters: "strict",
side_effects: true,
}
input: {
console.log(function f(a) {
a && 42[a = A && null];
}());
}
expect: {
console.log(function f(a) {
a && A,
0;
}());
}
expect_stdout: "undefined"
}

File diff suppressed because it is too large Load Diff

View File

@@ -9925,3 +9925,79 @@ issue_5309_2: {
}
expect_stdout: "PASS"
}
issue_5394: {
options = {
collapse_vars: true,
evaluate: true,
}
input: {
try {
throw A.p = (console.log("FAIL"), []), !1;
} catch (e) {
console.log(typeof e);
}
}
expect: {
try {
throw !(A.p = (console.log("FAIL"), []));
} catch (e) {
console.log(typeof e);
}
}
expect_stdout: "object"
}
issue_5396: {
options = {
collapse_vars: true,
merge_vars: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
var a, b;
function f() {}
b = 0;
new function g(c) {
var d = a && g(e), e = ++d, i = [ 42 ];
for (var j in i)
console.log("PASS"),
i;
}();
}
expect: {
var a, b;
function f() {}
b = 0;
(function g(c) {
a && g();
for (var j in [ 42 ])
console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_5568: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
A = "FAIL";
var a = (A = "PASS", !1);
for (var b in a);
console.log(A);
}
expect: {
A = "FAIL";
for (var b in !(A = "PASS"));
console.log(A);
}
expect_stdout: "PASS"
}

View File

@@ -40,6 +40,22 @@ unsafe_comps: {
}
}
unsafe_in_instanceof: {
options = {
side_effects: true,
unsafe_comps: true,
}
input: {
var a;
42 in a;
f() instanceof "foo";
}
expect: {
var a;
f();
}
}
dont_change_in_or_instanceof_expressions: {
input: {
1 in 1;

View File

@@ -82,14 +82,14 @@ ifs_3_should_warn: {
"WARN: Boolean && always false [test/compress/conditionals.js:3,12]",
"WARN: Condition left of && always false [test/compress/conditionals.js:3,12]",
"WARN: Condition always false [test/compress/conditionals.js:3,12]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:3,34]",
"WARN: + in boolean context always true [test/compress/conditionals.js:10,19]",
"WARN: Boolean || always true [test/compress/conditionals.js:10,12]",
"WARN: Condition left of || always true [test/compress/conditionals.js:10,12]",
"WARN: Condition always true [test/compress/conditionals.js:10,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:12,15]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:10,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:12,15]",
]
}
@@ -196,6 +196,88 @@ ifs_7: {
}
}
merge_tail_1: {
options = {
conditionals: true,
}
input: {
function f(a) {
var b = "foo";
if (a) {
while (console.log("bar"));
console.log(b);
} else {
while (console.log("baz"));
console.log(b);
}
}
f();
f(42);
}
expect: {
function f(a) {
var b = "foo";
if (a)
while (console.log("bar"));
else
while (console.log("baz"));
console.log(b);
}
f();
f(42);
}
expect_stdout: [
"baz",
"foo",
"bar",
"foo",
]
}
merge_tail_2: {
options = {
conditionals: true,
}
input: {
function f(a) {
var b = "foo";
if (a) {
while (console.log("bar"));
console.log(b);
} else {
c = "baz";
while (console.log(c));
while (console.log("bar"));
console.log(b);
var c;
}
}
f();
f(42);
}
expect: {
function f(a) {
var b = "foo";
if (!a) {
c = "baz";
while (console.log(c));
var c;
}
while (console.log("bar"));
console.log(b);
}
f();
f(42);
}
expect_stdout: [
"baz",
"bar",
"foo",
"bar",
"foo",
]
}
cond_1: {
options = {
conditionals: true,
@@ -1731,7 +1813,7 @@ issue_3576: {
expect_stdout: "PASS"
}
issue_3668: {
issue_3668_1: {
options = {
conditionals: true,
if_return: true,
@@ -1748,6 +1830,38 @@ issue_3668: {
}
console.log(f());
}
expect: {
function f() {
try {
var undefined = typeof f;
if (!f) return undefined;
} catch (e) {
return "FAIL";
}
}
console.log(f());
}
expect_stdout: "undefined"
}
issue_3668_2: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f() {
try {
var undefined = typeof f;
if (!f) return undefined;
return;
} catch (e) {
return "FAIL";
}
FAIL;
}
console.log(f());
}
expect: {
function f() {
try {
@@ -1756,6 +1870,7 @@ issue_3668: {
} catch (e) {
return "FAIL";
}
FAIL;
}
console.log(f());
}
@@ -2062,3 +2177,176 @@ issue_5334_2: {
}
expect_stdout: "PASS"
}
issue_5544_1: {
options = {
conditionals: true,
}
input: {
var a;
if (a)
switch (42) {
case console.log("FAIL"):
case console:
}
else
switch (false) {
case console.log("PASS"):
case console:
}
}
expect: {
var a;
if (a)
switch (42) {
case console.log("FAIL"):
case console:
}
else
switch (false) {
case console.log("PASS"):
case console:
}
}
expect_stdout: "PASS"
}
issue_5544_2: {
options = {
conditionals: true,
}
input: {
var a;
if (a)
switch (42) {
case console.log("FAIL"):
case console:
}
else
switch (42) {
case console.log("PASS"):
case console:
}
}
expect: {
var a;
if (a)
switch (42) {
case console.log("FAIL"):
case console:
}
else
switch (42) {
case console.log("PASS"):
case console:
}
}
expect_stdout: "PASS"
}
issue_5546_1: {
options = {
conditionals: true,
}
input: {
var a;
if (a)
try {
console;
} finally {
console.log("FAIL");
}
else
try {
console;
} finally {
console.log("PASS");
}
}
expect: {
var a;
if (a)
try {
console;
} finally {
console.log("FAIL");
}
else
try {
console;
} finally {
console.log("PASS");
}
}
expect_stdout: "PASS"
}
issue_5546_2: {
options = {
conditionals: true,
}
input: {
var a;
if (a)
try {
console;
} catch (e) {}
else
try {
console;
} finally {
console.log("PASS");
}
}
expect: {
var a;
if (a)
try {
console;
} catch (e) {}
else
try {
console;
} finally {
console.log("PASS");
}
}
expect_stdout: "PASS"
}
issue_5546_3: {
options = {
conditionals: true,
}
input: {
var a;
if (a)
try {
FAIL;
} catch (e) {
console.log("FAIL");
}
else
try {
FAIL;
} catch (e) {
console.log("PASS");
}
}
expect: {
var a;
if (a)
try {
FAIL;
} catch (e) {
console.log("FAIL");
}
else
try {
FAIL;
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -142,6 +142,80 @@ if_dead_branch: {
expect_stdout: "undefined"
}
retain_tail_1: {
options = {
conditionals: true,
}
input: {
function f(a) {
var b = "foo";
if (a) {
const b = "bar";
while (console.log("baz"));
console.log(b);
} else {
while (console.log("moo"));
console.log(b);
}
}
f();
f(42);
}
expect: {
function f(a) {
var b = "foo";
if (a) {
const b = "bar";
while (console.log("baz"));
console.log(b);
} else {
while (console.log("moo"));
console.log(b);
}
}
f();
f(42);
}
expect_stdout: true
}
retain_tail_2: {
options = {
conditionals: true,
}
input: {
function f(a) {
var b = "foo";
if (a) {
while (console.log("bar"));
console.log(b);
} else {
const b = "baz";
while (console.log("moo"));
console.log(b);
}
}
f();
f(42);
}
expect: {
function f(a) {
var b = "foo";
if (a) {
while (console.log("bar"));
console.log(b);
} else {
const b = "baz";
while (console.log("moo"));
console.log(b);
}
}
f();
f(42);
}
expect_stdout: true
}
merge_vars_1: {
options = {
merge_vars: true,
@@ -579,6 +653,37 @@ dead_block_after_return: {
expect_stdout: true
}
if_return_3: {
options = {
if_return: true,
}
input: {
var a = "PASS";
function f(b) {
if (console) {
const b = a;
return b;
} else
while (console.log("FAIL 1"));
return b;
}
console.log(f("FAIL 2"));
}
expect: {
var a = "PASS";
function f(b) {
if (console) {
const b = a;
return b;
} else
while (console.log("FAIL 1"));
return b;
}
console.log(f("FAIL 2"));
}
expect_stdout: true
}
do_if_continue_1: {
options = {
if_return: true,
@@ -1855,3 +1960,54 @@ issue_5338: {
}
expect_stdout: true
}
issue_5476: {
mangle = {
keep_fargs: true,
}
input: {
console.log(function(n) {
const a = 42;
}());
}
expect: {
console.log(function(n) {
const o = 42;
}());
}
expect_stdout: "undefined"
}
issue_5516: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
try {} catch (a) {
(function f() {
a;
})();
}
{
const a = function() {};
return a;
}
}());
}
expect: {
console.log(typeof function() {
try {} catch (a) {
void a;
}
{
const a = function() {};
return a;
}
}());
}
expect_stdout: "function"
}

View File

@@ -1669,3 +1669,41 @@ issue_5106_2: {
}
expect_stdout: "PASS"
}
issue_5506: {
options = {
dead_code: true,
}
input: {
try {
(function(a) {
var b = 1;
(function f() {
try {
b-- && f();
} catch (c) {}
console.log(a);
a = 42 in (a = "bar");
})();
})("foo");
} catch (e) {}
}
expect: {
try {
(function(a) {
var b = 1;
(function f() {
try {
b-- && f();
} catch (c) {}
console.log(a);
a = 42 in (a = "bar");
})();
})("foo");
} catch (e) {}
}
expect_stdout: [
"foo",
"bar",
]
}

View File

@@ -180,6 +180,26 @@ collapse_arg_sequence: {
node_version: ">=6"
}
collapse_in_arg: {
options = {
collapse_vars: true,
keep_fargs: false,
unused: true,
}
input: {
(function(a, b = a) {
b("PASS");
})(console.log);
}
expect: {
(function(a) {
a("PASS");
})(console.log);
}
expect_stdout: "PASS"
node_version: ">=6"
}
collapse_value_1: {
options = {
collapse_vars: true,
@@ -541,7 +561,7 @@ inline_side_effects_2: {
}
expect: {
var a = 42;
[ [].e = --a ] = [ console ];
[ [][0] = --a ] = [ console ];
console.log(a);
}
expect_stdout: "42"
@@ -1170,6 +1190,49 @@ mangle_arrow_2_toplevel: {
node_version: ">=6.9.3"
}
collapse_preceding_simple_arg: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = "foo";
console.log(function(b, c = "bar") {
return b + c;
}(a, a));
}
expect: {
var a = "foo";
console.log(function(b, c = "bar") {
return a + c;
}(0, a));
}
expect_stdout: "foofoo"
node_version: ">=6"
}
drop_preceding_simple_arg: {
options = {
collapse_vars: true,
keep_fargs: false,
unused: true,
}
input: {
var a = "foo";
console.log(function(b, c = "bar") {
return b + c;
}(a, a));
}
expect: {
var a = "foo";
console.log(function(c = "bar") {
return a + c;
}(a));
}
expect_stdout: "foofoo"
node_version: ">=6"
}
issue_4444: {
options = {
collapse_vars: true,
@@ -1558,7 +1621,7 @@ issue_4502_4: {
(function(a, b = console.log("FAIL")) {})(..."" + console.log(42));
}
expect: {
[ , [].e = console.log("FAIL") ] = [ ..."" + console.log(42) ];
[ , [][0] = console.log("FAIL") ] = [ ..."" + console.log(42) ];
}
expect_stdout: "42"
node_version: ">=6"
@@ -2183,7 +2246,7 @@ issue_5340_2: {
}
expect: {
var a;
[ [].e = 0 ] = [ ({ p: a } = true).q ];
[ [][0] = 0 ] = [ ({ p: a } = true).q ];
console.log(a);
}
expect_stdout: "undefined"
@@ -2209,3 +2272,747 @@ issue_5340_3: {
expect_stdout: "undefined"
node_version: ">=6"
}
issue_5407: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
(function(a) {
for (var i = 0; i < 2; i++)
(function(b = 4) {
console.log(b);
a = 2;
})(a);
})();
}
expect: {
(function(a) {
for (var i = 0; i < 2; i++)
(function(b = 4) {
console.log(b);
a = 2;
})(a);
})();
}
expect_stdout: [
"4",
"2",
]
node_version: ">=6"
}
issue_5444_1: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a = 42;
var b = function({} = setImmediate(function() {
console.log(a++);
})) {
return this;
}();
console.log(typeof b);
}
expect: {
var a = 42;
var b = function({} = setImmediate(function() {
console.log(a++);
})) {
return this;
}();
console.log(typeof b);
}
expect_stdout: [
"object",
"42",
]
node_version: ">=6"
}
issue_5444_2: {
options = {
merge_vars: true,
}
input: {
function f(a, b = a++) {
return b;
}
console.log(f("FAIL") || "PASS");
}
expect: {
function f(a, b = a++) {
return b;
}
console.log(f("FAIL") || "PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5444_3: {
options = {
merge_vars: true,
}
input: {
function f(a, b = function(c = a *= this) {
return c;
}()) {
return b;
}
console.log(f("FAIL") || "PASS");
}
expect: {
function f(a, b = function(c = a *= this) {
return c;
}()) {
return b;
}
console.log(f("FAIL") || "PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5448_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(a = typeof console.log) {
do {
var b = [ ...a ];
} while (console.log("PASS"));
})();
}
expect: {
(function(a = console.log) {
do {} while (console.log("PASS"));
})();
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5448_2: {
options = {
keep_fargs: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a = typeof console) {
do {
var b = [ ...a ];
} while (console.log("PASS"));
})();
}
expect: {
(function(a = 0) {
do {} while (console.log("PASS"));
})();
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5448_3: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var [ a = typeof console ] = [ void console.log("PASS") ];
var b = [ ...a ];
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5448_4: {
options = {
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var { p: a = typeof console } = { p: void console.log("PASS") };
var b = [ ...a ];
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5463: {
options = {
collapse_vars: true,
conditionals: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
if (console.log("PASS"))
var a = void 0,
b = void 0,
b = ([ a = FAIL ] = b && b);
}
expect: {
var a, b, b;
console.log("PASS") && (
b = a = void 0,
b = [a = FAIL] = a && a
);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5465: {
options = {
inline: true,
merge_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a, b) {
(function(c = b = "FAIL 2") {
this && console.log(b || "PASS");
})(42 - a && a);
}
f("FAIL 1");
}
expect: {
a = "FAIL 1",
void function(c = b = "FAIL 2") {
this && console.log(b || "PASS");
}(42 - a && a);
var a, b;
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5485: {
options = {
comparisons: true,
}
input: {
(function f(f, a = console.log(void 0 === f ? "PASS" : "FAIL")) {})();
}
expect: {
(function f(f, a = console.log(void 0 === f ? "PASS" : "FAIL")) {})();
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_1_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_1_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_2_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f([ b ] = []) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;) {
var [ [] = [] ] = [];
throw "PASS";
}
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_2_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f([ b ] = []) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;) {
var [ [] = [] ] = [];
throw "PASS";
}
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_3_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42, c = null) {
c;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_3_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42, c = null) {
c;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_4_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42, [ c ] = []) {
c;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;) {
var [ [] = [] ] = [];
throw "PASS";
}
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_4_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b = 42, [ c ] = []) {
c;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;) {
var [ [] = [] ] = [];
throw "PASS";
}
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5536: {
options = {
inline: true,
keep_fargs: true,
unused: true,
}
input: {
(function*() {
(([], a = 42) => {})([]);
console.log(typeof a);
})().next();
}
expect: {
(function*() {
[ , [][0] = 0 ] = [ [] ],
void 0;
console.log(typeof a);
})().next();
}
expect_stdout: "undefined"
node_version: ">=6"
}
issue_5566_1: {
options = {
unused: true,
}
input: {
(function(a, f = function() {
return a;
}) {
var a = "foo";
console.log(a, f());
})("bar");
}
expect: {
(function(a, f = function() {
return a;
}) {
var a = "foo";
console.log(a, f());
})("bar");
}
expect_stdout: "foo bar"
node_version: ">=6"
}
issue_5566_2: {
options = {
inline: true,
reduce_vars: true,
}
input: {
(function(a, f = function() {
return a;
}) {
function a() {}
console.log(typeof a, typeof f());
})(42);
}
expect: {
(function(a, f = function() {
return a;
}) {
function a() {}
console.log(typeof a, typeof f());
})(42);
}
expect_stdout: "function number"
node_version: ">=6"
}
issue_5566_3: {
options = {
reduce_vars: true,
unused: true,
}
input: {
(function(a, f = function() {
return a;
}) {
function a() {}
console.log(typeof a, typeof f());
})(42);
}
expect: {
(function(a, f = function() {
return a;
}) {
function a() {}
console.log(typeof a, typeof f());
})(42);
}
expect_stdout: "function number"
node_version: ">=6"
}
issue_5566_4: {
options = {
collapse_vars: true,
unused: true,
}
input: {
(function(a, b = function() {
return a;
}) {
var a = 0;
b()("PASS");
})(console.log);
}
expect: {
(function(a, b = function() {
return a;
}) {
var a = 0;
b()("PASS");
})(console.log);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5566_5: {
options = {
hoist_vars: true,
}
input: {
(function(a, f = function() {
return a;
}) {
var a = "foo";
var b;
console.log(a, f());
})("bar");
}
expect: {
(function(a, f = function() {
return a;
}) {
var b, a = "foo";
console.log(a, f());
})("bar");
}
expect_stdout: "foo bar"
node_version: ">=6"
}

View File

@@ -472,6 +472,93 @@ funarg_collapse_vars_3: {
node_version: ">=6"
}
funarg_collapse_vars_4: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = "PASS";
(function(b, { log: c }) {
c(b);
})(a, console);
}
expect: {
var a = "PASS";
(function(b, { log: c }) {
c(a);
})(0, console);
}
expect_stdout: "PASS"
node_version: ">=6"
}
funarg_collapse_vars_5: {
options = {
collapse_vars: true,
unused: true,
}
input: {
A = "FAIL";
B = "PASS";
try {
console.log(function({}, a) {
return a;
}(null, A = B));
} catch (e) {}
console.log(A);
}
expect: {
A = "FAIL";
B = "PASS";
try {
console.log(function({}, a) {
return a;
}(null, A = B));
} catch (e) {}
console.log(A);
}
expect_stdout: "PASS"
node_version: ">=6"
}
funarg_collapse_vars_6: {
options = {
collapse_vars: true,
unused: true,
}
input: {
A = "FAIL";
B = "PASS";
function f() {
console.log(function({}, a) {
return a;
}(null, A = B));
}
try {
f();
} catch (e) {
console.log(A);
}
}
expect: {
A = "FAIL";
B = "PASS";
function f() {
console.log(function({}, a) {
return a;
}(null, A = B));
}
try {
f();
} catch (e) {
console.log(A);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
funarg_reduce_vars_1: {
options = {
reduce_vars: true,
@@ -1848,8 +1935,8 @@ issue_4294: {
}) {}({
[a]: 0,
});
var a = A;
console.log(a);
var b = A;
console.log(b);
})();
}
expect_stdout: "PASS"
@@ -3020,6 +3107,7 @@ issue_5074_method_pure_getters: {
issue_5085_1: {
options = {
evaluate: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unsafe: true,
@@ -3032,8 +3120,7 @@ issue_5085_1: {
}
expect: {
var a = "PASS";
var b = [ 42 ][0];
b;
42;
console.log(a);
}
expect_stdout: "PASS"
@@ -3043,6 +3130,7 @@ issue_5085_1: {
issue_5085_2: {
options = {
evaluate: true,
passes: 2,
reduce_vars: true,
side_effects: true,
unsafe: true,
@@ -3059,7 +3147,7 @@ issue_5085_2: {
expect: {
var a = "PASS";
(function(b) {
b = [ 42 ][0];
0;
})();
console.log(a);
}
@@ -3400,7 +3488,7 @@ issue_5222: {
node_version: ">=6"
}
issue_5288: {
issue_5288_1: {
options = {
conditionals: true,
inline: true,
@@ -3420,7 +3508,37 @@ issue_5288: {
}() ]));
}
expect: {
while (console ? console.log("PASS") : 0, void 0);
while (function() {
if (console)
console.log("PASS");
}(), void 0);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5288_2: {
options = {
conditionals: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
varify: true,
}
input: {
while (function([]) {}([ function f() {
if (console)
return console.log("PASS");
else {
let a = 0;
}
}() ]));
}
expect: {
while (console && console.log("PASS"), void 0);
}
expect_stdout: "PASS"
node_version: ">=6"
@@ -3466,10 +3584,263 @@ issue_5314_2: {
A = this;
new function() {
[ {
[console.log(this === A ? "FAIL" : "PASS")]: [].e,
[console.log(this === A ? "FAIL" : "PASS")]: [][0],
} ] = [ 42 ];
}();
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5370: {
options = {
dead_code: true,
ie: true,
unused: true,
}
input: {
console.log(function arguments({}) {
return arguments;
try {} catch (e) {
var arguments;
}
}(42));
}
expect: {
console.log(function arguments({}) {
return arguments;
var arguments;
}(42));
}
expect_stdout: true
node_version: ">=6"
}
issue_5405_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var [ a ] = [ {} ];
console.log(a === a ? "PASS" : "FAIL");
}
expect: {
var [ a ] = [ {} ];
console.log(true ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5405_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
}
input: {
var { p: a } = { p: [] };
console.log(a === a ? "PASS" : "FAIL");
}
expect: {
var { p: a } = { p: [] };
console.log(true ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5423: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a, b;
function f({
[function() {
if (++a)
return 42;
}()]: c
}) {}
f(b = f);
console.log(typeof b);
}
expect: {
var a, b;
function f({
[function() {
if (++a)
return 42;
}()]: c
}) {}
f(b = f);
console.log(typeof b);
}
expect_stdout: "function"
node_version: ">=6"
}
issue_5454: {
options = {
hoist_props: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
var a = 42, a = {
p: [ a ] = [],
};
return "PASS";
}
console.log(f());
}
expect: {
console.log(function(a) {
a = 42, a = {
p: [ a ] = [],
};
return "PASS";
}());
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5485: {
options = {
comparisons: true,
}
input: {
(function f({
p: f,
[console.log(void 0 === f ? "PASS" : "FAIL")]: a,
}) {})(42);
}
expect: {
(function f({
p: f,
[console.log(void 0 === f ? "PASS" : "FAIL")]: a,
}) {})(42);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f([ b ]) {
b;
throw "PASS";
})([]);
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f([ b ]) {
b;
throw "PASS";
})([]);
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5573: {
options = {
collapse_vars: true,
}
input: {
var log = console.log;
var a = "FAIL";
(function([ { [log(a)]: b } ]) {
A = 42;
})((a = "PASS", [ {} ]));
log(a, A);
}
expect: {
var log = console.log;
var a = "FAIL";
(function([ { [log(a)]: b } ]) {
A = 42;
})((a = "PASS", [ {} ]));
log(a, A);
}
expect_stdout: [
"PASS",
"PASS 42",
]
node_version: ">=6"
}

View File

@@ -671,6 +671,76 @@ iife: {
}
}
drop_instanceof: {
options = {
booleans: true,
toplevel: true,
unused: true,
}
input: {
function f() {}
console.log({} instanceof f, Math instanceof f);
}
expect: {
console.log(!1, (Math, !1));
}
expect_stdout: "false false"
}
keep_instanceof_1: {
options = {
toplevel: true,
unused: true,
}
input: {
function f() {}
var f;
console.log({} instanceof f, Math instanceof f);
}
expect: {
function f() {}
var f;
console.log({} instanceof f, Math instanceof f);
}
expect_stdout: "false false"
}
keep_instanceof_2: {
options = {
toplevel: true,
unused: true,
}
input: {
function f() {}
var f = Object;
console.log({} instanceof f, Math instanceof f);
}
expect: {
function f() {}
var f = Object;
console.log({} instanceof f, Math instanceof f);
}
expect_stdout: "true true"
}
keep_instanceof_3: {
options = {
toplevel: true,
unused: true,
}
input: {
f = Object;
function f() {}
console.log({} instanceof f, Math instanceof f);
}
expect: {
f = Object;
function f() {}
console.log({} instanceof f, Math instanceof f);
}
expect_stdout: "true true"
}
issue_1539: {
options = {
collapse_vars: true,
@@ -3615,3 +3685,85 @@ issue_5271: {
}
expect_stdout: "42"
}
issue_5533_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
}
issue_5533_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(b) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
}

View File

@@ -907,6 +907,20 @@ chained_side_effects: {
]
}
instanceof_lambda: {
options = {
evaluate: true,
side_effects: true,
}
input: {
console.log(42 instanceof function() {});
}
expect: {
console.log(false);
}
expect_stdout: "false"
}
issue_1649: {
options = {
evaluate: true,
@@ -3349,3 +3363,56 @@ issue_5362_2: {
}
expect_stdout: "true"
}
issue_5380: {
options = {
evaluate: true,
keep_fnames: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = function f(b) {
return function g() {
for (b in { PASS: 42 });
}(), b;
}("FAIL");
console.log(a);
}
expect: {
var a = function f(b) {
return function g() {
for (b in { PASS: 42 });
}(), b;
}("FAIL");
console.log(a);
}
expect_stdout: "PASS"
}
issue_5558: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
sequences: true,
toplevel: true,
}
input: {
var a = 99, b = 0;
a++;
b++;
b += a;
b *= a;
b += a;
console.log(a);
}
expect: {
var a = 99, b = 0;
b++,
b = (b += ++a) * a + a,
console.log(a);
}
expect_stdout: "100"
}

View File

@@ -109,6 +109,17 @@ foreign: {
expect_exact: 'export*from"foo";export{}from"bar";export*as a from"baz";export{default}from"moo";export{b,c as case,default as delete,d}from"moz";'
}
non_identifiers: {
beautify = {
quote_style: 3,
}
input: {
export * as "42" from 'foo';
export { '42', "delete" as 'foo' } from "bar";
}
expect_exact: "export*as\"42\"from'foo';export{'42',delete as foo}from\"bar\";"
}
same_quotes: {
beautify = {
beautify: true,
@@ -417,6 +428,46 @@ hoist_funs: {
expect_exact: "export function f(){}export default async function*g(){}"
}
instanceof_default_class: {
options = {
toplevel: true,
unused: true,
}
input: {
export default class A {
f(a) {
return a instanceof A;
}
}
}
expect: {
export default class A {
f(a) {
return a instanceof A;
}
}
}
}
instanceof_default_function: {
options = {
toplevel: true,
unused: true,
}
input: {
export default function f() {
if (!(this instanceof f))
throw new Error("must instantiate");
}
}
expect: {
export default function f() {
if (!(this instanceof f))
throw new Error("must instantiate");
}
}
}
issue_4742_join_vars_1: {
options = {
join_vars: true,
@@ -496,3 +547,16 @@ issue_4766: {
export var a = "bar";
}
}
issue_5444: {
options = {
unused: true,
}
input: {
export var a = (console, console);
}
expect: {
console;
export var a = console;
}
}

View File

@@ -629,7 +629,7 @@ inline_binary_and: {
return void "moo";
return;
} else
return;
return void 0;
}());
}
expect_stdout: [
@@ -1508,6 +1508,48 @@ unsafe_call_3: {
expect_stdout: "3"
}
inline_eval_inner: {
options = {
inline: true,
}
input: {
(function() {
console.log(typeof eval("arguments"));
})();
}
expect: {
(function() {
console.log(typeof eval("arguments"));
})();
}
expect_stdout: "object"
}
inline_eval_outer: {
options = {
inline: true,
toplevel: true,
}
input: {
A = 42;
(function(a) {
console.log(a);
})(A);
console.log(eval("typeof a"));
}
expect: {
A = 42;
(function(a) {
console.log(a);
})(A);
console.log(eval("typeof a"));
}
expect_stdout: [
"42",
"undefined",
]
}
issue_2616: {
options = {
evaluate: true,
@@ -3515,6 +3557,27 @@ functions_inner_var: {
expect_stdout: "undefined undefined"
}
functions_keep_fnames: {
options = {
functions: true,
keep_fnames: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var FAIL = function PASS() {};
FAIL.p = 42;
console.log(FAIL.name, FAIL.p);
}
expect: {
var FAIL = function PASS() {};
FAIL.p = 42;
console.log(FAIL.name, FAIL.p);
}
expect_stdout: "PASS 42"
}
issue_2437: {
options = {
collapse_vars: true,
@@ -5519,7 +5582,7 @@ issue_3835: {
return f();
})();
}
expect_stdout: true
expect_stdout: RangeError("Maximum call stack size exceeded")
}
issue_3836_1: {
@@ -7772,7 +7835,7 @@ issue_5249_1: {
while (console.log("FAIL 2"));
return;
} else
return;
return void 0;
throw "FAIL 3";
}());
}
@@ -8228,3 +8291,358 @@ issue_5366: {
"baz",
]
}
issue_5376_1: {
options = {
evaluate: true,
inline: true,
join_vars: true,
loops: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a;
for (;42;)
var b = function() {
var c;
throw new Error(c++);
}();
}
expect: {
"use strict";
for (;;) {
42;
throw new Error(NaN);
}
}
expect_stdout: Error("NaN")
}
issue_5376_2: {
options = {
evaluate: true,
inline: true,
join_vars: true,
loops: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a;
for (;42;)
var b = function() {
var c;
c++;
throw new Error("PASS");
}();
}
expect: {
"use strict";
for (;;) {
0;
throw new Error("PASS");
}
}
expect_stdout: Error("PASS")
}
issue_5401: {
options = {
inline: true,
}
input: {
L: for (var a in function() {
while (console.log("PASS"));
}(), a) do {
continue L;
} while (console.log("FAIL"));
}
expect: {
while (console.log("PASS"));
L: for (var a in a) do {
continue L;
} while (console.log("FAIL"));
}
expect_stdout: "PASS"
}
issue_5409: {
options = {
inline: true,
merge_vars: true,
reduce_vars: true,
unused: true,
}
input: {
(function(a) {
(a = console) || FAIL(a);
(function(b) {
console.log(b && b);
while (!console);
})();
})();
}
expect: {
(function(a) {
(a = console) || FAIL(a);
a = void 0;
console.log(a && a);
while (!console);
return;
})();
}
expect_stdout: "undefined"
}
mixed_mode_inline_1: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return this;
}
console.log(function() {
return f();
}() ? "PASS" : "FAIL");
}
expect: {
console.log(function() {
return this;
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
mixed_mode_inline_1_strict: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {
return this;
}
console.log(function() {
return f();
}() ? "FAIL" : "PASS");
}
expect: {
"use strict";
console.log(function() {
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_2: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
"use strict";
return this;
}
console.log(function() {
return f();
}() ? "FAIL" : "PASS");
}
expect: {
console.log(function() {
"use strict";
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_2_strict: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {
"use strict";
return this;
}
console.log(function() {
return f();
}() ? "FAIL" : "PASS");
}
expect: {
"use strict";
console.log(function() {
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_3: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "PASS" : "FAIL");
}
expect: {
function f() {
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
mixed_mode_inline_3_strict: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "FAIL" : "PASS");
}
expect: {
"use strict";
console.log(function() {
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_4: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
"use strict";
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "FAIL" : "PASS");
}
expect: {
console.log(function() {
"use strict";
return function() {
"use strict";
return this;
}();
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
mixed_mode_inline_4_strict: {
options = {
directives: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
function f() {
"use strict";
return this;
}
console.log(function() {
"use strict";
return f();
}() ? "FAIL" : "PASS");
}
expect: {
"use strict";
console.log(function() {
return this;
}() ? "FAIL" : "PASS");
}
expect_stdout: "PASS"
}
module_inline: {
options = {
inline: true,
module: true,
reduce_vars: true,
}
input: {
var a = f;
function f() {
return a;
}
console.log(f() === a);
}
expect: {
var a = f;
function f() {
return a;
}
console.log(a === a);
}
expect_stdout: "true"
}

View File

@@ -1176,3 +1176,51 @@ issue_5182: {
"42",
]
}
issue_5441: {
options = {
hoist_props: true,
passes: 2,
reduce_vars: true,
side_effects: true,
}
input: {
console.log(function(a) {
(function() {
a = { p: this };
})();
return typeof a;
}());
}
expect: {
console.log(function(a) {
(function() {
a_p = this;
})();
var a_p;
return typeof {};
}());
}
expect_stdout: "object"
}
issue_5498: {
options = {
hoist_props: true,
reduce_vars: true,
toplevel: true,
}
input: {
var o = {
__proto__: 42,
};
while (console.log(typeof o.__proto__));
}
expect: {
var o = {
__proto__: 42,
};
while (console.log(typeof o.__proto__));
}
expect_stdout: "object"
}

View File

@@ -224,8 +224,7 @@ issue_4489: {
console.log(k);
}
expect: {
!(A = 0);
for (var k in true);
for (var k in !(A = 0));
console.log(k);
}
expect_stdout: "undefined"
@@ -407,9 +406,9 @@ issue_4893_2: {
expect: {
try{
(function() {
var b;
b = null;
b.p += 42;
var a;
a = null;
a.p += 42;
})();
} catch (e) {
console.log("PASS");
@@ -499,3 +498,108 @@ issue_5195: {
}
expect_stdout: "[object Object]"
}
issue_5378: {
options = {
hoist_vars: true,
inline: true,
toplevel: true,
}
input: {
var a = 2;
while (a--)
(function() {
var b;
var c;
while (console.log(b));
--b;
})();
}
expect: {
var a = 2;
while (a--) {
b = void 0;
var b, c;
while (console.log(b));
--b;
}
}
expect_stdout: [
"undefined",
"undefined",
]
}
issue_5411_1: {
options = {
collapse_vars: true,
dead_code: true,
hoist_vars: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
var a = "PASS";
b++;
b = a;
var b = b, c = c && c[b];
console.log(b);
}
expect: {
var b, c, a = "PASS";
b++;
b = a;
c = c && c[b];
console.log(b);
}
expect_stdout: "PASS"
}
issue_5411_2: {
options = {
collapse_vars: true,
dead_code: true,
evaluate: true,
hoist_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
b++;
b = a;
var b = b, c = c && c[b];
console.log(b);
}
expect: {
var b, c;
b++;
b = "PASS",
c = c && c[b];
console.log(b);
}
expect_stdout: "PASS"
}
issue_5411_3: {
options = {
collapse_vars: true,
hoist_vars: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = console;
a++;
var a = A = a;
console.log(A);
}
expect: {
var a = console;
a = A = ++a;
console.log(A);
}
expect_stdout: "NaN"
}

View File

@@ -850,3 +850,546 @@ issue_866_2: {
})();
}
}
identical_returns_1: {
options = {
conditionals: true,
if_return: true,
}
input: {
console.log(function() {
if (console.log("foo"))
return 42;
else
while (console.log("bar"));
return 42;
}());
}
expect: {
console.log(function() {
if (!console.log("foo"))
while (console.log("bar"));
return 42;
}());
}
expect_stdout: [
"foo",
"bar",
"42",
]
}
identical_returns_2: {
options = {
conditionals: true,
if_return: true,
}
input: {
console.log(function() {
if (console.log("foo"))
while (console.log("FAIL"));
else
return "bar";
return "bar";
}());
}
expect: {
console.log(function() {
if (console.log("foo"))
while (console.log("FAIL"));
return "bar";
}());
}
expect_stdout: [
"foo",
"bar",
]
}
identical_returns_3: {
options = {
if_return: true,
}
input: {
function f(a) {
if (a)
return 42;
if (a)
return;
return 42;
}
if (f(console))
console.log("PASS");
}
expect: {
function f(a) {
if (a)
return 42;
if (a)
;
else
return 42;
}
if (f(console))
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4374: {
options = {
booleans: true,
conditionals: true,
if_return: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
console.log(f(console));
function f(a) {
if (console) return 0;
if (a) return 1;
return 0;
}
})();
}
expect: {
(function() {
console.log(function(a) {
return !console && a ? 1 : 0;
}(console));
})();
}
expect_stdout: "0"
}
issue_5521: {
options = {
if_return: true,
}
input: {
console.log(function() {
if (console)
try {
return "FAIL";
} finally {
return;
}
}());
}
expect: {
console.log(function() {
if (console)
try {
return "FAIL";
} finally {
return;
}
}());
}
expect_stdout: "undefined"
}
issue_5523: {
options = {
if_return: true,
}
input: {
console.log(function() {
if (console)
try {
FAIL;
} finally {
return;
}
}());
}
expect: {
console.log(function() {
if (console)
try {
FAIL;
} finally {
return;
}
}());
}
expect_stdout: "undefined"
}
drop_catch: {
options = {
if_return: true,
}
input: {
function f() {
try {
throw 42;
} catch (e) {
return console.log("foo"), "bar";
} finally {
console.log("baz");
}
return "bar";
}
console.log(f());
}
expect: {
function f() {
try {
throw 42;
} catch (e) {
console.log("foo");
} finally {
console.log("baz");
}
return "bar";
}
console.log(f());
}
expect_stdout: [
"foo",
"baz",
"bar",
]
}
retain_catch: {
options = {
if_return: true,
}
input: {
function f() {
try {
throw 42;
} catch (e) {
return console.log("foo");
} finally {
console.log("bar");
}
return console.log("foo");
}
f();
}
expect: {
function f() {
try {
throw 42;
} catch (e) {
return console.log("foo");
} finally {
console.log("bar");
}
return console.log("foo");
}
f();
}
expect_stdout: [
"foo",
"bar",
]
}
retain_finally: {
options = {
if_return: true,
}
input: {
function f() {
try {
return console.log("foo"), FAIL;
} catch (e) {
return console.log("bar"), "FAIL";
} finally {
return console.log("baz"), console.log("moo");
}
return console.log("moo");
}
console.log(f());
}
expect: {
function f() {
try {
return console.log("foo"), FAIL;
} catch (e) {
return console.log("bar"), "FAIL";
} finally {
return console.log("baz"), console.log("moo");
}
return console.log("moo");
}
console.log(f());
}
expect_stdout: [
"foo",
"bar",
"baz",
"moo",
"undefined",
]
}
drop_try: {
options = {
if_return: true,
}
input: {
function f() {
try {
return console.log("foo"), "bar";
} finally {
console.log("baz");
}
return "bar";
}
console.log(f());
}
expect: {
function f() {
try {
console.log("foo");
} finally {
console.log("baz");
}
return "bar";
}
console.log(f());
}
expect_stdout: [
"foo",
"baz",
"bar",
]
}
retain_try: {
options = {
if_return: true,
}
input: {
function f() {
try {
return console.log("foo");
} finally {
console.log("bar");
}
return console.log("foo");
}
f();
}
expect: {
function f() {
try {
return console.log("foo");
} finally {
console.log("bar");
}
return console.log("foo");
}
f();
}
expect_stdout: [
"foo",
"bar",
]
}
drop_try_catch: {
options = {
if_return: true,
}
input: {
function f(a) {
try {
if (a())
return console.log("foo"), console.log("baz");
} catch (e) {
return console.log("bar"), console.log("baz");
}
return console.log("baz");
}
f(function() {
return 42;
});
f(function() {});
f();
}
expect: {
function f(a) {
try {
if (a())
console.log("foo");
} catch (e) {
console.log("bar");
}
return console.log("baz");
}
f(function() {
return 42;
});
f(function() {});
f();
}
expect_stdout: [
"foo",
"baz",
"baz",
"bar",
"baz",
]
}
empty_try: {
options = {
if_return: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
return f;
function f() {
try {} finally {}
return "PASS";
}
}()());
}
expect: {
console.log(function() {
return function() {
try {} finally {}
return "PASS";
};
}()());
}
expect_stdout: "PASS"
}
sequence_void_1: {
options = {
if_return: true,
}
input: {
function f() {
{
if (console)
return console, void console.log("PASS");
return;
}
}
f();
}
expect: {
function f() {
if (console)
console, void console.log("PASS");
}
f();
}
expect_stdout: "PASS"
}
sequence_void_2: {
options = {
if_return: true,
}
input: {
function f() {
{
if (console)
return console, void console.log("PASS");
return;
}
FAIL;
}
f();
}
expect: {
function f() {
if (console)
console, void console.log("PASS");
else {
return;
FAIL;
}
}
f();
}
expect_stdout: "PASS"
}
tail_match: {
options = {
if_return: true,
}
input: {
function f(a) {
if (a) {
console.log("foo");
return console.log("bar");
}
while (console.log("baz"));
return console.log("moo"), console.log("bar");
}
f();
f(42);
}
expect: {
function f(a) {
if (a)
console.log("foo");
else {
while (console.log("baz"));
console.log("moo");
}
return console.log("bar");
}
f();
f(42);
}
expect_stdout: [
"baz",
"moo",
"bar",
"foo",
"bar",
]
}
void_match: {
options = {
if_return: true,
}
input: {
function f(a) {
if (a) {
console.log("foo");
return;
}
while (console.log("bar"));
return console.log("baz"), void console.log("moo");
}
f();
f(42);
}
expect: {
function f(a) {
if (a)
console.log("foo");
else {
while (console.log("bar"));
console.log("baz"),
console.log("moo");
}
}
f();
f(42);
}
expect_stdout: [
"bar",
"baz",
"moo",
"foo",
]
}

View File

@@ -40,6 +40,17 @@ default_keys: {
expect_exact: 'import foo,{bar}from"baz";'
}
non_identifiers: {
beautify = {
quote_style: 3,
}
input: {
import { '42' as foo } from "bar";
import { "foo" as bar } from 'baz';
}
expect_exact: "import{'42'as foo}from\"bar\";import{foo as bar}from'baz';"
}
dynamic: {
input: {
(async a => await import(a))("foo").then(bar);
@@ -227,3 +238,33 @@ issue_4708_2: {
import a from "foo";
}
}
pr_5550_1: {
input: {
if (console)
import("foo");
else
import.meta.url.replace(/bar/g, console.log);
}
expect: {
if (console)
import("foo");
else
import.meta.url.replace(/bar/g, console.log);
}
}
pr_5550_2: {
input: {
L: {
import("foo");
import.meta.url.replace(/bar/g, console.log);
}
}
expect: {
L: {
import("foo");
import.meta.url.replace(/bar/g, console.log);
}
}
}

View File

@@ -1258,8 +1258,7 @@ issues_3267_1: {
}
expect: {
!function() {
var i = Object();
if (i)
if (Object())
return console.log("PASS");
throw "FAIL";
}();

View File

@@ -111,6 +111,7 @@ labels_5: {
labels_6: {
options = {
dead_code: true,
unused: true,
}
input: {
out: break out;
@@ -208,6 +209,59 @@ labels_10: {
expect_stdout: "PASS"
}
labels_11: {
options = {
conditionals: true,
if_return: true,
unused: true,
}
input: {
L: if (console.log("PASS"))
break L;
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
labels_12: {
options = {
conditionals: true,
dead_code: true,
if_return: true,
}
input: {
L: try {
if (console.log("foo"))
break L;
throw "bar";
} catch (e) {
console.log(e);
break L;
} finally {
if (console.log("baz"))
break L;
}
}
expect: {
L: try {
if (!console.log("foo"))
throw "bar";
} catch (e) {
console.log(e);
} finally {
if (console.log("baz"))
break L;
}
}
expect_stdout: [
"foo",
"bar",
"baz",
]
}
issue_4466_1: {
mangle = {
v8: false,
@@ -327,3 +381,53 @@ issue_4466_2_toplevel_v8: {
}
expect_stdout: "PASS"
}
issue_5522: {
options = {
dead_code: true,
}
input: {
console.log(function() {
L: try {
return "FAIL";
} finally {
break L;
}
return "PASS";
}());
}
expect: {
console.log(function() {
L: try {
return "FAIL";
} finally {
break L;
}
return "PASS";
}());
}
expect_stdout: "PASS"
}
issue_5524: {
options = {
dead_code: true,
}
input: {
L: try {
FAIL;
} finally {
break L;
}
console.log("PASS");
}
expect: {
L: try {
FAIL;
} finally {
break L;
}
console.log("PASS");
}
expect_stdout: "PASS"
}

View File

@@ -190,6 +190,96 @@ if_dead_branch: {
node_version: ">=4"
}
retain_tail_1: {
options = {
conditionals: true,
}
input: {
"use strict";
function f(a) {
var b = "foo";
if (a) {
let b = "bar";
while (console.log("baz"));
console.log(b);
} else {
while (console.log("moo"));
console.log(b);
}
}
f();
f(42);
}
expect: {
"use strict";
function f(a) {
var b = "foo";
if (a) {
let b = "bar";
while (console.log("baz"));
console.log(b);
} else {
while (console.log("moo"));
console.log(b);
}
}
f();
f(42);
}
expect_stdout: [
"moo",
"foo",
"baz",
"bar",
]
node_version: ">=4"
}
retain_tail_2: {
options = {
conditionals: true,
}
input: {
"use strict";
function f(a) {
var b = "foo";
if (a) {
while (console.log("bar"));
console.log(b);
} else {
let b = "baz";
while (console.log("moo"));
console.log(b);
}
}
f();
f(42);
}
expect: {
"use strict";
function f(a) {
var b = "foo";
if (a) {
while (console.log("bar"));
console.log(b);
} else {
let b = "baz";
while (console.log("moo"));
console.log(b);
}
}
f();
f(42);
}
expect_stdout: [
"moo",
"baz",
"bar",
"foo",
]
node_version: ">=4"
}
merge_vars_1: {
options = {
merge_vars: true,
@@ -892,6 +982,40 @@ if_return_2: {
node_version: ">=4"
}
if_return_3: {
options = {
if_return: true,
}
input: {
"use strict";
var a = "PASS";
function f(b) {
if (console) {
let b = a;
return b;
} else
while (console.log("FAIL 1"));
return b;
}
console.log(f("FAIL 2"));
}
expect: {
"use strict";
var a = "PASS";
function f(b) {
if (console) {
let b = a;
return b;
} else
while (console.log("FAIL 1"));
return b;
}
console.log(f("FAIL 2"));
}
expect_stdout: "PASS"
node_version: ">=4"
}
do_if_continue_1: {
options = {
if_return: true,
@@ -1604,48 +1728,6 @@ issue_4305_2: {
node_version: ">=4"
}
issue_1753: {
mangle = {
toplevel: false,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_1753_toplevel: {
mangle = {
toplevel: true,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let e = 0; e < 1; e++)
console.log(e);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_4438: {
options = {
if_return: true,
@@ -1667,7 +1749,7 @@ issue_4438: {
function f() {
if (console) {
let a = console.log;
void a("PASS");
a("PASS");
}
}
f();
@@ -2020,3 +2102,23 @@ issue_5338: {
expect_stdout: ReferenceError("a is not defined")
node_version: ">=4"
}
issue_5476: {
mangle = {
keep_fargs: true,
}
input: {
"use strict";
console.log(function(n) {
let a;
}());
}
expect: {
"use strict";
console.log(function(n) {
let o;
}());
}
expect_stdout: "undefined"
node_version: ">=4"
}

View File

@@ -3702,3 +3702,146 @@ issue_5182: {
]
node_version: ">=4"
}
issue_5420: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
do {
var a = "FAIL 1";
a && a.p;
a = "FAIL 2";
try {
continue;
} catch (e) {}
var b = "FAIL 3";
} while (console.log(b || "PASS"));
}
expect: {
do {
var a = "FAIL 1";
a && a.p;
a = "FAIL 2";
try {
continue;
} catch (e) {}
var b = "FAIL 3";
} while (console.log(b || "PASS"));
}
expect_stdout: "PASS"
}
issue_5451: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
A = 1;
var a = 1, b;
console.log(function f() {
return a-- && f(b = A, b);
}());
}
expect: {
A = 1;
var a = 1, b;
console.log(function f() {
return a-- && f(b = A, b);
}());
}
expect_stdout: "0"
}
issue_5471_1: {
options = {
conditionals: true,
inline: true,
merge_vars: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL 1";
function f(b, c) {
function g() {
if (console)
return 42;
else
c = "FAIL 2";
}
var d = g();
console.log(c || "PASS");
var e = function h() {
while (b && e);
}();
}
f(a++) && a;
}
expect: {
var a = "FAIL 1";
var b, c, e;
b = +a,
function() {
if (console)
return;
c = "FAIL 2";
}(),
console.log(c || "PASS"),
e = function() {
while (b && e);
}();
}
expect_stdout: "PASS"
}
issue_5471_2: {
options = {
conditionals: true,
evaluate: true,
inline: true,
merge_vars: true,
reduce_vars: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
var a = "FAIL 1";
function f(b, c) {
function g() {
if (console)
return 42;
else
c = "FAIL 2";
}
var d = g();
console.log(c || "PASS");
var e = function h() {
while (b && e);
}();
}
f(a++) && a;
}
expect: {
var a = "FAIL 1";
var b, c, e;
b = +a,
function() {
if (console)
return;
c = "FAIL 2";
}(),
console.log(c || "PASS"),
e = function() {
while (b && e);
}(),
void 0;
}
expect_stdout: "PASS"
}

View File

@@ -308,6 +308,7 @@ issue_4679: {
issue_5266: {
options = {
inline: true,
side_effects: true,
}
input: {
[

View File

@@ -147,7 +147,7 @@ relational: {
"bar" >= "bar";
}
expect: {
bar();
0 instanceof bar();
bar();
bar(), bar();
bar();

View File

@@ -6,6 +6,7 @@ reduce_vars: {
C: 0,
},
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
@@ -6688,8 +6689,7 @@ issues_3267_1: {
}
expect: {
!function(x) {
var i = Object();
if (i)
if (Object())
return console.log("PASS");
throw "FAIL";
}();
@@ -7861,3 +7861,38 @@ issue_5324: {
}
expect_stdout: "NaN"
}
issue_5434: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
for (var i = 0; i < 2; i++) {
var b = "FAIL";
f && f();
a = b;
var f = function() {
b = "PASS";
};
}
return a;
}());
}
expect: {
console.log(function(a) {
for (var i = 0; i < 2; i++) {
var b = "FAIL";
f && f();
a = b;
var f = function() {
b = "PASS";
};
}
return a;
}());
}
expect_stdout: "PASS"
}

View File

@@ -50,6 +50,22 @@ regexp_properties: {
expect_stdout: "abc true false 0 false"
}
instanceof_1: {
input: {
console.log(/foo/ instanceof RegExp);
}
expect_exact: "console.log(/foo/ instanceof RegExp);"
expect_stdout: "true"
}
instanceof_2: {
input: {
console.log(42 + /foo/ instanceof Object);
}
expect_exact: "console.log(42+/foo/ instanceof Object);"
expect_stdout: "false"
}
issue_3434_1: {
options = {
evaluate: true,

View File

@@ -648,7 +648,7 @@ drop_new_function: {
}
expect: {
void ([ ... {
[console.log("PASS")]: [].e,
[console.log("PASS")]: [][0],
}] = []);
}
expect_stdout: "PASS"
@@ -1299,3 +1299,328 @@ issue_5360: {
expect_stdout: "PASS"
node_version: ">=8.3.0"
}
issue_5370: {
options = {
dead_code: true,
ie: true,
unused: true,
}
input: {
console.log(function arguments(...a) {
return arguments;
try {} catch (e) {
var arguments;
}
}());
}
expect: {
console.log(function arguments(...a) {
return arguments;
var arguments;
}());
}
expect_stdout: true
node_version: ">=6"
}
issue_5391: {
options = {
evaluate: true,
keep_fargs: false,
objects: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a, b = function f({
p: {},
...c
}) {
while (c.q);
}({
p: {
r: a++,
r: 0,
}
});
console.log(a);
}
expect: {
(function({
p: {},
...c
}) {
while (c.q);
})({
p: 0,
});
console.log(NaN);
}
expect_stdout: "NaN"
node_version: ">=8.3.0"
}
issue_5533_1_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(...b) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_1_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(...b) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_2_keep_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: true,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(...[ b ]) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5533_2_drop_fargs: {
options = {
evaluate: true,
inline: true,
join_vars: true,
keep_fargs: false,
loops: true,
side_effects: true,
unused: true,
}
input: {
"use strict";
try {
(function() {
var a;
for (; 1;)
a = function() {
(function f(...[ b ]) {
b;
throw "PASS";
})();
}();
})();
} catch (e) {
console.log(e);
}
}
expect: {
"use strict";
try {
(function() {
for (;;)
throw "PASS";
})();
} catch (e) {
console.log(e);
}
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5552_1: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
}
input: {
var log = console.log;
var a = f, b = log();
function f(...[ c = a = "PASS" ]) {}
f((a = "FAIL", b));
log(a);
}
expect: {
var log = console.log;
var a = f, b = log();
function f(...[ c = a = "PASS" ]) {}
f((a = "FAIL", b));
log(a);
}
expect_stdout: [
"",
"PASS",
]
node_version: ">=6"
}
issue_5552_2: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
}
input: {
var log = console.log;
var a = f;
function f(...{ [a = "PASS"]: b }) {}
f((a = "FAIL", 42));
log(a);
}
expect: {
var log = console.log;
var a = f;
function f(...{ [a = "PASS"]: b }) {}
f((a = "FAIL", 42));
log(a);
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5552_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = [ "FAIL", "PASS" ];
console.log(function(b, ...[ c = a.pop() ]) {
return b;
}(a.pop()));
}
expect: {
var a = [ "FAIL", "PASS" ];
console.log(function(b, ...[ c = a.pop() ]) {
return b;
}(a.pop()));
}
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5552_4: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = [ "FAIL", "PASS" ];
console.log(function(b, ...{ [a.pop()]: c }) {
return b;
}(a.pop()));
}
expect: {
var a = [ "FAIL", "PASS" ];
console.log(function(b, ...{ [a.pop()]: c }) {
return b;
}(a.pop()));
}
expect_stdout: "PASS"
node_version: ">=6"
}

View File

@@ -1329,7 +1329,7 @@ issue_3490_2: {
expect: {
var b = 42, c = "FAIL";
var a;
for (c = "PASS", b; "" == typeof d;);
for (c = "PASS"; "" == typeof d;);
console.log(c, b);
}
expect_stdout: "PASS 42"

View File

@@ -645,3 +645,56 @@ issue_4751: {
}
expect_stdout: "PASS"
}
drop_instanceof: {
options = {
side_effects: true,
}
input: {
42 instanceof function() {};
console.log("PASS");
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
drop_instanceof_reference: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
function f() {}
42 instanceof f;
console.log("PASS");
}
expect: {
function f() {}
console.log("PASS");
}
expect_stdout: "PASS"
}
retain_instanceof: {
options = {
side_effects: true,
}
input: {
try {
42 instanceof "foo";
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
0 instanceof "foo";
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -1,3 +1,12 @@
decimal: {
input: {
console.log({... 0.42});
}
expect_exact: "console.log({....42});"
expect_stdout: "{}"
node_version: ">=8.3.0"
}
collapse_vars_1: {
options = {
collapse_vars: true,
@@ -1166,3 +1175,31 @@ issue_5006: {
expect_stdout: "PASS"
node_version: ">=6"
}
issue_5382: {
options = {
side_effects: true,
}
input: {
({
f() {
({ ...this });
},
get p() {
console.log("PASS");
},
}).f();
}
expect: {
({
f() {
({ ...this });
},
get p() {
console.log("PASS");
},
}).f();
}
expect_stdout: "PASS"
node_version: ">=8.3.0"
}

View File

@@ -1608,3 +1608,83 @@ issue_5012: {
}
expect_stdout: "PASS"
}
issue_5543_1: {
options = {
dead_code: true,
switches: true,
}
input: {
var a;
switch (a) {
default:
switch (42) {
case a:
case console.log("PASS"):
}
break;
case null:
switch (false) {
case a:
case console.log("FAIL"):
}
}
}
expect: {
var a;
switch (a) {
default:
switch (42) {
case a:
case console.log("PASS"):
}
break;
case null:
switch (false) {
case a:
case console.log("FAIL"):
}
}
}
expect_stdout: "PASS"
}
issue_5543_2: {
options = {
dead_code: true,
switches: true,
}
input: {
var a;
switch (a) {
default:
switch (42) {
case a:
case console.log("PASS"):
}
break;
case null:
switch (42) {
case a:
case console.log("FAIL"):
}
}
}
expect: {
var a;
switch (a) {
default:
switch (42) {
case a:
case console.log("PASS"):
}
break;
case null:
switch (42) {
case a:
case console.log("FAIL"):
}
}
}
expect_stdout: "PASS"
}

View File

@@ -127,7 +127,7 @@ if_return: {
if (w) {
if (y) return;
} else if (z) return;
return x == y || (x && w(), y && z()), !0;
return x != y && (x && w(), y && z()), !0;
}
}
}

View File

@@ -111,7 +111,7 @@ hoist_props_const: {
}
}
expect: {
var o = 0, o_p = "PASS";
var o, o_p = "PASS";
console.log(o_p);
}
expect_stdout: "PASS"
@@ -613,3 +613,35 @@ issue_4954: {
]
node_version: ">=4"
}
issue_5516: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
varify: true,
}
input: {
"use strict";
console.log(typeof function() {
{
let a;
}
{
const a = function() {};
return a;
}
}());
}
expect: {
"use strict";
console.log(typeof function() {
{
const a = function() {};
return a;
}
}());
}
expect_stdout: "function"
node_version: ">=4"
}

View File

@@ -107,3 +107,209 @@ function_name_mangle_ie8: {
expect_exact: "(function(){console.log(typeof function n(o){})})();"
expect_stdout: "function"
}
issue_1753: {
mangle = {
toplevel: false,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_1753_toplevel: {
mangle = {
toplevel: true,
webkit: true,
}
input: {
"use strict";
let l = null;
for (let i = 0; i < 1; i++)
console.log(i);
}
expect: {
"use strict";
let l = null;
for (let e = 0; e < 1; e++)
console.log(e);
}
expect_stdout: "0"
node_version: ">=4"
}
issue_5032_await: {
options = {
merge_vars: true,
webkit: false,
}
input: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=8"
}
issue_5032_await_webkit: {
options = {
merge_vars: true,
webkit: true,
}
input: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect: {
function log(value) {
console.log(value);
return value;
}
async function f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS");
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=8"
}
issue_5032_yield: {
options = {
merge_vars: true,
webkit: false,
}
input: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS").next();
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=4"
}
issue_5032_yield_webkit: {
options = {
merge_vars: true,
webkit: true,
}
input: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=4"
}
issue_5480: {
mangle = {
webkit: true,
}
input: {
"use strict";
L: for (let a in console.log("PASS"));
}
expect: {
"use strict";
o: for (let o in console.log("PASS"));
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -907,7 +907,7 @@ drop_body: {
})([ console.log("baz") ]);
}
expect: {
[ [ , [].e = console.log("foo") ] ] = [ [ console.log("baz") ] ];
[ [ , [][0] = console.log("foo") ] ] = [ [ console.log("baz") ] ];
}
expect_stdout: [
"baz",
@@ -934,6 +934,21 @@ drop_unused_call: {
node_version: ">=4"
}
instanceof_lambda: {
options = {
evaluate: true,
side_effects: true,
}
input: {
console.log(42 instanceof function*() {});
}
expect: {
console.log(false);
}
expect_stdout: "false"
node_version: ">=4"
}
issue_4454_1: {
rename = false
options = {
@@ -978,8 +993,8 @@ issue_4454_2: {
expect: {
function f(a) {
(function*(c = console.log(a)) {})();
var a = 42..toString();
console.log(a);
var b = 42..toString();
console.log(b);
}
f("PASS");
}
@@ -1168,6 +1183,35 @@ issue_4641_2: {
node_version: ">=10"
}
issue_4641_3: {
options = {
if_return: 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 "FAIL";
} finally {
console.log("PASS");
}
}().next().then);
}
expect_stdout: [
"function",
"PASS",
]
node_version: ">=10"
}
issue_4769_1: {
options = {
side_effects: true,
@@ -1267,80 +1311,6 @@ issue_5019_2: {
node_version: ">=4"
}
issue_5032_normal: {
options = {
merge_vars: true,
webkit: false,
}
input: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var a = log(a), c = a;
log(a);
log(c);
}
f("PASS").next();
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=4"
}
issue_5032_webkit: {
options = {
merge_vars: true,
webkit: true,
}
input: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect: {
function log(value) {
console.log(value);
return value;
}
function *f(a) {
var b = log(a), c = b;
log(b);
log(c);
}
f("PASS").next();
}
expect_stdout: [
"PASS",
"PASS",
"PASS",
]
node_version: ">=4"
}
issue_5034: {
options = {
functions: true,
@@ -1451,3 +1421,283 @@ issue_5177: {
expect_stdout: "function"
node_version: ">=4"
}
issue_5385_1: {
options = {
inline: true,
}
input: {
(async function*() {
(function() {
try {
return console.log("foo");
} finally {
return console.log("bar");
}
console.log("baz");
})();
})().next();
console.log("moo");
}
expect: {
(async function*() {
(function() {
try {
return console.log("foo");
} finally {
return console.log("bar");
}
console.log("baz");
})();
})().next();
console.log("moo");
}
expect_stdout: [
"foo",
"bar",
"moo",
]
node_version: ">=10"
}
issue_5385_2: {
options = {
inline: true,
}
input: {
(async function*() {
return function() {
try {
return console.log("foo");
} finally {
return console.log("bar");
}
}();
})().next();
console.log("moo");
}
expect: {
(async function*() {
return function() {
try {
return console.log("foo");
} finally {
return console.log("bar");
}
}();
})().next();
console.log("moo");
}
expect_stdout: [
"foo",
"bar",
"moo",
]
node_version: ">=10"
}
issue_5385_3: {
options = {
inline: true,
}
input: {
(async function*() {
return function() {
try {
throw console.log("foo");
} catch (e) {
return console.log("bar");
}
}();
})().next();
console.log("moo");
}
expect: {
(async function*() {
try {
throw console.log("foo");
} catch (e) {
return console.log("bar");
}
return void 0;
})().next();
console.log("moo");
}
expect_stdout: [
"foo",
"bar",
"moo",
]
node_version: ">=10"
}
issue_5385_4: {
options = {
awaits: true,
inline: true,
}
input: {
(async function*() {
return async function() {
try {
return {
then(resolve) {
resolve(console.log("FAIL"));
},
};
} finally {
return "PASS";
}
}();
})().next().then(o => console.log(o.value, o.done));
}
expect: {
(async function*() {
return async function() {
try {
return {
then(resolve) {
resolve(console.log("FAIL"));
},
};
} finally {
return "PASS";
}
}();
})().next().then(o => console.log(o.value, o.done));
}
expect_stdout: "PASS true"
node_version: ">=10"
}
issue_5425: {
options = {
assignments: true,
ie: true,
toplevel: true,
unused: true,
yields: true,
}
input: {
var a = "FAIL";
var b = function* f() {}(a ? a = "PASS" : 42);
console.log(a, typeof f);
}
expect: {
var a = "FAIL";
(function* f() {})(a && (a = "PASS"));
console.log(a, typeof f);
}
expect_stdout: "PASS undefined"
node_version: ">=4"
}
issue_5456: {
options = {
inline: true,
merge_vars: true,
}
input: {
var a = true;
(function() {
(function(b, c) {
var d = function*() {
c = null;
}();
var e = function() {
if (c)
console.log(typeof d);
while (b);
}();
})(function(i) {
return console.log("foo") && i;
}(a));
})();
}
expect: {
var a = true;
(function() {
b = (i = a, console.log("foo") && i),
d = function*() {
c = null;
}(),
e = function() {
if (c) console.log(typeof d);
while (b);
}(),
void 0;
var b, c, d, e;
var i;
})();
}
expect_stdout: "foo"
node_version: ">=4"
}
issue_5506: {
options = {
dead_code: true,
}
input: {
console.log(function(a) {
var b = function*() {
a = null in (a = "PASS");
}();
try {
b.next();
} catch (e) {
return a;
}
}("FAIL"));
}
expect: {
console.log(function(a) {
var b = function*() {
a = null in (a = "PASS");
}();
try {
b.next();
} catch (e) {
return a;
}
}("FAIL"));
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_5526: {
options = {
inline: true,
side_effects: true,
}
input: {
(async function*() {
try {
return function() {
while (console.log("foo"));
}();
} finally {
console.log("bar");
}
})().next();
console.log("baz");
}
expect: {
(async function*() {
try {
while (console.log("foo"));
return void 0;
} finally {
console.log("bar");
}
})().next();
console.log("baz");
}
expect_stdout: [
"foo",
"baz",
"bar",
]
node_version: ">=10"
}

View File

@@ -0,0 +1 @@
function n(){return this||arguments[0]+arguments[1]}function o(){return this||arguments[0]+arguments[1]}console.log(n(n(1,3),5)),console.log(o(o(2,4),6));

View File

@@ -0,0 +1,13 @@
console.log(function() {
function sum(...params) {
return this || arguments[0] + arguments[1];
}
return sum(sum(1, 3), 5);
}());
console.log(function() {
"use strict";
function sum(...params) {
return this || arguments[0] + arguments[1];
}
return sum(sum(2, 4), 6);
}());

View File

@@ -928,6 +928,14 @@ describe("bin/uglifyjs", function() {
done();
});
});
it("Should work with --module", function(done) {
var command = uglifyjscmd + " test/input/module/input.js --module -mc";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/module/expect.js"));
done();
});
});
it("Should compress swarm of unused variables with reasonable performance", function(done) {
var code = [
"console.log(function() {",

View File

@@ -1,3 +1,4 @@
var acorn = require("acorn");
var assert = require("assert");
var UglifyJS = require("../node");
@@ -25,6 +26,7 @@ describe("export", function() {
"export { * };",
"export { * as A };",
"export { 42 as A };",
"export { 'A' as B };",
"export { A as B-C };",
"export { default as A };",
].forEach(function(code) {
@@ -51,8 +53,11 @@ describe("export", function() {
it("Should reject invalid `export ... from ...` statement syntax", function() {
[
"export from 'path';",
"export A from 'path';",
"export * from `path`;",
"export 'A' from 'path';",
"export A as B from 'path';",
"export 'A' as B from 'path';",
"export default from 'path';",
"export { A }, B from 'path';",
"export * as A, B from 'path';",
@@ -68,4 +73,109 @@ describe("export", function() {
}, code);
});
});
it("Should reject `export` statement not under top-level scope", function() {
[
"{ export {}; }",
"if (0) export var A;",
"function f() { export default 42; }",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
it("Should compare `export` statements correctly", function() {
var stats = {
Declaration: [
"export let A;",
"export const A = 42;",
"export var { A, B: [] } = C;",
"export function A() { return B(A); }",
"export async function* A({}, ...[]) { return B(A); }",
],
Default: [
"export default 42;",
"export default A => A(B);",
"export default class A extends B {}",
"export default (class A extends B {});",
"export default class A { static C = 42; }",
"export default class A extends B { static C = 42; }",
],
Foreign: [
"export * from 'path';",
"export {} from 'path';",
"export * as A from 'path';",
"export { default } from 'path';",
"export { A, B as C } from 'path';",
"export { A, default as C } from 'path';",
],
References: [
"export {};",
"export { A };",
"export { A as B };",
"export { A, B as C };",
"export { A as default };",
],
};
for (var k in stats) stats[k].forEach(function(c, i) {
var s = UglifyJS.parse(c);
assert.ok(s instanceof UglifyJS.AST_Toplevel, c);
assert.strictEqual(s.body.length, 1, c);
assert.strictEqual(s.body[0].TYPE, "Export" + k, c);
for (var l in stats) stats[l].forEach(function(d, j) {
var t = UglifyJS.parse(d);
assert.ok(t instanceof UglifyJS.AST_Toplevel, d);
assert.strictEqual(t.body.length, 1, d);
assert.strictEqual(t.body[0].TYPE, "Export" + l, d);
assert.strictEqual(s.equals(t), k === l && i === j, c + "\n" + d);
});
});
});
it("Should interoperate with ESTree correctly", function() {
[
"export var A = 42;",
"export default (class A {});",
"var A; export { A as '42' };",
"export { '42' } from 'path';",
"export * as '42' from 'path';",
].forEach(function(code) {
var ast = UglifyJS.parse(code);
try {
var spidermonkey = ast.to_mozilla_ast();
} catch (ex) {
assert.fail("[to_mozilla_ast] " + ex.stack);
}
try {
var ast2 = UglifyJS.AST_Node.from_mozilla_ast(spidermonkey);
} catch (ex) {
assert.fail("[from_mozilla_ast] " + ex.stack);
}
assert.strictEqual(ast2.print_to_string(), ast.print_to_string(), [
"spidermonkey:",
ast.print_to_string(),
ast2.print_to_string(),
].join("\n"));
try {
var acorn_est = acorn.parse(code, {
ecmaVersion: "latest",
locations: true,
sourceType: "module",
});
} catch (ex) {
assert.fail("[acorn.parse] " + ex.stack);
}
try {
var ast3 = UglifyJS.AST_Node.from_mozilla_ast(acorn_est);
} catch (ex) {
assert.fail("[from_acorn] " + ex.stack);
}
assert.strictEqual(ast3.print_to_string(), ast.print_to_string(), [
"acorn:",
ast.print_to_string(),
ast3.print_to_string(),
].join("\n"));
});
});
});

View File

@@ -1,3 +1,4 @@
var acorn = require("acorn");
var assert = require("assert");
var UglifyJS = require("../node");
@@ -8,15 +9,25 @@ describe("import", function() {
"import A;",
"import {};",
"import `path`;",
"{ import 'path'; }",
"import from 'path';",
"if (0) import 'path';",
"import * from 'path';",
"import 'A' from 'path';",
"import A-B from 'path';",
"import A as B from 'path';",
"import { A }, B from 'path';",
"import * as 'A' from 'path';",
"import * as A-B from 'path';",
"import * as A, B from 'path';",
"import * as A, {} from 'path';",
"import { * as A } from 'path';",
"import { * as 'A' } from 'path';",
"import { * as A-B } from 'path';",
"function f() { import 'path'; }",
"import { 42 as A } from 'path';",
"import { A-B as C } from 'path';",
"import { 'A' as 'B' } from 'path';",
].forEach(function(code) {
assert.throws(function() {
UglifyJS.parse(code);
@@ -25,4 +36,74 @@ describe("import", function() {
}, code);
});
});
it("Should compare `import` statements correctly", function() {
[
"import 'foo';",
"import 'path';",
"import A from 'path';",
"import { A } from 'path';",
"import * as A from 'path';",
"import A, { B } from 'path';",
"import A, * as B from 'path';",
"import { A as B } from 'path';",
"import A, { B, C as D } from 'path';",
].forEach(function(c, i, stats) {
var s = UglifyJS.parse(c);
assert.ok(s instanceof UglifyJS.AST_Toplevel, c);
assert.strictEqual(s.body.length, 1, c);
assert.ok(s.body[0] instanceof UglifyJS.AST_Import, c);
stats.forEach(function(d, j) {
var t = UglifyJS.parse(d);
assert.ok(t instanceof UglifyJS.AST_Toplevel, d);
assert.strictEqual(t.body.length, 1, d);
assert.ok(t.body[0] instanceof UglifyJS.AST_Import, d);
assert.strictEqual(s.equals(t), i === j, c + "\n" + d);
});
});
});
it("Should interoperate with ESTree correctly", function() {
[
"import A from 'path';",
"import * as A from 'path';",
"import A, * as B from 'path';",
"import { '42' as A, B } from 'path';",
"import A, { '42' as B } from 'path';",
].forEach(function(code) {
var ast = UglifyJS.parse(code);
try {
var spidermonkey = ast.to_mozilla_ast();
} catch (ex) {
assert.fail("[to_mozilla_ast] " + ex.stack);
}
try {
var ast2 = UglifyJS.AST_Node.from_mozilla_ast(spidermonkey);
} catch (ex) {
assert.fail("[from_mozilla_ast] " + ex.stack);
}
assert.strictEqual(ast2.print_to_string(), ast.print_to_string(), [
"spidermonkey:",
ast.print_to_string(),
ast2.print_to_string(),
].join("\n"));
try {
var acorn_est = acorn.parse(code, {
ecmaVersion: "latest",
locations: true,
sourceType: "module",
});
} catch (ex) {
assert.fail("[acorn.parse] " + ex.stack);
}
try {
var ast3 = UglifyJS.AST_Node.from_mozilla_ast(acorn_est);
} catch (ex) {
assert.fail("[from_acorn] " + ex.stack);
}
assert.strictEqual(ast3.print_to_string(), ast.print_to_string(), [
"acorn:",
ast.print_to_string(),
ast3.print_to_string(),
].join("\n"));
});
});
});

View File

@@ -1,6 +1,7 @@
var assert = require("assert");
var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code;
var semver = require("semver");
var UglifyJS = require("../..");
function read(path) {
@@ -320,6 +321,24 @@ describe("minify", function() {
});
});
describe("module", function() {
it("Should not inline `await` variables", function() {
if (semver.satisfies(process.version, "<8")) return;
var code = [
"console.log(function() {",
" return typeof await;",
"}());",
].join("\n");
assert.strictEqual(run_code("(async function(){" + code + "})();"), "undefined\n");
var result = UglifyJS.minify(code, {
module: true,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, "console.log(function(){return typeof await}());");
assert.strictEqual(run_code("(async function(){" + result.code + "})();"), "undefined\n");
});
});
describe("rename", function() {
it("Should be repeatable", function() {
var code = "!function(x){return x(x)}(y);";

View File

@@ -434,7 +434,7 @@ describe("test/reduce.js", function() {
"// }",
].join("\n"));
});
it("Should transform `export default` correctly", function() {
it("Should transform `export default class` correctly", function() {
var result = reduce_test(read("test/input/reduce/export_default.js"), {
compress: false,
toplevel: true,
@@ -448,4 +448,41 @@ describe("test/reduce.js", function() {
"// }",
].join("\n"));
});
it("Should transform `export default function` correctly", function() {
var code = [
"for (var k in this)",
" console.log(k);",
"export default (function f() {});",
"console.log(k);",
].join("\n");
var result = reduce_test(code, {
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure",
"// minify options: {",
'// "mangle": false',
"// }",
].join("\n"));
});
it("Should transform `export default (42)` correctly", function() {
var code = [
"export default (42);",
"for (var k in this)",
" console.log(k);",
].join("\n");
var result = reduce_test(code, {
compress: false,
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure",
"// minify options: {",
'// "compress": false,',
'// "mangle": false',
"// }",
].join("\n"));
});
});

View File

@@ -9,19 +9,22 @@ function beautify(ast) {
var beautified = UglifyJS.minify(ast, {
compress: false,
mangle: false,
module: ufuzz.module,
output: {
ast: true,
beautify: true,
braces: true,
},
});
if (beautified.error) return beautified;
return UglifyJS.minify(beautified.code, {
compress: false,
mangle: false,
output: {
ast: true,
},
});
if (!beautified.error) {
var verify = UglifyJS.minify(beautified.code, {
compress: false,
mangle: false,
module: ufuzz.module,
});
if (verify.error) return verify;
}
return beautified;
}
function validate(ast) {
@@ -35,6 +38,7 @@ function validate(ast) {
return UglifyJS.minify(ast, {
compress: false,
mangle: false,
module: ufuzz.module,
output: {
ast: true,
},
@@ -57,12 +61,12 @@ function test(input, to_moz, description, skip_on_error, beautified) {
var ast = UglifyJS.AST_Node.from_mozilla_ast(to_moz(input));
} catch (e) {
if (skip_on_error) return true;
console.log("//=============================================================");
console.log("//", description, "failed... round", round);
console.log(e);
console.log("// original code");
if (beautified === true) console.log("// (beautified)");
console.log(input.code);
console.error("//=============================================================");
console.error("//", description, "failed... round", round);
console.error(e);
console.error("// original code");
if (beautified === true) console.error("// (beautified)");
console.error(input.code);
return false;
}
var transformed = validate(ast);
@@ -74,34 +78,34 @@ function test(input, to_moz, description, skip_on_error, beautified) {
if (!test(beautified, to_moz, description, skip_on_error, true)) return false;
}
}
console.log("//=============================================================");
console.log("// !!!!!! Failed... round", round);
console.log("// original code");
console.error("//=============================================================");
console.error("// !!!!!! Failed... round", round);
console.error("// original code");
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
console.error("// !!! beautify failed !!!");
console.error(beautified.error.stack);
} else if (beautified === true) {
console.log("// (beautified)");
console.error("// (beautified)");
}
console.log(input.raw);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("//", description);
console.error(input.raw);
console.error();
console.error();
console.error("//-------------------------------------------------------------");
console.error("//", description);
if (transformed.error) {
console.log(transformed.error.stack);
console.error(transformed.error.stack);
} else {
beautified = beautify(transformed.ast);
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
console.log(transformed.code);
console.error("// !!! beautify failed !!!");
console.error(beautified.error.stack);
console.error(transformed.code);
} else {
console.log("// (beautified)");
console.log(beautified.code);
console.error("// (beautified)");
console.error(beautified.code);
}
}
console.log("!!!!!! Failed... round", round);
console.error("!!!!!! Failed... round", round);
return false;
}
return true;
@@ -115,9 +119,29 @@ for (var round = 1; round <= num_iterations; round++) {
var code = ufuzz.createTopLevelCode();
minify_options.forEach(function(options) {
var ok = true;
var input = UglifyJS.minify(options ? UglifyJS.minify(code, JSON.parse(options)).code : code, {
var minified;
if (options) {
var o = JSON.parse(options);
o.module = ufuzz.module;
minified = UglifyJS.minify(code, o);
if (minified.error) {
console.log("//=============================================================");
console.log("// minify() failed... round", round);
console.log("// original code");
console.log(code);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("minify(options):");
console.log(JSON.stringify(o, null, 2));
return;
}
minified = minified.code;
}
var input = UglifyJS.minify(minified || code, {
compress: false,
mangle: false,
module: ufuzz.module,
output: {
ast: true,
},
@@ -125,11 +149,27 @@ for (var round = 1; round <= num_iterations; round++) {
input.raw = options ? input.code : code;
if (input.error) {
ok = false;
console.log("//=============================================================");
console.log("// minify() failed... round", round);
console.log(input.error);
console.log("// original code");
console.log(code);
console.error("//=============================================================");
console.error("// parse() failed... round", round);
console.error("// original code");
console.error(code);
console.error();
console.error();
if (options) {
console.error("//-------------------------------------------------------------");
console.error("// minified code");
console.error(minified);
console.error();
console.error();
console.error("//-------------------------------------------------------------");
console.error("minify(options):");
console.error(JSON.stringify(o, null, 2));
console.error();
console.error();
}
console.error("//-------------------------------------------------------------");
console.error("// parse() error");
console.error(input.error);
}
if (ok) ok = test(input, function(input) {
return input.ast.to_mozilla_ast();
@@ -141,7 +181,10 @@ for (var round = 1; round <= num_iterations; round++) {
sourceType: "module",
});
}, "acorn.parse()", !ufuzz.verbose);
if (!ok) process.exit(1);
if (!ok && isFinite(num_iterations)) {
console.log();
process.exit(1);
}
});
}
console.log();

View File

@@ -210,10 +210,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
if (node.expression instanceof U.AST_Function) {
// hoist and return expressions from the IIFE function expression
var seq = [];
var scope = tt.find_parent(U.AST_Scope), seq = [];
node.expression.body.forEach(function(node) {
var expr = node instanceof U.AST_Exit ? node.value : node.body;
if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr)) {
if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr, scope)) {
// collect expressions from each statement's body
seq.push(expr);
}
@@ -241,6 +241,23 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true;
return node.name;
}
else if (node instanceof U.AST_Defun) {
switch (((node.start._permute += step) * steps | 0) % 2) {
case 0:
CHANGED = true;
return List.skip;
default:
if (can_hoist(node, tt.find_parent(U.AST_Scope))) {
// hoist function declaration body
var body = node.body;
node.body = [];
// retain function with empty body to be dropped later
body.push(node);
CHANGED = true;
return List.splice(body);
}
}
}
else if (node instanceof U.AST_DestructuredArray) {
var expr = node.elements[0];
if (expr && !(expr instanceof U.AST_Hole)) {
@@ -258,22 +275,6 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return expr.value;
}
}
else if (node instanceof U.AST_Defun) {
switch (((node.start._permute += step) * steps | 0) % 2) {
case 0:
CHANGED = true;
return List.skip;
default:
if (!has_exit(node) && can_hoist(node)) {
// hoist function declaration body
var body = node.body;
node.body = [];
body.push(node); // retain function with empty body to be dropped later
CHANGED = true;
return List.splice(body);
}
}
}
else if (node instanceof U.AST_DWLoop) {
var expr = [
node.condition,
@@ -295,6 +296,16 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_statement(expr);
}
}
else if (node instanceof U.AST_ExportDeclaration) {
node.start._permute++;
CHANGED = true;
return node.body;
}
else if (node instanceof U.AST_ExportDefault) {
node.start._permute++;
CHANGED = true;
return to_statement(node.body);
}
else if (node instanceof U.AST_Finally) {
// drop finally block
node.start._permute++;
@@ -350,6 +361,15 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_statement(expr);
}
}
else if (node instanceof U.AST_LabeledStatement) {
if (node.body instanceof U.AST_Statement
&& !has_loopcontrol(node.body, node.body, node)) {
// replace labelled statement with its non-labelled body
node.start._permute = REPLACEMENTS.length;
CHANGED = true;
return node.body;
}
}
else if (node instanceof U.AST_Object) {
// first property's value
var expr = node.properties[0];
@@ -382,7 +402,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
// hoist simple statement IIFE function expression body
node.start._permute++;
if (!has_exit(node.body.expression) && can_hoist(node.body.expression)) {
if (can_hoist(node.body.expression, tt.find_parent(U.AST_Scope))) {
CHANGED = true;
return List.splice(node.body.expression.body);
}
@@ -440,13 +460,14 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_statement(node.definitions[0].value);
}
}
else if (node instanceof U.AST_LabeledStatement) {
if (node.body instanceof U.AST_Statement
&& !has_loopcontrol(node.body, node.body, node)) {
// replace labelled statement with its non-labelled body
node.start._permute = REPLACEMENTS.length;
else if (node instanceof U.AST_VarDef) {
if (node.value) {
node.start._permute++;
CHANGED = true;
return node.body;
return new U.AST_VarDef({
name: node.name,
start: {},
});
}
}
@@ -647,21 +668,6 @@ function trim_trailing_whitespace(value) {
return ("" + value).replace(/\s+$/, "");
}
function has_exit(fn) {
var found = false;
var tw = new U.TreeWalker(function(node) {
if (found) return found;
if (node instanceof U.AST_Exit) {
return found = true;
}
if (node instanceof U.AST_Scope && node !== fn) {
return true; // don't descend into nested functions
}
});
fn.walk(tw);
return found;
}
function has_loopcontrol(body, loop, label) {
var found = false;
var tw = new U.TreeWalker(function(node) {
@@ -676,17 +682,31 @@ function has_loopcontrol(body, loop, label) {
return found;
}
function can_hoist(body) {
function can_hoist(body, scope) {
var found = false;
body.walk(new U.TreeWalker(function(node) {
var tw = new U.TreeWalker(function(node) {
if (found) return true;
if (node instanceof U.AST_Exit) return found = true;
if (node instanceof U.AST_NewTarget) return found = true;
if (node instanceof U.AST_Scope) {
if (node === body) return;
if (node instanceof U.AST_Arrow || node instanceof U.AST_AsyncArrow) node.argnames.forEach(function(sym) {
sym.walk(tw);
});
// don't descend into nested functions
return true;
}
if (node instanceof U.AST_Super) return found = true;
}));
if (node instanceof U.AST_SymbolDeclaration || node instanceof U.AST_SymbolRef) switch (node.name) {
case "await":
if (/^Async/.test(scope.TYPE)) return found = true;
return;
case "yield":
if (/Generator/.test(scope.TYPE)) return found = true;
return;
}
});
body.walk(tw);
return !found;
}
@@ -760,9 +780,9 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) {
if (minified.error) return minified;
var toplevel = sandbox.has_toplevel(minify_options);
var unminified = run_code(code, toplevel, result_cache, max_timeout);
var unminified = run(code, max_timeout);
var timeout = Math.min(100 * unminified.elapsed, max_timeout);
var minified_result = run_code(minified.code, toplevel, result_cache, timeout).result;
var minified_result = run(minified.code, timeout).result;
if (sandbox.same_stdout(unminified.result, minified_result)) {
return is_timed_out(unminified.result) && is_timed_out(minified_result) && {
@@ -774,6 +794,16 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) {
minified_result: minified_result,
elapsed: unminified.elapsed,
};
function run(code, timeout) {
if (minify_options.module) code = [
'"use strict";',
"(async()=>{",
code,
'})().catch(e=>process.on("exit",()=>{throw e}));',
].join("\n");
return run_code(code, toplevel, result_cache, timeout);
}
}
function test_minify(code, minify_options) {

View File

@@ -54,7 +54,7 @@ ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi
minify_in_situ "src" \
&& minify_in_situ "third_party" \
&& rm -rf node_modules \
&& npm_install \
&& npm_install --package-lock \
&& rm -rf build/* \
&& npm run build:terser-bundled \
&& npm run build:uglify-js-bundled \

View File

@@ -52,12 +52,17 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
};
exports.patch_module_statements = function(code) {
var count = 0, imports = [];
code = code.replace(/\bexport(?:\s*\{[^{}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) {
var count = 0, has_default = "", imports = [], strict_mode = "";
code = code.replace(/^\s*("|')use strict\1\s*;?/, function(match) {
strict_mode = match;
return "";
}).replace(/\bexport(?:\s*\{[^{}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) {
if (/^export\s+default/.test(match)) has_default = "var _uglify_export_default_;";
if (!header) return "";
if (header.length == 1) return "0, " + header;
do {
var name = "_export_default_" + ++count;
var name = "_uglify_export_default_";
if (/^class\b/.test(header)) do {
name = "_uglify_export_default_" + ++count;
} while (code.indexOf(name) >= 0);
return header.slice(0, -1) + " " + name + header.slice(-1);
}).replace(/\bimport\.meta\b/g, function() {
@@ -76,7 +81,7 @@ exports.patch_module_statements = function(code) {
return "";
});
imports.push("");
return imports.join("\n") + code;
return strict_mode + has_default + imports.join("\n") + code;
};
function is_error(result) {

View File

@@ -128,7 +128,7 @@ for (var i = 2; i < process.argv.length; ++i) {
var SUPPORT = function(matrix) {
for (var name in matrix) {
matrix[name] = typeof sandbox.run_code(matrix[name]) == "string";
matrix[name] = !sandbox.is_error(sandbox.run_code(matrix[name]));
}
return matrix;
}({
@@ -140,6 +140,7 @@ var SUPPORT = function(matrix) {
class: "class C { f() {} }",
class_field: "class C { p = 0; }",
class_private: "class C { #f() {} }",
class_static_init: "class C { static {} }",
computed_key: "({[0]: 0});",
const_block: "var a; { const a = 0; }",
default_value: "[ a = 0 ] = [];",
@@ -252,7 +253,7 @@ 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.push(" in ");
BINARY_OPS.push(" in ", " instanceof ");
var ASSIGNMENTS = [ "=" ];
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
@@ -405,7 +406,7 @@ function createTopLevelCode() {
unique_vars.length = 0;
classes.length = 0;
allow_this = true;
async = false;
async = SUPPORT.async && rng(200) == 0;
has_await = false;
export_default = false;
generator = false;
@@ -413,7 +414,7 @@ function createTopLevelCode() {
funcs = 0;
clazz = 0;
imports = 0;
in_class = 0;
in_class = async;
called = Object.create(null);
var s = [
strictMode(),
@@ -1181,7 +1182,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
unique_vars.length = unique_len;
});
}
if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
if (n !== 0) s += [
" finally { ",
createStatements(rng(5) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
" }",
].join("");
return s;
case STMT_C:
return "c = c + 1;";
@@ -1805,7 +1810,7 @@ function createClassLiteral(recurmax, stmtDepth, canThrow, name) {
if (canThrow && rng(20) == 0) {
s += p;
} else {
s += "(" + p + " && " + p + ".constructor === Function ? " + p + " : function() {})";
s += "(typeof " + p + ' == "function" && typeof ' + p + '.prototype == "object" && ' + p + ".constructor === Function ? " + p + " : function() {})";
}
}
s += " {\n";
@@ -1824,15 +1829,31 @@ function createClassLiteral(recurmax, stmtDepth, canThrow, name) {
declared.push(internal);
}
if (SUPPORT.class_field && rng(2)) {
s += internal || createObjectKey(recurmax, stmtDepth, canThrow);
if (internal) {
s += internal;
} else if (fixed && bug_class_static_nontrivial) {
s += getDotKey();
} else {
s += createObjectKey(recurmax, stmtDepth, canThrow);
}
if (rng(5)) {
async = bug_async_class_await && fixed && 0;
async = false;
generator = false;
s += " = " + createExpression(recurmax, NO_COMMA, stmtDepth, fixed ? canThrow : CANNOT_THROW);
generator = save_generator;
async = save_async;
}
s += ";\n";
} else if (SUPPORT.class_static_init && fixed && !internal && rng(10) == 0) {
async = false;
generator = false;
s += [
"{ ",
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, stmtDepth),
" }\n",
].join("");
generator = save_generator;
async = save_async;
} else {
if (!fixed && !internal && constructor && rng(10) == 0) {
internal = constructor;
@@ -1988,7 +2009,7 @@ function createBinaryOp(noComma, canThrow) {
var op;
do {
op = BINARY_OPS[rng(BINARY_OPS.length)];
} while (noComma && op == "," || !canThrow && op == " in ");
} while (noComma && op == "," || !canThrow && /^ in/.test(op));
return op;
}
@@ -2031,7 +2052,7 @@ function isBannedKeyword(name) {
case "let":
return in_class;
case "await":
return async !== false;
return async || in_class && bug_class_static_await;
case "yield":
return generator || in_class;
}
@@ -2074,13 +2095,23 @@ function createVarName(maybe, dontStore) {
}
if (require.main !== module) {
exports.createTopLevelCode = createTopLevelCode;
exports.createTopLevelCode = function() {
var code = createTopLevelCode();
exports.module = async && has_await;
return code;
};
exports.num_iterations = num_iterations;
exports.verbose = verbose;
return;
}
function run_code(code, toplevel, timeout) {
if (async && has_await) code = [
'"use strict";',
"(async()=>{",
code,
'})().catch(e=>process.on("exit",()=>{throw e}));',
].join("\n");
return sandbox.run_code(sandbox.patch_module_statements(code), toplevel, timeout);
}
@@ -2100,7 +2131,9 @@ function errorln(msg) {
}
function try_beautify(code, toplevel, result, printfn, options) {
var beautified = UglifyJS.minify(code, JSON.parse(beautify_options));
var o = JSON.parse(beautify_options);
if (async && has_await) o.module = true;
var beautified = UglifyJS.minify(code, o);
if (beautified.error) {
printfn("// !!! beautify failed !!!");
printfn(beautified.error);
@@ -2110,7 +2143,7 @@ function try_beautify(code, toplevel, result, printfn, options) {
} else if (options) {
var uglified = UglifyJS.minify(beautified.code, JSON.parse(options));
var expected, actual;
if (typeof uglify_code != "string" || uglified.error) {
if (sandbox.is_error(uglify_code) || uglified.error) {
expected = uglify_code;
actual = uglified.error;
} else {
@@ -2147,7 +2180,7 @@ function log_suspects(minify_options, component) {
m[component] = o;
m.validate = true;
var result = UglifyJS.minify(original_code, m);
if (typeof uglify_code != "string") {
if (sandbox.is_error(uglify_code)) {
return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) {
errorln("Error testing options." + component + "." + name);
@@ -2175,7 +2208,7 @@ function log_suspects_global(options, toplevel) {
m[component] = false;
m.validate = true;
var result = UglifyJS.minify(original_code, m);
if (typeof uglify_code != "string") {
if (sandbox.is_error(uglify_code)) {
return !sandbox.same_stdout(uglify_code, result.error);
} else if (result.error) {
errorln("Error testing options." + component);
@@ -2204,7 +2237,16 @@ function log(options) {
errorln();
errorln();
errorln("//-------------------------------------------------------------");
if (typeof uglify_code == "string") {
if (sandbox.is_error(uglify_code)) {
errorln("// !!! uglify failed !!!");
errorln(uglify_code);
if (original_erred) {
errorln();
errorln();
errorln("original stacktrace:");
errorln(original_result);
}
} else {
errorln("// uglified code");
try_beautify(uglify_code, toplevel, uglify_result, errorln);
errorln();
@@ -2213,15 +2255,6 @@ function log(options) {
errorln(original_result);
errorln("uglified result:");
errorln(uglify_result);
} else {
errorln("// !!! uglify failed !!!");
errorln(uglify_code);
if (errored) {
errorln();
errorln();
errorln("original stacktrace:");
errorln(original_result);
}
}
errorln("//-------------------------------------------------------------");
if (!ok) {
@@ -2253,12 +2286,27 @@ function log(options) {
}
function sort_globals(code) {
var globals = run_code("throw Object.keys(this).sort(" + function(global) {
var injected = "throw Object.keys(this).sort(" + function(global) {
return function(m, n) {
return (typeof global[n] == "function") - (typeof global[m] == "function")
|| (m < n ? -1 : m > n ? 1 : 0);
};
} + "(this));\n" + code);
} + "(this));";
var save_async = async;
if (async && has_await) {
async = false;
injected = [
'"use strict";',
injected,
"(async function(){",
code,
"})();"
].join("\n");
} else {
injected += "\n" + code;
}
var globals = run_code(injected);
async = save_async;
if (!Array.isArray(globals)) {
errorln();
errorln();
@@ -2318,20 +2366,28 @@ function is_error_in(ex) {
return ex.name == "TypeError" && /'in'/.test(ex.message);
}
function is_error_tdz(ex) {
return ex.name == "ReferenceError";
}
function is_error_spread(ex) {
return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| not a function/.test(ex.message);
return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| not a function|Symbol\(Symbol\.iterator\)/.test(ex.message);
}
function is_error_recursion(ex) {
return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message);
}
function is_error_set_property(ex) {
return ex.name == "TypeError" && /^Cannot set propert[\s\S]+? of (null|undefined)/.test(ex.message);
}
function is_error_redeclaration(ex) {
return ex.name == "SyntaxError" && /already been declared|redeclaration/.test(ex.message);
}
function is_error_destructuring(ex) {
return ex.name == "TypeError" && /^Cannot destructure /.test(ex.message);
return ex.name == "TypeError" && /^Cannot (destructure|read propert)/.test(ex.message);
}
function is_error_class_constructor(ex) {
@@ -2345,6 +2401,8 @@ function is_error_getter_only_property(ex) {
}
function patch_try_catch(orig, toplevel) {
var patched = Object.create(null);
var patches = [];
var stack = [ {
code: orig,
index: 0,
@@ -2382,7 +2440,7 @@ function patch_try_catch(orig, toplevel) {
"throw " + match[1] + ";",
].join("\n");
}
var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw;
var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw + "var UFUZZ_ERROR;";
var result = run_code(new_code, toplevel);
if (!sandbox.is_error(result)) {
if (!stack.filled && match[1]) stack.push({
@@ -2394,27 +2452,35 @@ function patch_try_catch(orig, toplevel) {
offset += insert.length;
code = new_code;
} else if (is_error_in(result)) {
index = result.ufuzz_catch;
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("invalid `in`");' + orig.slice(index);
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("invalid `in`");');
} else if (is_error_tdz(result)) {
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("TDZ");');
} else if (is_error_spread(result)) {
index = result.ufuzz_catch;
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("spread not iterable");' + orig.slice(index);
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("spread not iterable");');
} else if (is_error_recursion(result)) {
index = result.ufuzz_try;
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
patch(result.ufuzz_try, 'throw new Error("skipping infinite recursion");');
} else if (is_error_set_property(result)) {
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("cannot set property");');
} 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);
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("cannot destructure");');
} else if (is_error_class_constructor(result)) {
index = result.ufuzz_catch;
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("missing new for class");' + orig.slice(index);
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("missing new for class");');
} else if (is_error_getter_only_property(result)) {
index = result.ufuzz_catch;
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("setting getter-only property");' + orig.slice(index);
patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("setting getter-only property");');
}
}
stack.filled = true;
}
if (patches.length) return patches.reduce(function(code, patch) {
var index = patch[0];
return code.slice(0, index) + patch[1] + code.slice(index);
}, orig);
function patch(index, code) {
if (patched[index]) return;
patched[index] = true;
patches.unshift([ index, code ]);
}
}
var beautify_options = {
@@ -2426,22 +2492,26 @@ var beautify_options = {
},
};
var minify_options = require("./options.json");
if (typeof sandbox.run_code("A:if (0) B:; else B:;") != "string") {
if (sandbox.is_error(sandbox.run_code("A:if (0) B:; else B:;"))) {
minify_options.forEach(function(o) {
if (!("mangle" in o)) o.mangle = {};
if (o.mangle) o.mangle.v8 = true;
});
}
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 && sandbox.is_error(sandbox.run_code("async (a = f(...[], b)) => 0;"))) {
bug_async_arrow_rest = function(ex) {
return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter";
};
}
var bug_async_class_await = SUPPORT.async && SUPPORT.class_field && typeof sandbox.run_code("var await; async function f() { class A { static p = await; } }") != "string";
var bug_for_of_async = SUPPORT.for_await_of && typeof sandbox.run_code("var async; for (async of []);") != "string";
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") {
var bug_class_static_await = SUPPORT.async && SUPPORT.class_field && sandbox.is_error(sandbox.run_code("var await; class A { static p = await; }"));
var bug_class_static_nontrivial = SUPPORT.class_field && sandbox.is_error(sandbox.run_code("class A { static 42; static get 42() {} }"));
var bug_for_of_async = SUPPORT.for_await_of && sandbox.is_error(sandbox.run_code("var async; for (async of []);"));
var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && sandbox.is_error(sandbox.run_code("try {} catch (e) { for (var e of []); }"));
var bug_proto_stream = function(ex) {
return ex.name == "TypeError" && ex.message == "callback is not a function";
}
if (SUPPORT.destructuring && sandbox.is_error(sandbox.run_code("console.log([ 1 ], {} = 2);"))) {
beautify_options.output.v8 = true;
minify_options.forEach(function(o) {
if (!("output" in o)) o.output = {};
@@ -2450,7 +2520,7 @@ if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2)
}
beautify_options = JSON.stringify(beautify_options);
minify_options = minify_options.map(JSON.stringify);
var original_code, original_result, errored;
var original_code, original_result, original_erred;
var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
@@ -2458,7 +2528,7 @@ for (var round = 1; round <= num_iterations; round++) {
original_code = createTopLevelCode();
var orig_result = [ run_code(original_code), run_code(original_code, true) ];
if (orig_result.some(function(result, toplevel) {
if (typeof result == "string") return;
if (!sandbox.is_error(result)) return;
println();
println();
println("//=============================================================");
@@ -2471,28 +2541,37 @@ for (var round = 1; round <= num_iterations; round++) {
println();
// ignore v8 parser bug
return bug_async_arrow_rest(result)
// ignore Node.js `__proto__` quirks
|| bug_proto_stream(result)
// ignore runtime platform bugs
|| result.message == "Script execution aborted.";
})) continue;
minify_options.forEach(function(options) {
var o = JSON.parse(options);
if (async && has_await) {
o.module = true;
options = JSON.stringify(o);
}
var toplevel = sandbox.has_toplevel(o);
o.validate = true;
uglify_code = UglifyJS.minify(original_code, o);
original_result = orig_result[toplevel ? 1 : 0];
errored = typeof original_result != "string";
original_erred = sandbox.is_error(original_result);
if (!uglify_code.error) {
uglify_code = uglify_code.code;
uglify_result = run_code(uglify_code, toplevel);
ok = sandbox.same_stdout(original_result, uglify_result);
var uglify_erred = sandbox.is_error(uglify_result);
// ignore v8 parser bug
if (!ok && bug_async_arrow_rest(uglify_result)) ok = true;
if (!ok && uglify_erred && bug_async_arrow_rest(uglify_result)) ok = true;
// ignore Node.js `__proto__` quirks
if (!ok && uglify_erred && bug_proto_stream(uglify_result)) ok = true;
// ignore runtime platform bugs
if (!ok && uglify_result.message == "Script execution aborted.") ok = true;
if (!ok && uglify_erred && uglify_result.message == "Script execution aborted.") ok = true;
// handle difference caused by time-outs
if (!ok) {
if (errored && is_error_timeout(original_result)) {
if (is_error_timeout(uglify_result)) {
if (original_erred && is_error_timeout(original_result)) {
if (uglify_erred && is_error_timeout(uglify_result)) {
// ignore difference in error message
ok = true;
} else {
@@ -2500,21 +2579,23 @@ for (var round = 1; round <= num_iterations; round++) {
if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = run_code(original_code, toplevel, 10000);
ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result);
}
} else if (is_error_timeout(uglify_result)) {
} else if (uglify_erred && is_error_timeout(uglify_result)) {
// ignore spurious time-outs
var waited_result = run_code(uglify_code, toplevel, 10000);
ok = sandbox.same_stdout(original_result, waited_result);
}
}
// ignore declaration order of global variables
if (!ok && !toplevel && uglify_result.name != "SyntaxError" && original_result.name != "SyntaxError") {
ok = sandbox.same_stdout(run_code(sort_globals(original_code)), run_code(sort_globals(uglify_code)));
if (!ok && !toplevel) {
if (!(original_erred && original_result.name == "SyntaxError") && !(uglify_erred && uglify_result.name == "SyntaxError")) {
ok = sandbox.same_stdout(run_code(sort_globals(original_code)), run_code(sort_globals(uglify_code)));
}
}
// ignore numerical imprecision caused by `unsafe_math`
if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == typeof uglify_result) {
if (typeof original_result == "string") {
if (!ok && o.compress && o.compress.unsafe_math) {
if (typeof original_result == "string" && typeof uglify_result == "string") {
ok = fuzzy_match(original_result, uglify_result);
} else if (sandbox.is_error(original_result)) {
} else if (original_erred && uglify_erred) {
ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message);
}
if (!ok) {
@@ -2522,46 +2603,33 @@ for (var round = 1; round <= num_iterations; round++) {
ok = sandbox.same_stdout(fuzzy_result, uglify_result);
}
}
// ignore difference in error message caused by Temporal Dead Zone
if (!ok && errored && uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError") ok = true;
// ignore difference due to implicit strict-mode in `class`
if (!ok && /\bclass\b/.test(original_code)) {
var original_strict = run_code('"use strict";\n' + original_code, toplevel);
if (/^(Syntax|Type)Error$/.test(uglify_result.name)) {
ok = typeof original_strict != "string";
} else {
ok = sandbox.same_stdout(original_strict, uglify_result);
}
}
// ignore difference in error message caused by `import` symbol redeclaration
if (!ok && errored && /\bimport\b/.test(original_code)) {
if (is_error_redeclaration(uglify_result) && is_error_redeclaration(original_result)) ok = true;
}
if (!ok && original_erred && uglify_erred && (
// ignore difference in error message caused by `in`
is_error_in(original_result) && is_error_in(uglify_result)
// ignore difference in error message caused by Temporal Dead Zone
|| is_error_tdz(original_result) && is_error_tdz(uglify_result)
// ignore difference in error message caused by spread syntax
|| is_error_spread(original_result) && is_error_spread(uglify_result)
// ignore difference in error message caused by destructuring assignment
|| is_error_set_property(original_result) && is_error_set_property(uglify_result)
// ignore difference in error message caused by `import` symbol redeclaration
|| /\bimport\b/.test(original_code) && is_error_redeclaration(original_result) && is_error_redeclaration(uglify_result)
// ignore difference in error message caused by destructuring
|| is_error_destructuring(original_result) && is_error_destructuring(uglify_result)
// ignore difference in error message caused by call on class
|| is_error_class_constructor(original_result) && is_error_class_constructor(uglify_result)
// ignore difference in error message caused by setting getter-only property
|| is_error_getter_only_property(original_result) && is_error_getter_only_property(uglify_result)
)) ok = true;
// ignore difference due to `__proto__` assignment
if (!ok && /\b__proto__\b/.test(original_code)) {
var original_proto = run_code("(" + patch_proto + ")();\n" + original_code, toplevel);
var uglify_proto = run_code("(" + patch_proto + ")();\n" + uglify_code, toplevel);
ok = sandbox.same_stdout(original_proto, uglify_proto);
}
// ignore difference in error message caused by `in`
if (!ok && errored && is_error_in(uglify_result) && is_error_in(original_result)) ok = true;
// ignore difference in error message caused by spread syntax
if (!ok && errored && is_error_spread(uglify_result) && is_error_spread(original_result)) ok = true;
// ignore difference in depth of termination caused by infinite recursion
if (!ok && errored && is_error_recursion(original_result)) {
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 difference in error message caused by call on class
if (!ok && errored && is_error_class_constructor(uglify_result) && is_error_class_constructor(original_result)) {
ok = true;
}
// ignore difference in error message caused by setting getter-only property
if (!ok && errored && is_error_getter_only_property(uglify_result) && is_error_getter_only_property(original_result)) {
ok = true;
if (!ok && original_erred && is_error_recursion(original_result)) {
if (!uglify_erred || is_error_recursion(uglify_result)) ok = true;
}
// ignore errors above when caught by try-catch
if (!ok) {
@@ -2573,7 +2641,7 @@ for (var round = 1; round <= num_iterations; round++) {
}
} else {
uglify_code = uglify_code.error;
ok = errored && uglify_code.name == original_result.name;
ok = original_erred && uglify_code.name == original_result.name;
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
if (!ok && isFinite(num_iterations)) {