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 fail-fast: false
matrix: matrix:
include: include:
- node: latest - node: '16'
os: macos-latest os: macos-latest
- node: '8' - node: '12'
os: ubuntu-latest os: ubuntu-latest
- node: '8' - node: '8'
os: ubuntu-latest os: ubuntu-latest
- node: '8' - node: '12'
os: windows-latest os: windows-latest
- node: '8' - node: '8'
os: windows-latest 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-fargs Do not mangle/drop function arguments.
--keep-fnames Do not mangle/drop function names. Useful for --keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name. code relying on Function.prototype.name.
--module Process input as ES module (implies --toplevel)
--name-cache <file> File to hold mangled name mappings. --name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS) --self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options: --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. --warn Print warning messages.
--webkit Support non-standard Safari/Webkit. --webkit Support non-standard Safari/Webkit.
Equivalent to setting `webkit: true` in `minify()` 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. By default UglifyJS will not try to be Safari-proof.
--wrap <name> Embed everything in a big function, making the --wrap <name> Embed everything in a big function, making the
“exports” and “global” variables available. You “exports” and “global” variables available. You
@@ -517,6 +518,10 @@ if (result.error) throw result.error;
- `mangle.properties` (default: `false`) — a subcategory of the mangle option. - `mangle.properties` (default: `false`) — a subcategory of the mangle option.
Pass an object to specify custom [mangle property options](#mangle-properties-options). 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 - `nameCache` (default: `null`) — pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is 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 - `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 - `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. - `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" - `negate_iife` (default: `true`) — negate "Immediately-Called Function Expressions"
where the return value is discarded, to avoid the parens that the where the return value is discarded, to avoid the parens that the
code generator would insert. 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` (default: `false`) — apply "unsafe" transformations (discussion below)
- `unsafe_comps` (default: `false`) — compress expressions like `a <= b` assuming - `unsafe_comps` (default: `false`) — assume operands cannot be (coerced to) `NaN`
none of the operands can 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)` - `unsafe_Function` (default: `false`) — compress and mangle `Function(args, code)`
when both `args` and `code` are string literals. 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: - Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript ```javascript
var await; var await;
async function f() { class A {
class A { static p = await;
static p = await;
}
} }
// SyntaxError: Unexpected reserved word // 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'. // 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. 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: following:
```javascript ```javascript
console.log({ console.log({
@@ -1355,9 +1368,15 @@ To allow for better optimizations, the compiler makes various assumptions:
return "FAIL"; return "FAIL";
}, },
[42]: "PASS", [42]: "PASS",
}[42], {
...console,
get 42() {
return "FAIL";
},
42: "PASS",
}[42]); }[42]);
// Expected: "PASS" // Expected: "PASS PASS"
// Actual: "FAIL" // Actual: "PASS FAIL"
``` ```
UglifyJS may modify the input which in turn may suppress those errors. UglifyJS may modify the input which in turn may suppress those errors.
- Earlier versions of JavaScript will throw `TypeError` with the following: - 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 // TypeError: const 'a' has already been declared
``` ```
UglifyJS may modify the input which in turn may suppress those errors. 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.", " --ie Support non-standard Internet Explorer.",
" --keep-fargs Do not mangle/drop function 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.", " --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.", " --name-cache <file> File to hold mangled name mappings.",
" --rename Force symbol expansion.", " --rename Force symbol expansion.",
" --no-rename Disable symbol expansion.", " --no-rename Disable symbol expansion.",
@@ -152,6 +153,7 @@ function process_option(name, no_value) {
case "annotations": case "annotations":
case "ie": case "ie":
case "ie8": case "ie8":
case "module":
case "timings": case "timings":
case "toplevel": case "toplevel":
case "v8": case "v8":

View File

@@ -109,6 +109,9 @@ var AST_Node = DEFNODE("Node", "start end", {
start: "[AST_Token] The first token of this node", start: "[AST_Token] The first token of this node",
end: "[AST_Token] The last 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) { walk: function(visitor) {
visitor.visit(this); visitor.visit(this);
}, },
@@ -138,6 +141,7 @@ var AST_Node = DEFNODE("Node", "start end", {
}, null); }, null);
DEF_BITPROPS(AST_Node, [ DEF_BITPROPS(AST_Node, [
// AST_Node
"_optimized", "_optimized",
"_squeezed", "_squeezed",
// AST_Call // AST_Call
@@ -172,6 +176,8 @@ DEF_BITPROPS(AST_Node, [
"pure", "pure",
// AST_Assign // AST_Assign
"redundant", "redundant",
// AST_Node
"single_use",
// AST_ClassProperty // AST_ClassProperty
"static", "static",
// AST_Call // AST_Call
@@ -231,6 +237,24 @@ AST_Node.disable_validation = function() {
while (restore = restore_transforms.pop()) restore(); 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 ]----- */ /* -----[ statements ]----- */
var AST_Statement = DEFNODE("Statement", null, { var AST_Statement = DEFNODE("Statement", null, {
@@ -242,6 +266,7 @@ var AST_Statement = DEFNODE("Statement", null, {
var AST_Debugger = DEFNODE("Debugger", null, { var AST_Debugger = DEFNODE("Debugger", null, {
$documentation: "Represents a debugger statement", $documentation: "Represents a debugger statement",
_equals: return_true,
}, AST_Statement); }, AST_Statement);
var AST_Directive = DEFNODE("Directive", "quote value", { var AST_Directive = DEFNODE("Directive", "quote value", {
@@ -250,6 +275,9 @@ var AST_Directive = DEFNODE("Directive", "quote value", {
quote: "[string?] the original quote character", quote: "[string?] the original quote character",
value: "[string] The value of this directive as a plain string (it's not an AST_String!)", 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() { _validate: function() {
if (this.quote != null) { if (this.quote != null) {
if (typeof this.quote != "string") throw new Error("quote must be string"); 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); }, AST_Statement);
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { 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); }, AST_Statement);
function is_statement(node) { function is_statement(node) {
@@ -291,6 +320,9 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
$propdoc: { $propdoc: {
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)", body: "[AST_Node] an expression node (should not be instanceof AST_Statement)",
}, },
_equals: function(node) {
return this.body.equals(node.body);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -342,6 +374,9 @@ var AST_Block = DEFNODE("Block", "body", {
$propdoc: { $propdoc: {
body: "[AST_Statement*] an array of statements" body: "[AST_Statement*] an array of statements"
}, },
_equals: function(node) {
return all_equals(this.body, node.body);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -376,6 +411,10 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
$propdoc: { $propdoc: {
label: "[AST_Label] a label definition" label: "[AST_Label] a label definition"
}, },
_equals: function(node) {
return this.label.equals(node.label)
&& this.body.equals(node.body);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -417,6 +456,10 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", {
$propdoc: { $propdoc: {
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" 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() { _validate: function() {
if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop"); if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop");
must_be_expression(this, "condition"); must_be_expression(this, "condition");
@@ -431,7 +474,7 @@ var AST_Do = DEFNODE("Do", null, {
node.body.walk(visitor); node.body.walk(visitor);
node.condition.walk(visitor); node.condition.walk(visitor);
}); });
} },
}, AST_DWLoop); }, AST_DWLoop);
var AST_While = DEFNODE("While", null, { var AST_While = DEFNODE("While", null, {
@@ -442,7 +485,7 @@ var AST_While = DEFNODE("While", null, {
node.condition.walk(visitor); node.condition.walk(visitor);
node.body.walk(visitor); node.body.walk(visitor);
}); });
} },
}, AST_DWLoop); }, AST_DWLoop);
var AST_For = DEFNODE("For", "init condition step", { 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", condition: "[AST_Node?] the `for` termination clause, or null if empty",
step: "[AST_Node?] the `for` update 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -479,6 +528,11 @@ var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", {
init: "[AST_Node] the assignment target during iteration", init: "[AST_Node] the assignment target during iteration",
object: "[AST_Node] the object to iterate over" 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -519,6 +573,10 @@ var AST_With = DEFNODE("With", "expression", {
$propdoc: { $propdoc: {
expression: "[AST_Node] the `with` expression" expression: "[AST_Node] the `with` expression"
}, },
_equals: function(node) {
return this.expression.equals(node.expression)
&& this.body.equals(node.body);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -534,7 +592,7 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */ /* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "fn_defs may_call_this uses_eval uses_with", { 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: { $propdoc: {
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", 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", uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
@@ -592,6 +650,10 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
} }
}, AST_Scope); }, 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", { var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_arguments", {
$documentation: "Base class for functions", $documentation: "Base class for functions",
$propdoc: { $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); 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { 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", extends: "[AST_Node?] the super class, or null if not specified",
properties: "[AST_ClassProperty*] array of class properties", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -871,11 +948,17 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, {
var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", { var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
$documentation: "Base class for `class` properties", $documentation: "Base class for `class` properties",
$propdoc: { $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", private: "[boolean] whether this is a private property",
static: "[boolean] whether this is a static 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)", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -885,7 +968,9 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
}, },
_validate: function() { _validate: function() {
if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty"); 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"); if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
must_be_expression(this, "key"); must_be_expression(this, "key");
} }
@@ -925,6 +1010,17 @@ var AST_ClassMethod = DEFNODE("ClassMethod", null, {
}, },
}, AST_ClassProperty); }, 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 ]----- */ /* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, { var AST_Jump = DEFNODE("Jump", null, {
@@ -939,6 +1035,9 @@ var AST_Exit = DEFNODE("Exit", "value", {
$propdoc: { $propdoc: {
value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -969,6 +1068,9 @@ var AST_LoopControl = DEFNODE("LoopControl", "label", {
$propdoc: { $propdoc: {
label: "[AST_LabelRef?] the label, or null if none", label: "[AST_LabelRef?] the label, or null if none",
}, },
_equals: function(node) {
return prop_equals(this.label, node.label);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -999,6 +1101,11 @@ var AST_If = DEFNODE("If", "condition alternative", {
condition: "[AST_Node] the `if` condition", condition: "[AST_Node] the `if` condition",
alternative: "[AST_Statement?] the `else` part, or null if not present" 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1022,6 +1129,10 @@ var AST_Switch = DEFNODE("Switch", "expression", {
$propdoc: { $propdoc: {
expression: "[AST_Node] the `switch` “discriminant”" expression: "[AST_Node] the `switch` “discriminant”"
}, },
_equals: function(node) {
return this.expression.equals(node.expression)
&& all_equals(this.body, node.body);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1053,6 +1164,10 @@ var AST_Case = DEFNODE("Case", "expression", {
$propdoc: { $propdoc: {
expression: "[AST_Node] the `case` expression" expression: "[AST_Node] the `case` expression"
}, },
_equals: function(node) {
return this.expression.equals(node.expression)
&& all_equals(this.body, node.body);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { 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", bcatch: "[AST_Catch?] the catch block, or null if not present",
bfinally: "[AST_Finally?] the finally 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1096,6 +1216,10 @@ var AST_Catch = DEFNODE("Catch", "argname", {
$propdoc: { $propdoc: {
argname: "[(AST_Destructured|AST_SymbolCatch)?] symbol for the exception, or null if not present", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1121,6 +1245,9 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
$propdoc: { $propdoc: {
definitions: "[AST_VarDef*] array of variable definitions" definitions: "[AST_VarDef*] array of variable definitions"
}, },
_equals: function(node) {
return all_equals(this.definitions, node.definitions);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1177,6 +1304,10 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
name: "[AST_Destructured|AST_SymbolVar] name of the variable", name: "[AST_Destructured|AST_SymbolVar] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1196,6 +1327,9 @@ var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", {
$propdoc: { $propdoc: {
body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export", body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export",
}, },
_equals: function(node) {
return this.body.equals(node.body);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1216,6 +1350,9 @@ var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
$propdoc: { $propdoc: {
body: "[AST_Node] the default export", body: "[AST_Node] the default export",
}, },
_equals: function(node) {
return this.body.equals(node.body);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1229,29 +1366,29 @@ var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
}, },
}, AST_Statement); }, AST_Statement);
var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path quote", { var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path", {
$documentation: "An `export ... from '...'` statement", $documentation: "An `export ... from '...'` statement",
$propdoc: { $propdoc: {
aliases: "[string*] array of aliases to export", aliases: "[AST_String*] array of aliases to export",
keys: "[string*] array of keys to import", keys: "[AST_String*] array of keys to import",
path: "[string] the path to import module", path: "[AST_String] the path to import module",
quote: "[string?] the original quote character", },
_equals: function(node) {
return this.path.equals(node.path)
&& all_equals(this.aliases, node.aliases)
&& all_equals(this.keys, node.keys);
}, },
_validate: function() { _validate: function() {
if (this.aliases.length != this.keys.length) { if (this.aliases.length != this.keys.length) {
throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length); throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length);
} }
this.aliases.forEach(function(name) { 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) { 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.path instanceof AST_String)) throw new Error("path must be AST_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);
}
}, },
}, AST_Statement); }, AST_Statement);
@@ -1260,6 +1397,9 @@ var AST_ExportReferences = DEFNODE("ExportReferences", "properties", {
$propdoc: { $propdoc: {
properties: "[AST_SymbolExport*] array of aliases to export", properties: "[AST_SymbolExport*] array of aliases to export",
}, },
_equals: function(node) {
return all_equals(this.properties, node.properties);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1275,14 +1415,20 @@ var AST_ExportReferences = DEFNODE("ExportReferences", "properties", {
}, },
}, AST_Statement); }, 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", $documentation: "An `import` statement",
$propdoc: { $propdoc: {
all: "[AST_SymbolImport?] the imported namespace, or null if not specified", all: "[AST_SymbolImport?] the imported namespace, or null if not specified",
default: "[AST_SymbolImport?] the alias for default `export`, 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", 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) { walk: function(visitor) {
var node = this; var node = this;
@@ -1301,16 +1447,12 @@ var AST_Import = DEFNODE("Import", "all default path properties quote", {
} }
if (this.default != null) { if (this.default != null) {
if (!(this.default instanceof AST_SymbolImport)) throw new Error("default must be AST_SymbolImport"); 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 (this.properties != null) this.properties.forEach(function(node) {
if (!(node instanceof AST_SymbolImport)) throw new Error("properties must contain AST_SymbolImport"); 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); }, AST_Statement);
@@ -1320,6 +1462,10 @@ var AST_DefaultValue = DEFNODE("DefaultValue", "name value", {
name: "[AST_Destructured|AST_SymbolDeclaration] name of the variable", name: "[AST_Destructured|AST_SymbolDeclaration] name of the variable",
value: "[AST_Node] value to assign if variable is `undefined`", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { 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", pure: "[boolean/S] marker for side-effect-free call expression",
terminal: "[boolean] whether the chain has ended", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1375,6 +1526,9 @@ var AST_Sequence = DEFNODE("Sequence", "expressions", {
$propdoc: { $propdoc: {
expressions: "[AST_Node*] array of expressions (at least two)" expressions: "[AST_Node*] array of expressions (at least two)"
}, },
_equals: function(node) {
return all_equals(this.expressions, node.expressions);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { 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", 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", 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() { get_property: function() {
var p = this.property; var p = this.property;
if (p instanceof AST_Constant) return p.value; if (p instanceof AST_Constant) return p.value;
@@ -1446,6 +1605,9 @@ var AST_Spread = DEFNODE("Spread", "expression", {
$propdoc: { $propdoc: {
expression: "[AST_Node] expression to be expanded", expression: "[AST_Node] expression to be expanded",
}, },
_equals: function(node) {
return this.expression.equals(node.expression);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1463,6 +1625,10 @@ var AST_Unary = DEFNODE("Unary", "operator expression", {
operator: "[string] the operator", operator: "[string] the operator",
expression: "[AST_Node] expression that this unary operator applies to" 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1491,6 +1657,11 @@ var AST_Binary = DEFNODE("Binary", "operator left right", {
operator: "[string] the operator", operator: "[string] the operator",
right: "[AST_Node] right-hand side expression" 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1512,6 +1683,11 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative",
consequent: "[AST_Node]", consequent: "[AST_Node]",
alternative: "[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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1553,6 +1729,9 @@ var AST_Await = DEFNODE("Await", "expression", {
$propdoc: { $propdoc: {
expression: "[AST_Node] expression with Promise to resolve on", expression: "[AST_Node] expression with Promise to resolve on",
}, },
_equals: function(node) {
return this.expression.equals(node.expression);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { 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", expression: "[AST_Node?] return value for iterator, or null if undefined",
nested: "[boolean] whether to iterate over expression as generator", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1592,6 +1775,9 @@ var AST_Array = DEFNODE("Array", "elements", {
$propdoc: { $propdoc: {
elements: "[AST_Node*] array of elements" elements: "[AST_Node*] array of elements"
}, },
_equals: function(node) {
return all_equals(this.elements, node.elements);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1634,6 +1820,10 @@ var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
$propdoc: { $propdoc: {
elements: "[(AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)*] array of elements", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { 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.", 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", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1672,6 +1866,10 @@ var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", {
$propdoc: { $propdoc: {
properties: "[AST_DestructuredKeyVal*] array of properties", 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) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { visitor.visit(node, function() {
@@ -1693,6 +1891,9 @@ var AST_Object = DEFNODE("Object", "properties", {
$propdoc: { $propdoc: {
properties: "[(AST_ObjectProperty|AST_Spread)*] array of properties" properties: "[(AST_ObjectProperty|AST_Spread)*] array of properties"
}, },
_equals: function(node) {
return all_equals(this.properties, node.properties);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { 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.", key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.", value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.",
}, },
_equals: function(node) {
return prop_equals(this.key, node.key)
&& this.value.equals(node.value);
},
walk: function(visitor) { walk: function(visitor) {
var node = this; var node = this;
visitor.visit(node, function() { 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)", scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
thedef: "[SymbolDef/S] the definition of this symbol" thedef: "[SymbolDef/S] the definition of this symbol"
}, },
_equals: function(node) {
return this.thedef ? this.thedef === node.thedef : this.name == node.name;
},
_validate: function() { _validate: function() {
if (this.TYPE == "Symbol") throw new Error("should not instantiate AST_Symbol"); if (this.TYPE == "Symbol") throw new Error("should not instantiate AST_Symbol");
if (typeof this.name != "string") throw new Error("name must be string"); if (typeof this.name != "string") throw new Error("name must be string");
@@ -1787,10 +1995,14 @@ var AST_SymbolConst = DEFNODE("SymbolConst", null, {
var AST_SymbolImport = DEFNODE("SymbolImport", "key", { var AST_SymbolImport = DEFNODE("SymbolImport", "key", {
$documentation: "Symbol defined by an `import` statement", $documentation: "Symbol defined by an `import` statement",
$propdoc: { $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() { _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); }, AST_SymbolConst);
@@ -1834,7 +2046,7 @@ var AST_Label = DEFNODE("Label", "references", {
initialize: function() { initialize: function() {
this.references = []; this.references = [];
this.thedef = this; this.thedef = this;
} },
}, AST_Symbol); }, AST_Symbol);
var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", { 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", { var AST_SymbolExport = DEFNODE("SymbolExport", "alias", {
$documentation: "Reference in an `export` statement", $documentation: "Reference in an `export` statement",
$propdoc: { $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() { _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); }, AST_SymbolRef);
@@ -1857,6 +2073,7 @@ var AST_LabelRef = DEFNODE("LabelRef", null, {
var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, { var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, {
$documentation: "Base class for `super` & `this`", $documentation: "Base class for `super` & `this`",
_equals: return_true,
_validate: function() { _validate: function() {
if (this.TYPE == "ObjectIdentity") throw new Error("should not instantiate AST_ObjectIdentity"); 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: { $propdoc: {
expressions: "[AST_Node*] the placeholder expressions", expressions: "[AST_Node*] the placeholder expressions",
strings: "[string*] the raw text segments", 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) { walk: function(visitor) {
var node = this; var node = this;
@@ -1916,6 +2138,9 @@ var AST_Template = DEFNODE("Template", "expressions strings tag", {
var AST_Constant = DEFNODE("Constant", null, { var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants", $documentation: "Base class for all constants",
_equals: function(node) {
return this.value === node.value;
},
_validate: function() { _validate: function() {
if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant"); if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant");
}, },
@@ -1964,6 +2189,9 @@ var AST_RegExp = DEFNODE("RegExp", "value", {
$propdoc: { $propdoc: {
value: "[RegExp] the actual regexp" value: "[RegExp] the actual regexp"
}, },
_equals: function(node) {
return "" + this.value == "" + node.value;
},
_validate: function() { _validate: function() {
if (!(this.value instanceof RegExp)) throw new Error("value must be RegExp"); 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, { var AST_Atom = DEFNODE("Atom", null, {
$documentation: "Base class for atoms", $documentation: "Base class for atoms",
_equals: return_true,
_validate: function() { _validate: function() {
if (this.TYPE == "Atom") throw new Error("should not instantiate AST_Atom"); 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)]; return this.stack[this.stack.length - 2 - (n || 0)];
}, },
push: function(node) { 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); 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); this.stack.push(node);
}, },
pop: function() { pop: function() {
var node = this.stack.pop(); 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); 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_fargs: false,
keep_fnames: false, keep_fnames: false,
mangle: {}, mangle: {},
module: false,
nameCache: null, nameCache: null,
output: {}, output: {},
parse: {}, parse: {},
rename: undefined, rename: undefined,
sourceMap: false, sourceMap: false,
timings: false, timings: false,
toplevel: false, toplevel: !!(options && options["module"]),
v8: false, v8: false,
validate: false, validate: false,
warnings: false, warnings: false,
@@ -96,15 +97,15 @@ function minify(files, options) {
}, true); }, true);
if (options.validate) AST_Node.enable_validation(); if (options.validate) AST_Node.enable_validation();
var timings = options.timings && { start: Date.now() }; 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.annotations !== undefined) set_shorthand("annotations", options, [ "compress", "output" ]);
if (options.ie8) options.ie = options.ie || options.ie8; if (options.ie8) options.ie = options.ie || options.ie8;
if (options.ie) set_shorthand("ie", 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" ]); if (options.keep_fargs) set_shorthand("keep_fargs", options, [ "compress", "mangle", "rename" ]);
if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle", "rename" ]);
if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle" ]); if (options.module) set_shorthand("module", options, [ "compress", "parse" ]);
if (options.v8) set_shorthand("v8", options, [ "mangle", "output" ]); if (options.toplevel) set_shorthand("toplevel", options, [ "compress", "mangle", "rename" ]);
if (options.webkit) set_shorthand("webkit", options, [ "compress", "mangle", "output" ]); if (options.v8) set_shorthand("v8", options, [ "mangle", "output", "rename" ]);
if (options.webkit) set_shorthand("webkit", options, [ "compress", "mangle", "output", "rename" ]);
var quoted_props; var quoted_props;
if (options.mangle) { if (options.mangle) {
options.mangle = defaults(options.mangle, { options.mangle = defaults(options.mangle, {
@@ -135,6 +136,7 @@ function minify(files, options) {
init_cache(options.mangle.cache); init_cache(options.mangle.cache);
init_cache(options.mangle.properties.cache); init_cache(options.mangle.properties.cache);
} }
if (options.rename === undefined) options.rename = options.compress && options.mangle;
if (options.sourceMap) { if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, { options.sourceMap = defaults(options.sourceMap, {
content: null, content: null,
@@ -190,8 +192,8 @@ function minify(files, options) {
if (options.validate) toplevel.validate_ast(); if (options.validate) toplevel.validate_ast();
if (timings) timings.rename = Date.now(); if (timings) timings.rename = Date.now();
if (options.rename) { if (options.rename) {
toplevel.figure_out_scope(options.mangle); toplevel.figure_out_scope(options.rename);
toplevel.expand_names(options.mangle); toplevel.expand_names(options.rename);
} }
if (timings) timings.compress = Date.now(); if (timings) timings.compress = Date.now();
if (options.compress) { if (options.compress) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1018,3 +1018,197 @@ issue_5356: {
expect_stdout: "NaN" expect_stdout: "NaN"
node_version: ">=4" 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" 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: { collapse_property_lambda: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -1293,6 +1347,21 @@ functions_inner_var: {
node_version: ">=8" 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: { issue_4335_1: {
options = { options = {
inline: true, inline: true,
@@ -1748,8 +1817,8 @@ issue_4454_2: {
expect: { expect: {
function f(a) { function f(a) {
(async function(c = console.log(a)) {})(); (async function(c = console.log(a)) {})();
var a = 42..toString(); var b = 42..toString();
console.log(a); console.log(b);
} }
f("PASS"); f("PASS");
} }
@@ -2427,80 +2496,6 @@ issue_5023_2: {
node_version: ">=8" 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: { issue_5034: {
options = { options = {
functions: true, functions: true,
@@ -2959,3 +2954,278 @@ issue_5305_3: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=8" 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" 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" 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: { dont_change_in_or_instanceof_expressions: {
input: { input: {
1 in 1; 1 in 1;

View File

@@ -82,14 +82,14 @@ ifs_3_should_warn: {
"WARN: Boolean && always false [test/compress/conditionals.js:3,12]", "WARN: Boolean && always false [test/compress/conditionals.js:3,12]",
"WARN: Condition left of && always false [test/compress/conditionals.js:3,12]", "WARN: Condition left of && always false [test/compress/conditionals.js:3,12]",
"WARN: Condition always false [test/compress/conditionals.js:3,12]", "WARN: Condition always false [test/compress/conditionals.js:3,12]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:3,34]", "WARN: Dropping unreachable code [test/compress/conditionals.js:3,34]",
"WARN: + in boolean context always true [test/compress/conditionals.js:10,19]", "WARN: + in boolean context always true [test/compress/conditionals.js:10,19]",
"WARN: Boolean || always true [test/compress/conditionals.js:10,12]", "WARN: Boolean || always true [test/compress/conditionals.js:10,12]",
"WARN: Condition left of || always true [test/compress/conditionals.js:10,12]", "WARN: Condition left of || always true [test/compress/conditionals.js:10,12]",
"WARN: Condition always true [test/compress/conditionals.js:10,12]", "WARN: Condition always true [test/compress/conditionals.js:10,12]",
"WARN: Dropping unreachable code [test/compress/conditionals.js:12,15]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]",
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:10,12]", "WARN: Dropping side-effect-free statement [test/compress/conditionals.js:10,12]",
"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: { cond_1: {
options = { options = {
conditionals: true, conditionals: true,
@@ -1731,7 +1813,7 @@ issue_3576: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_3668: { issue_3668_1: {
options = { options = {
conditionals: true, conditionals: true,
if_return: true, if_return: true,
@@ -1748,6 +1830,38 @@ issue_3668: {
} }
console.log(f()); 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: { expect: {
function f() { function f() {
try { try {
@@ -1756,6 +1870,7 @@ issue_3668: {
} catch (e) { } catch (e) {
return "FAIL"; return "FAIL";
} }
FAIL;
} }
console.log(f()); console.log(f());
} }
@@ -2062,3 +2177,176 @@ issue_5334_2: {
} }
expect_stdout: "PASS" 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" 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: { merge_vars_1: {
options = { options = {
merge_vars: true, merge_vars: true,
@@ -579,6 +653,37 @@ dead_block_after_return: {
expect_stdout: true 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: { do_if_continue_1: {
options = { options = {
if_return: true, if_return: true,
@@ -1855,3 +1960,54 @@ issue_5338: {
} }
expect_stdout: true 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" 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" 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: { collapse_value_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -541,7 +561,7 @@ inline_side_effects_2: {
} }
expect: { expect: {
var a = 42; var a = 42;
[ [].e = --a ] = [ console ]; [ [][0] = --a ] = [ console ];
console.log(a); console.log(a);
} }
expect_stdout: "42" expect_stdout: "42"
@@ -1170,6 +1190,49 @@ mangle_arrow_2_toplevel: {
node_version: ">=6.9.3" 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: { issue_4444: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -1558,7 +1621,7 @@ issue_4502_4: {
(function(a, b = console.log("FAIL")) {})(..."" + console.log(42)); (function(a, b = console.log("FAIL")) {})(..."" + console.log(42));
} }
expect: { expect: {
[ , [].e = console.log("FAIL") ] = [ ..."" + console.log(42) ]; [ , [][0] = console.log("FAIL") ] = [ ..."" + console.log(42) ];
} }
expect_stdout: "42" expect_stdout: "42"
node_version: ">=6" node_version: ">=6"
@@ -2183,7 +2246,7 @@ issue_5340_2: {
} }
expect: { expect: {
var a; var a;
[ [].e = 0 ] = [ ({ p: a } = true).q ]; [ [][0] = 0 ] = [ ({ p: a } = true).q ];
console.log(a); console.log(a);
} }
expect_stdout: "undefined" expect_stdout: "undefined"
@@ -2209,3 +2272,747 @@ issue_5340_3: {
expect_stdout: "undefined" expect_stdout: "undefined"
node_version: ">=6" 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" 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: { funarg_reduce_vars_1: {
options = { options = {
reduce_vars: true, reduce_vars: true,
@@ -1848,8 +1935,8 @@ issue_4294: {
}) {}({ }) {}({
[a]: 0, [a]: 0,
}); });
var a = A; var b = A;
console.log(a); console.log(b);
})(); })();
} }
expect_stdout: "PASS" expect_stdout: "PASS"
@@ -3020,6 +3107,7 @@ issue_5074_method_pure_getters: {
issue_5085_1: { issue_5085_1: {
options = { options = {
evaluate: true, evaluate: true,
passes: 2,
reduce_vars: true, reduce_vars: true,
toplevel: true, toplevel: true,
unsafe: true, unsafe: true,
@@ -3032,8 +3120,7 @@ issue_5085_1: {
} }
expect: { expect: {
var a = "PASS"; var a = "PASS";
var b = [ 42 ][0]; 42;
b;
console.log(a); console.log(a);
} }
expect_stdout: "PASS" expect_stdout: "PASS"
@@ -3043,6 +3130,7 @@ issue_5085_1: {
issue_5085_2: { issue_5085_2: {
options = { options = {
evaluate: true, evaluate: true,
passes: 2,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
unsafe: true, unsafe: true,
@@ -3059,7 +3147,7 @@ issue_5085_2: {
expect: { expect: {
var a = "PASS"; var a = "PASS";
(function(b) { (function(b) {
b = [ 42 ][0]; 0;
})(); })();
console.log(a); console.log(a);
} }
@@ -3400,7 +3488,7 @@ issue_5222: {
node_version: ">=6" node_version: ">=6"
} }
issue_5288: { issue_5288_1: {
options = { options = {
conditionals: true, conditionals: true,
inline: true, inline: true,
@@ -3420,7 +3508,37 @@ issue_5288: {
}() ])); }() ]));
} }
expect: { 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" expect_stdout: "PASS"
node_version: ">=6" node_version: ">=6"
@@ -3466,10 +3584,263 @@ issue_5314_2: {
A = this; A = this;
new function() { new function() {
[ { [ {
[console.log(this === A ? "FAIL" : "PASS")]: [].e, [console.log(this === A ? "FAIL" : "PASS")]: [][0],
} ] = [ 42 ]; } ] = [ 42 ];
}(); }();
} }
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=6" 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: { issue_1539: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -3615,3 +3685,85 @@ issue_5271: {
} }
expect_stdout: "42" 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: { issue_1649: {
options = { options = {
evaluate: true, evaluate: true,
@@ -3349,3 +3363,56 @@ issue_5362_2: {
} }
expect_stdout: "true" 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";' 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: { same_quotes: {
beautify = { beautify = {
beautify: true, beautify: true,
@@ -417,6 +428,46 @@ hoist_funs: {
expect_exact: "export function f(){}export default async function*g(){}" 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: { issue_4742_join_vars_1: {
options = { options = {
join_vars: true, join_vars: true,
@@ -496,3 +547,16 @@ issue_4766: {
export var a = "bar"; 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 void "moo";
return; return;
} else } else
return; return void 0;
}()); }());
} }
expect_stdout: [ expect_stdout: [
@@ -1508,6 +1508,48 @@ unsafe_call_3: {
expect_stdout: "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: { issue_2616: {
options = { options = {
evaluate: true, evaluate: true,
@@ -3515,6 +3557,27 @@ functions_inner_var: {
expect_stdout: "undefined undefined" 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: { issue_2437: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -5519,7 +5582,7 @@ issue_3835: {
return f(); return f();
})(); })();
} }
expect_stdout: true expect_stdout: RangeError("Maximum call stack size exceeded")
} }
issue_3836_1: { issue_3836_1: {
@@ -7772,7 +7835,7 @@ issue_5249_1: {
while (console.log("FAIL 2")); while (console.log("FAIL 2"));
return; return;
} else } else
return; return void 0;
throw "FAIL 3"; throw "FAIL 3";
}()); }());
} }
@@ -8228,3 +8291,358 @@ issue_5366: {
"baz", "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", "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); console.log(k);
} }
expect: { expect: {
!(A = 0); for (var k in !(A = 0));
for (var k in true);
console.log(k); console.log(k);
} }
expect_stdout: "undefined" expect_stdout: "undefined"
@@ -407,9 +406,9 @@ issue_4893_2: {
expect: { expect: {
try{ try{
(function() { (function() {
var b; var a;
b = null; a = null;
b.p += 42; a.p += 42;
})(); })();
} catch (e) { } catch (e) {
console.log("PASS"); console.log("PASS");
@@ -499,3 +498,108 @@ issue_5195: {
} }
expect_stdout: "[object Object]" 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";' 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: { dynamic: {
input: { input: {
(async a => await import(a))("foo").then(bar); (async a => await import(a))("foo").then(bar);
@@ -227,3 +238,33 @@ issue_4708_2: {
import a from "foo"; 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: { expect: {
!function() { !function() {
var i = Object(); if (Object())
if (i)
return console.log("PASS"); return console.log("PASS");
throw "FAIL"; throw "FAIL";
}(); }();

View File

@@ -111,6 +111,7 @@ labels_5: {
labels_6: { labels_6: {
options = { options = {
dead_code: true, dead_code: true,
unused: true,
} }
input: { input: {
out: break out; out: break out;
@@ -208,6 +209,59 @@ labels_10: {
expect_stdout: "PASS" 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: { issue_4466_1: {
mangle = { mangle = {
v8: false, v8: false,
@@ -327,3 +381,53 @@ issue_4466_2_toplevel_v8: {
} }
expect_stdout: "PASS" 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" 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: { merge_vars_1: {
options = { options = {
merge_vars: true, merge_vars: true,
@@ -892,6 +982,40 @@ if_return_2: {
node_version: ">=4" 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: { do_if_continue_1: {
options = { options = {
if_return: true, if_return: true,
@@ -1604,48 +1728,6 @@ issue_4305_2: {
node_version: ">=4" 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: { issue_4438: {
options = { options = {
if_return: true, if_return: true,
@@ -1667,7 +1749,7 @@ issue_4438: {
function f() { function f() {
if (console) { if (console) {
let a = console.log; let a = console.log;
void a("PASS"); a("PASS");
} }
} }
f(); f();
@@ -2020,3 +2102,23 @@ issue_5338: {
expect_stdout: ReferenceError("a is not defined") expect_stdout: ReferenceError("a is not defined")
node_version: ">=4" 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" 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: { issue_5266: {
options = { options = {
inline: true, inline: true,
side_effects: true,
} }
input: { input: {
[ [

View File

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

View File

@@ -6,6 +6,7 @@ reduce_vars: {
C: 0, C: 0,
}, },
inline: true, inline: true,
passes: 2,
reduce_funcs: true, reduce_funcs: true,
reduce_vars: true, reduce_vars: true,
toplevel: true, toplevel: true,
@@ -6688,8 +6689,7 @@ issues_3267_1: {
} }
expect: { expect: {
!function(x) { !function(x) {
var i = Object(); if (Object())
if (i)
return console.log("PASS"); return console.log("PASS");
throw "FAIL"; throw "FAIL";
}(); }();
@@ -7861,3 +7861,38 @@ issue_5324: {
} }
expect_stdout: "NaN" 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" 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: { issue_3434_1: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -648,7 +648,7 @@ drop_new_function: {
} }
expect: { expect: {
void ([ ... { void ([ ... {
[console.log("PASS")]: [].e, [console.log("PASS")]: [][0],
}] = []); }] = []);
} }
expect_stdout: "PASS" expect_stdout: "PASS"
@@ -1299,3 +1299,328 @@ issue_5360: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=8.3.0" 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: { expect: {
var b = 42, c = "FAIL"; var b = 42, c = "FAIL";
var a; var a;
for (c = "PASS", b; "" == typeof d;); for (c = "PASS"; "" == typeof d;);
console.log(c, b); console.log(c, b);
} }
expect_stdout: "PASS 42" expect_stdout: "PASS 42"

View File

@@ -645,3 +645,56 @@ issue_4751: {
} }
expect_stdout: "PASS" 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: { collapse_vars_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -1166,3 +1175,31 @@ issue_5006: {
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=6" 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" 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 (w) {
if (y) return; if (y) return;
} else if (z) 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: { expect: {
var o = 0, o_p = "PASS"; var o, o_p = "PASS";
console.log(o_p); console.log(o_p);
} }
expect_stdout: "PASS" expect_stdout: "PASS"
@@ -613,3 +613,35 @@ issue_4954: {
] ]
node_version: ">=4" 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_exact: "(function(){console.log(typeof function n(o){})})();"
expect_stdout: "function" 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") ]); })([ console.log("baz") ]);
} }
expect: { expect: {
[ [ , [].e = console.log("foo") ] ] = [ [ console.log("baz") ] ]; [ [ , [][0] = console.log("foo") ] ] = [ [ console.log("baz") ] ];
} }
expect_stdout: [ expect_stdout: [
"baz", "baz",
@@ -934,6 +934,21 @@ drop_unused_call: {
node_version: ">=4" 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: { issue_4454_1: {
rename = false rename = false
options = { options = {
@@ -978,8 +993,8 @@ issue_4454_2: {
expect: { expect: {
function f(a) { function f(a) {
(function*(c = console.log(a)) {})(); (function*(c = console.log(a)) {})();
var a = 42..toString(); var b = 42..toString();
console.log(a); console.log(b);
} }
f("PASS"); f("PASS");
} }
@@ -1168,6 +1183,35 @@ issue_4641_2: {
node_version: ">=10" 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: { issue_4769_1: {
options = { options = {
side_effects: true, side_effects: true,
@@ -1267,80 +1311,6 @@ issue_5019_2: {
node_version: ">=4" 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: { issue_5034: {
options = { options = {
functions: true, functions: true,
@@ -1451,3 +1421,283 @@ issue_5177: {
expect_stdout: "function" expect_stdout: "function"
node_version: ">=4" 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(); 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) { it("Should compress swarm of unused variables with reasonable performance", function(done) {
var code = [ var code = [
"console.log(function() {", "console.log(function() {",

View File

@@ -1,3 +1,4 @@
var acorn = require("acorn");
var assert = require("assert"); var assert = require("assert");
var UglifyJS = require("../node"); var UglifyJS = require("../node");
@@ -25,6 +26,7 @@ describe("export", function() {
"export { * };", "export { * };",
"export { * as A };", "export { * as A };",
"export { 42 as A };", "export { 42 as A };",
"export { 'A' as B };",
"export { A as B-C };", "export { A as B-C };",
"export { default as A };", "export { default as A };",
].forEach(function(code) { ].forEach(function(code) {
@@ -51,8 +53,11 @@ describe("export", function() {
it("Should reject invalid `export ... from ...` statement syntax", function() { it("Should reject invalid `export ... from ...` statement syntax", function() {
[ [
"export from 'path';", "export from 'path';",
"export A from 'path';",
"export * from `path`;", "export * from `path`;",
"export 'A' from 'path';",
"export A as B from 'path';", "export A as B from 'path';",
"export 'A' as B from 'path';",
"export default from 'path';", "export default from 'path';",
"export { A }, B from 'path';", "export { A }, B from 'path';",
"export * as A, B from 'path';", "export * as A, B from 'path';",
@@ -68,4 +73,109 @@ describe("export", function() {
}, code); }, 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 assert = require("assert");
var UglifyJS = require("../node"); var UglifyJS = require("../node");
@@ -8,15 +9,25 @@ describe("import", function() {
"import A;", "import A;",
"import {};", "import {};",
"import `path`;", "import `path`;",
"{ import 'path'; }",
"import from 'path';", "import from 'path';",
"if (0) import 'path';",
"import * from 'path';", "import * from 'path';",
"import 'A' from 'path';",
"import A-B from 'path';",
"import A as B from 'path';", "import A as B from 'path';",
"import { A }, 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, B from 'path';",
"import * as A, {} from 'path';", "import * as A, {} 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 { 42 as A } from 'path';",
"import { A-B as C } from 'path';", "import { A-B as C } from 'path';",
"import { 'A' as 'B' } from 'path';",
].forEach(function(code) { ].forEach(function(code) {
assert.throws(function() { assert.throws(function() {
UglifyJS.parse(code); UglifyJS.parse(code);
@@ -25,4 +36,74 @@ describe("import", function() {
}, code); }, 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 assert = require("assert");
var readFileSync = require("fs").readFileSync; var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code; var run_code = require("../sandbox").run_code;
var semver = require("semver");
var UglifyJS = require("../.."); var UglifyJS = require("../..");
function read(path) { 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() { describe("rename", function() {
it("Should be repeatable", function() { it("Should be repeatable", function() {
var code = "!function(x){return x(x)}(y);"; var code = "!function(x){return x(x)}(y);";

View File

@@ -434,7 +434,7 @@ describe("test/reduce.js", function() {
"// }", "// }",
].join("\n")); ].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"), { var result = reduce_test(read("test/input/reduce/export_default.js"), {
compress: false, compress: false,
toplevel: true, toplevel: true,
@@ -448,4 +448,41 @@ describe("test/reduce.js", function() {
"// }", "// }",
].join("\n")); ].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, { var beautified = UglifyJS.minify(ast, {
compress: false, compress: false,
mangle: false, mangle: false,
module: ufuzz.module,
output: { output: {
ast: true,
beautify: true, beautify: true,
braces: true, braces: true,
}, },
}); });
if (beautified.error) return beautified; if (!beautified.error) {
return UglifyJS.minify(beautified.code, { var verify = UglifyJS.minify(beautified.code, {
compress: false, compress: false,
mangle: false, mangle: false,
output: { module: ufuzz.module,
ast: true, });
}, if (verify.error) return verify;
}); }
return beautified;
} }
function validate(ast) { function validate(ast) {
@@ -35,6 +38,7 @@ function validate(ast) {
return UglifyJS.minify(ast, { return UglifyJS.minify(ast, {
compress: false, compress: false,
mangle: false, mangle: false,
module: ufuzz.module,
output: { output: {
ast: true, 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)); var ast = UglifyJS.AST_Node.from_mozilla_ast(to_moz(input));
} catch (e) { } catch (e) {
if (skip_on_error) return true; if (skip_on_error) return true;
console.log("//============================================================="); console.error("//=============================================================");
console.log("//", description, "failed... round", round); console.error("//", description, "failed... round", round);
console.log(e); console.error(e);
console.log("// original code"); console.error("// original code");
if (beautified === true) console.log("// (beautified)"); if (beautified === true) console.error("// (beautified)");
console.log(input.code); console.error(input.code);
return false; return false;
} }
var transformed = validate(ast); 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; if (!test(beautified, to_moz, description, skip_on_error, true)) return false;
} }
} }
console.log("//============================================================="); console.error("//=============================================================");
console.log("// !!!!!! Failed... round", round); console.error("// !!!!!! Failed... round", round);
console.log("// original code"); console.error("// original code");
if (beautified.error) { if (beautified.error) {
console.log("// !!! beautify failed !!!"); console.error("// !!! beautify failed !!!");
console.log(beautified.error.stack); console.error(beautified.error.stack);
} else if (beautified === true) { } else if (beautified === true) {
console.log("// (beautified)"); console.error("// (beautified)");
} }
console.log(input.raw); console.error(input.raw);
console.log(); console.error();
console.log(); console.error();
console.log("//-------------------------------------------------------------"); console.error("//-------------------------------------------------------------");
console.log("//", description); console.error("//", description);
if (transformed.error) { if (transformed.error) {
console.log(transformed.error.stack); console.error(transformed.error.stack);
} else { } else {
beautified = beautify(transformed.ast); beautified = beautify(transformed.ast);
if (beautified.error) { if (beautified.error) {
console.log("// !!! beautify failed !!!"); console.error("// !!! beautify failed !!!");
console.log(beautified.error.stack); console.error(beautified.error.stack);
console.log(transformed.code); console.error(transformed.code);
} else { } else {
console.log("// (beautified)"); console.error("// (beautified)");
console.log(beautified.code); console.error(beautified.code);
} }
} }
console.log("!!!!!! Failed... round", round); console.error("!!!!!! Failed... round", round);
return false; return false;
} }
return true; return true;
@@ -115,9 +119,29 @@ for (var round = 1; round <= num_iterations; round++) {
var code = ufuzz.createTopLevelCode(); var code = ufuzz.createTopLevelCode();
minify_options.forEach(function(options) { minify_options.forEach(function(options) {
var ok = true; 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, compress: false,
mangle: false, mangle: false,
module: ufuzz.module,
output: { output: {
ast: true, ast: true,
}, },
@@ -125,11 +149,27 @@ for (var round = 1; round <= num_iterations; round++) {
input.raw = options ? input.code : code; input.raw = options ? input.code : code;
if (input.error) { if (input.error) {
ok = false; ok = false;
console.log("//============================================================="); console.error("//=============================================================");
console.log("// minify() failed... round", round); console.error("// parse() failed... round", round);
console.log(input.error); console.error("// original code");
console.log("// original code"); console.error(code);
console.log(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) { if (ok) ok = test(input, function(input) {
return input.ast.to_mozilla_ast(); return input.ast.to_mozilla_ast();
@@ -141,7 +181,10 @@ for (var round = 1; round <= num_iterations; round++) {
sourceType: "module", sourceType: "module",
}); });
}, "acorn.parse()", !ufuzz.verbose); }, "acorn.parse()", !ufuzz.verbose);
if (!ok) process.exit(1); if (!ok && isFinite(num_iterations)) {
console.log();
process.exit(1);
}
}); });
} }
console.log(); 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) { if (node.expression instanceof U.AST_Function) {
// hoist and return expressions from the IIFE function expression // 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) { node.expression.body.forEach(function(node) {
var expr = node instanceof U.AST_Exit ? node.value : node.body; 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 // collect expressions from each statement's body
seq.push(expr); seq.push(expr);
} }
@@ -241,6 +241,23 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true; CHANGED = true;
return node.name; 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) { else if (node instanceof U.AST_DestructuredArray) {
var expr = node.elements[0]; var expr = node.elements[0];
if (expr && !(expr instanceof U.AST_Hole)) { if (expr && !(expr instanceof U.AST_Hole)) {
@@ -258,22 +275,6 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return expr.value; 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) { else if (node instanceof U.AST_DWLoop) {
var expr = [ var expr = [
node.condition, node.condition,
@@ -295,6 +296,16 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_statement(expr); 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) { else if (node instanceof U.AST_Finally) {
// drop finally block // drop finally block
node.start._permute++; node.start._permute++;
@@ -350,6 +361,15 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return to_statement(expr); 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) { else if (node instanceof U.AST_Object) {
// first property's value // first property's value
var expr = node.properties[0]; 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) { if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
// hoist simple statement IIFE function expression body // hoist simple statement IIFE function expression body
node.start._permute++; 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; CHANGED = true;
return List.splice(node.body.expression.body); 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); return to_statement(node.definitions[0].value);
} }
} }
else if (node instanceof U.AST_LabeledStatement) { else if (node instanceof U.AST_VarDef) {
if (node.body instanceof U.AST_Statement if (node.value) {
&& !has_loopcontrol(node.body, node.body, node)) { node.start._permute++;
// replace labelled statement with its non-labelled body
node.start._permute = REPLACEMENTS.length;
CHANGED = true; 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+$/, ""); 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) { function has_loopcontrol(body, loop, label) {
var found = false; var found = false;
var tw = new U.TreeWalker(function(node) { var tw = new U.TreeWalker(function(node) {
@@ -676,17 +682,31 @@ function has_loopcontrol(body, loop, label) {
return found; return found;
} }
function can_hoist(body) { function can_hoist(body, scope) {
var found = false; var found = false;
body.walk(new U.TreeWalker(function(node) { var tw = new U.TreeWalker(function(node) {
if (found) return true; 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_NewTarget) return found = true;
if (node instanceof U.AST_Scope) { if (node instanceof U.AST_Scope) {
if (node === body) return; 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; return true;
} }
if (node instanceof U.AST_Super) return found = 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; return !found;
} }
@@ -760,9 +780,9 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) {
if (minified.error) return minified; if (minified.error) return minified;
var toplevel = sandbox.has_toplevel(minify_options); 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 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)) { if (sandbox.same_stdout(unminified.result, minified_result)) {
return is_timed_out(unminified.result) && is_timed_out(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, minified_result: minified_result,
elapsed: unminified.elapsed, 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) { 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 "src" \
&& minify_in_situ "third_party" \ && minify_in_situ "third_party" \
&& rm -rf node_modules \ && rm -rf node_modules \
&& npm_install \ && npm_install --package-lock \
&& rm -rf build/* \ && rm -rf build/* \
&& npm run build:terser-bundled \ && npm run build:terser-bundled \
&& npm run build:uglify-js-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); return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
}; };
exports.patch_module_statements = function(code) { exports.patch_module_statements = function(code) {
var count = 0, imports = []; var count = 0, has_default = "", imports = [], strict_mode = "";
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) { 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) return "";
if (header.length == 1) return "0, " + header; if (header.length == 1) return "0, " + header;
do { var name = "_uglify_export_default_";
var name = "_export_default_" + ++count; if (/^class\b/.test(header)) do {
name = "_uglify_export_default_" + ++count;
} while (code.indexOf(name) >= 0); } while (code.indexOf(name) >= 0);
return header.slice(0, -1) + " " + name + header.slice(-1); return header.slice(0, -1) + " " + name + header.slice(-1);
}).replace(/\bimport\.meta\b/g, function() { }).replace(/\bimport\.meta\b/g, function() {
@@ -76,7 +81,7 @@ exports.patch_module_statements = function(code) {
return ""; return "";
}); });
imports.push(""); imports.push("");
return imports.join("\n") + code; return strict_mode + has_default + imports.join("\n") + code;
}; };
function is_error(result) { function is_error(result) {

View File

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