Compare commits

..

84 Commits

Author SHA1 Message Date
Alex Lam S.L
d930c705f6 v3.13.7 2021-05-19 02:38:19 +08:00
Alex Lam S.L
254937754c fix corner case in reduce_vars (#4944)
fixes #4943
2021-05-18 05:52:24 +08:00
Alex Lam S.L
ae4dbcb5b9 document v8 quirks (#4942)
closes #4941
2021-05-16 02:13:30 +08:00
Alex Lam S.L
e13615549e fix corner case in pure_getters (#4940)
fixes #4939
2021-05-16 02:12:58 +08:00
Alex Lam S.L
a7698f8845 fix corner case in reduce_vars (#4938)
fixes #4937
2021-05-15 22:38:09 +08:00
Alex Lam S.L
bbed9b13b1 fix corner case in collapse_vars (#4936)
fixes #4935
2021-05-15 22:34:14 +08:00
Alex Lam S.L
2cff7c94e8 fix corner case in reduce_vars (#4934)
fixes #4933
2021-05-15 01:49:46 +08:00
Alex Lam S.L
7576048118 fix corner case in unsafe evaluate (#4932)
fixes #4931
2021-05-14 22:51:19 +08:00
Alex Lam S.L
3c1898fd65 suppress invalid test case generation (#4930) 2021-05-13 19:57:36 +08:00
Alex Lam S.L
e04429350f fix corner case in ie8 (#4929)
fixes #4928
2021-05-13 09:26:57 +08:00
Alex Lam S.L
60f3b55156 fix corner case with optional chain operator (#4927) 2021-05-12 10:12:19 +08:00
Alex Lam S.L
689f8f504d enhance mangle (#4926) 2021-05-11 23:41:32 +08:00
Alex Lam S.L
ae51f76ba7 fix corner case in unused (#4925)
fixes #4924
2021-05-11 20:50:58 +08:00
Alex Lam S.L
7eef86ed05 workaround GitHub Actions issue (#4923) 2021-05-11 18:21:21 +08:00
Alex Lam S.L
b1cfa71131 enhance unused (#4922) 2021-05-11 10:30:20 +08:00
Alex Lam S.L
7b8570f16c v3.13.6 2021-05-11 03:15:16 +08:00
Alex Lam S.L
bb225367cb fix corner case collapse_vars (#4921)
fixes #4920
2021-05-09 02:59:45 +08:00
Alex Lam S.L
bbca9de9cd fix corner case in collapse_vars (#4919)
fixes #4918
2021-05-08 03:58:29 +08:00
Alex Lam S.L
ee9ceb79ca fix corner case in collapse_vars (#4917)
fixes #4916
2021-05-08 02:34:27 +08:00
Alex Lam S.L
ac1f7d689b fix corner case in collapse_vars (#4915)
fixes #4914
2021-05-08 01:14:59 +08:00
Alex Lam S.L
19d232badb fix corner case in unused (#4913)
fixes #4912
2021-05-07 19:38:22 +08:00
Alex Lam S.L
d464be3f3f fix corner case in collapse_vars (#4911)
fixes #4910
2021-05-05 00:03:43 +08:00
Alex Lam S.L
3094eaaa89 fix corner case in collapse_vars (#4909)
fixes #4908
2021-05-04 16:33:52 +08:00
Alex Lam S.L
ce3c35fa8b fix corner case in unused (#4907)
fixes #4906
2021-05-04 06:19:25 +08:00
Alex Lam S.L
5d9224deb8 fix corner cases with template literals (#4903)
fixes #4902
2021-05-03 22:26:20 +08:00
Alex Lam S.L
45b6d23d36 suppress false positives in ufuzz (#4901) 2021-05-03 19:11:53 +08:00
Alex Lam S.L
f0de9a8b5d support optional chaining operator (#4899) 2021-05-03 10:08:29 +08:00
Alex Lam S.L
203f4b7ad9 fix corner case in hoist_vars (#4900)
fixes #4898
2021-05-03 04:05:52 +08:00
Alex Lam S.L
4114431eec fix corner case in collapse_vars (#4896)
fixes #4895
2021-05-02 18:23:18 +08:00
Alex Lam S.L
fb03561799 fix corner case in hoist_vars (#4894)
fixes #4893
2021-05-02 07:28:31 +08:00
Alex Lam S.L
6ab26eef6c fix corner case in collapse_vars (#4892)
fixes #4891
2021-05-02 03:57:17 +08:00
Alex Lam S.L
53b57ee57e enhance reduce_vars & unused (#4890) 2021-05-02 00:19:56 +08:00
Alex Lam S.L
16411dcb87 enhance collapse_vars (#4885) 2021-05-01 21:37:52 +08:00
Alex Lam S.L
8bbfaacdae fix corner case in properties (#4889)
fixes #4888
2021-05-01 07:52:53 +08:00
Alex Lam S.L
df980db4a8 fix corner case in unsafe evaluate (#4887)
fixes #4886
2021-05-01 07:24:39 +08:00
Alex Lam S.L
8e4a19ffec fix corner case in spreads (#4883)
fixes #4882
2021-04-30 22:21:15 +08:00
Alex Lam S.L
d833e66d23 enhance join_vars (#4881) 2021-04-30 11:40:47 +08:00
Alex Lam S.L
8aa650bcf9 v3.13.5 2021-04-30 00:44:50 +08:00
Alex Lam S.L
d576495e5a support #__PURE__ in ESTree (#4879) 2021-04-29 04:13:42 +08:00
Alex Lam S.L
a06e20304b fix corner case in pure_getters (#4877)
fixes #4876
2021-04-27 22:52:37 +08:00
Alex Lam S.L
4cccc01f3e fix corner case in collapse_vars (#4875)
fixes #4874
2021-04-27 08:54:02 +08:00
Alex Lam S.L
97bd56b7e8 improve AST tests & tools (#4873) 2021-04-27 08:53:45 +08:00
Alex Lam S.L
acf951a5bc fix corner case with for [await]...of (#4872) 2021-04-26 05:10:22 +08:00
Alex Lam S.L
324587f769 upgrade AST<->ESTree translation (#4870)
fixes #968
2021-04-26 04:23:52 +08:00
Alex Lam S.L
80efaa2f33 fix corner case with export (#4871) 2021-04-25 22:37:26 +08:00
Alex Lam S.L
a1a212f639 fix corner case in collapse_vars (#4869)
fixes #4868
2021-04-25 05:16:51 +08:00
Alex Lam S.L
a2b1b96752 fix corner case with yield (#4867) 2021-04-25 03:10:12 +08:00
Alex Lam S.L
c296a63fb3 fix corner case in collapse_vars (#4866)
fixes #4865
2021-04-24 20:04:31 +08:00
Alex Lam S.L
10dd9d4eaf enhance collapse_vars (#4864) 2021-04-24 12:45:18 +08:00
Alex Lam S.L
9b8deff64d enhance pure_getters, reduce_vars & unused (#4863) 2021-04-24 07:17:30 +08:00
Alex Lam S.L
f46209b7e5 enhance unsafe side_effects (#4862)
closes #4861
2021-04-23 06:58:14 +08:00
Alex Lam S.L
bddb5a0102 enhance unused (#4858) 2021-04-22 10:58:50 +08:00
Alex Lam S.L
3c161a6662 fix corner case in hoist_vars (#4860)
fixes #4859
2021-04-22 05:47:21 +08:00
Alex Lam S.L
c58e174647 document various ECMAScript bugs (#4857) 2021-04-21 08:33:54 +08:00
Alex Lam S.L
c53af3dfb1 fix corner case in collapse_vars (#4855)
fixes #4854
2021-04-14 01:22:08 +08:00
Alex Lam S.L
f1f4a4dd82 v3.13.4 2021-04-12 04:33:40 +08:00
Alex Lam S.L
ca49f6f41a fix corner case in collapse_vars (#4853)
fixes #4852
2021-04-08 17:50:40 +08:00
Alex Lam S.L
8a82822654 fix corner case in if_return (#4851)
fixes #4848
2021-04-08 08:57:59 +08:00
Alex Lam S.L
ebe4e1ad28 fix corner case in unused (#4850)
fixes #4849
2021-04-08 06:31:15 +08:00
Alex Lam S.L
a37ca558dd reject invalid for await syntax (#4847) 2021-04-07 22:37:15 +08:00
Alex Lam S.L
73a564343b preserve compatibility of quote_style (#4845) 2021-04-07 06:49:12 +08:00
Alex Lam S.L
4870747306 speed up OutputStream (#4844) 2021-04-07 02:57:23 +08:00
Alex Lam S.L
b179a2459f parse octal literals correctly (#4843) 2021-04-07 02:23:35 +08:00
Alex Lam S.L
231c3d7c84 clean up OutputStream (#4842) 2021-04-06 21:34:27 +08:00
Alex Lam S.L
aed758ed5c enhance comparisons & reduce_vars (#4841) 2021-04-03 22:06:05 +08:00
Alex Lam S.L
0df028187d fix corner case in hoist_vars (#4840)
fixes #4839
2021-04-03 10:07:18 +08:00
Alex Lam S.L
10fbf8e295 support mangle.properties on class (#4838) 2021-04-03 06:00:19 +08:00
Alex Lam S.L
cf38b52afa parse import.meta correctly (#4836) 2021-04-03 04:31:29 +08:00
Alex Lam S.L
e755d01a0b fix corner case in unused (#4835)
fixes #4834
2021-04-02 03:55:53 +08:00
Alex Lam S.L
4084948d3b suppress invalid AST transform in --reduce-test (#4833) 2021-04-01 18:52:29 +08:00
Alex Lam S.L
cea1fb5c58 fix corner case in properties (#4832)
fixes #4831
2021-04-01 14:39:51 +08:00
Alex Lam S.L
1947a21824 fix corner case in properties (#4830)
fixes #4829
2021-04-01 07:05:50 +08:00
Alex Lam S.L
6335b5fd8a v3.13.3 2021-03-29 06:58:06 +08:00
Alex Lam S.L
daa8319b8a fix corner cases with logical assignment operators (#4828)
fixes #4827
2021-03-28 03:44:45 +08:00
Alex Lam S.L
d5599604e8 enhance collapse_vars (#4826) 2021-03-27 22:14:37 +08:00
Alex Lam S.L
072933f1d5 diagnose GitHub Actions (#4825) 2021-03-25 18:44:39 +08:00
Alex Lam S.L
39df3a1680 fix corner case in functions (#4824)
fixes #4823
2021-03-25 08:49:01 +08:00
Alex Lam S.L
03c5ecb2e3 fix corner cases with class (#4822)
fixes #4821
2021-03-25 04:36:50 +08:00
Alex Lam S.L
40ef074cb3 fix corner case in comparisons (#4820)
fixes #4819
2021-03-24 10:10:02 +08:00
Alex Lam S.L
78e3936cd4 fix corner case in inline (#4818)
fixes #4817
2021-03-23 22:33:24 +08:00
Alex Lam S.L
e7be38b42a fix corner cases with logical assignment operators (#4816)
fixes #4815
2021-03-23 13:02:45 +08:00
Alex Lam S.L
44394e61c9 workaround toString() quirks on global context (#4814) 2021-03-23 11:15:41 +08:00
Alex Lam S.L
f9055df44d support logical assignment operators (#4813) 2021-03-23 04:59:43 +08:00
Alex Lam S.L
51bdb7281b improve global context enumeration under sandbox (#4812)
fixes #4811
2021-03-22 22:43:33 +08:00
56 changed files with 4692 additions and 801 deletions

View File

@@ -8,7 +8,12 @@ jobs:
strategy:
fail-fast: false
matrix:
options: [ '-mb braces', '--ie8 -c', '-mc', '--toplevel -mc passes=3,pure_getters,unsafe' ]
options:
- '-mb braces'
- '--ie8 -c'
- '-mc'
- '-p acorn --toplevel -mco spidermonkey'
- '--toplevel -mc passes=3,pure_getters,unsafe'
script:
- acorn.sh
- bootstrap.sh

View File

@@ -2,7 +2,7 @@ name: Fuzzing
on:
pull_request:
schedule:
- cron: '*/5 * * * *'
- cron: '*/15 * * * *'
env:
BASE_URL: https://api.github.com/repos/${{ github.repository }}
CAUSE: ${{ github.event_name }}

View File

@@ -1309,3 +1309,35 @@ To allow for better optimizations, the compiler makes various assumptions:
// Actual: { '42': undefined }
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
var await;
async function f() {
class A {
static p = await;
}
}
// SyntaxError: Unexpected reserved word
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of JavaScript will throw `SyntaxError` with the following:
```javascript
var async;
for (async of []);
// SyntaxError: The left-hand side of a for-of loop may not be 'async'.
```
UglifyJS may modify the input which in turn may suppress those errors.
- Later versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
console.log({
...console,
get 42() {
return "FAIL";
},
[42]: "PASS",
}[42]);
// Expected: "PASS"
// Actual: "FAIL"
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -235,10 +235,11 @@ if (options.mangle && options.mangle.properties) {
});
}
}
if (output == "ast") options.output = {
ast: true,
code: false,
};
if (/^ast|spidermonkey$/.test(output)) {
if (typeof options.output != "object") options.output = {};
options.output.ast = true;
options.output.code = false;
}
if (options.parse && (options.parse.acorn || options.parse.spidermonkey)
&& options.sourceMap && options.sourceMap.content == "inline") {
fatal("inline source map only works with built-in parser");
@@ -265,7 +266,9 @@ if (specified["self"]) {
paths = UglifyJS.FILES;
}
if (specified["in-situ"]) {
if (output || specified["reduce-test"] || specified["self"]) fatal("incompatible options specified");
if (output && output != "spidermonkey" || specified["reduce-test"] || specified["self"]) {
fatal("incompatible options specified");
}
paths.forEach(function(name) {
print(name);
if (/^ast|spidermonkey$/.test(name)) fatal("invalid file name specified");
@@ -311,13 +314,43 @@ function run() {
try {
if (options.parse) {
if (options.parse.acorn) {
var annotations = Object.create(null);
files = convert_ast(function(toplevel, name) {
return require("acorn").parse(files[name], {
var content = files[name];
var list = annotations[name] = [];
var prev = -1;
return require("acorn").parse(content, {
allowHashBang: true,
ecmaVersion: "latest",
locations: true,
onComment: function(block, text, start, end) {
var match = /[@#]__PURE__/.exec(text);
if (!match) {
if (start != prev) return;
match = [ list[prev] ];
}
while (/\s/.test(content[end])) end++;
list[end] = match[0];
prev = end;
},
preserveParens: true,
program: toplevel,
sourceFile: name
sourceFile: name,
sourceType: "module",
});
});
files.walk(new UglifyJS.TreeWalker(function(node) {
if (!(node instanceof UglifyJS.AST_Call)) return;
var list = annotations[node.start.file];
var pure = list[node.start.pos];
if (!pure) {
var pos = node.start.parens;
if (pos) for (var i = 0; !pure && i < pos.length; i++) {
pure = list[pos[i]];
}
}
if (pure) node.pure = pure;
}));
} else if (options.parse.spidermonkey) {
files = convert_ast(function(toplevel, name) {
var obj = JSON.parse(files[name]);
@@ -409,16 +442,19 @@ function run() {
return value;
}, 2));
} else if (output == "spidermonkey") {
print(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
ast: true,
code: false
},
}).ast.to_mozilla_ast(), null, 2));
print(JSON.stringify(result.ast.to_mozilla_ast(), null, 2));
} else if (output) {
fs.writeFileSync(output, result.code);
var code;
if (result.ast) {
var opts = {};
for (var name in options.output) {
if (!/^ast|code$/.test(name)) opts[name] = options.output[name];
}
code = UglifyJS.AST_Node.from_mozilla_ast(result.ast.to_mozilla_ast()).print_to_string(opts);
} else {
code = result.code;
}
fs.writeFileSync(output, code);
if (result.map) fs.writeFileSync(output + ".map", result.map);
} else {
print(result.code);

View File

@@ -261,9 +261,9 @@ var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_s
$documentation: "Base class for all statements introducing a lexical scope",
$propdoc: {
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
functions: "[Object/S] like `variables`, but only lists function declarations",
functions: "[Dictionary/S] like `variables`, but only lists function declarations",
parent_scope: "[AST_Scope?/S] link to the parent scope",
variables: "[Object/S] a map of name ---> SymbolDef for all variables/functions defined in this scope",
variables: "[Dictionary/S] a map of name ---> SymbolDef for all variables/functions defined in this scope",
},
clone: function(deep) {
var node = this._clone(deep);
@@ -506,7 +506,7 @@ var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", {
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
$documentation: "The toplevel scope",
$propdoc: {
globals: "[Object/S] a map of name ---> SymbolDef for all undeclared names",
globals: "[Dictionary/S] a map of name ---> SymbolDef for all undeclared names",
},
wrap: function(name) {
var body = this.body;
@@ -1293,11 +1293,13 @@ function must_be_expressions(node, prop, allow_spread, allow_hole) {
});
}
var AST_Call = DEFNODE("Call", "expression args pure", {
var AST_Call = DEFNODE("Call", "args expression optional pure", {
$documentation: "A function call expression",
$propdoc: {
args: "[AST_Node*] array of arguments",
expression: "[AST_Node] expression to invoke as function",
args: "[AST_Node*] array of arguments"
optional: "[boolean] whether the expression is optional chaining",
pure: "[string/S] marker for side-effect-free call expression",
},
walk: function(visitor) {
var node = this;
@@ -1315,7 +1317,10 @@ var AST_Call = DEFNODE("Call", "expression args pure", {
});
var AST_New = DEFNODE("New", null, {
$documentation: "An object instantiation. Derives from a function call since it has exactly the same properties"
$documentation: "An object instantiation. Derives from a function call since it has exactly the same properties",
_validate: function() {
if (this.optional) throw new Error("optional must be false");
},
}, AST_Call);
var AST_Sequence = DEFNODE("Sequence", "expressions", {
@@ -1337,11 +1342,12 @@ var AST_Sequence = DEFNODE("Sequence", "expressions", {
},
});
var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
var AST_PropAccess = DEFNODE("PropAccess", "expression optional property", {
$documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`",
$propdoc: {
expression: "[AST_Node] the “container” expression",
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"
optional: "[boolean] whether the expression is optional chaining",
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",
},
getProperty: function() {
var p = this.property;

File diff suppressed because it is too large Load Diff

View File

@@ -204,27 +204,29 @@ function minify(files, options) {
if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties);
if (timings) timings.output = Date.now();
var result = {};
if (options.output.ast) {
result.ast = toplevel;
}
if (!HOP(options.output, "code") || options.output.code) {
var output = defaults(options.output, {
ast: false,
code: true,
});
if (output.ast) result.ast = toplevel;
if (output.code) {
if (options.sourceMap) {
options.output.source_map = SourceMap(options.sourceMap);
output.source_map = SourceMap(options.sourceMap);
if (options.sourceMap.includeSources) {
if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) if (HOP(files, name)) {
options.output.source_map.setSourceContent(name, files[name]);
output.source_map.setSourceContent(name, files[name]);
}
}
}
delete options.output.ast;
delete options.output.code;
var stream = OutputStream(options.output);
delete output.ast;
delete output.code;
var stream = OutputStream(output);
toplevel.print(stream);
result.code = stream.get();
if (options.sourceMap) {
result.map = options.output.source_map.toString();
result.map = output.source_map.toString();
var url = options.sourceMap.url;
if (url) {
result.code = result.code.replace(/\n\/\/# sourceMappingURL=\S+\s*$/, "");

File diff suppressed because it is too large Load Diff

View File

@@ -49,8 +49,6 @@ function is_some_comments(comment) {
}
function OutputStream(options) {
var readonly = !options;
options = defaults(options, {
annotations : false,
ascii_only : false,
@@ -103,12 +101,35 @@ function OutputStream(options) {
}
}
var indentation = options.indent_start;
var current_col = 0;
var current_line = 1;
var current_pos = 0;
var OUTPUT = "";
var indentation = options.indent_start;
var last;
var line_end = 0;
var line_fixed = true;
var mappings = options.source_map && [];
var mapping_name;
var mapping_token;
var might_need_space;
var might_need_semicolon;
var need_newline_indented = false;
var need_space = false;
var newline_insert = -1;
var stack;
var OUTPUT;
function reset() {
last = "";
might_need_space = false;
might_need_semicolon = false;
stack = [];
var str = OUTPUT;
OUTPUT = "";
return str;
}
reset();
var to_utf8 = options.ascii_only ? function(str, identifier) {
if (identifier) str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
return "\\u{" + (ch.charCodeAt(0) - 0xd7c0 << 10 | ch.charCodeAt(1) - 0xdc00).toString(16) + "}";
@@ -141,6 +162,25 @@ function OutputStream(options) {
return j == 0 ? str : s + str.slice(j);
};
function quote_single(str) {
return "'" + str.replace(/\x27/g, "\\'") + "'";
}
function quote_double(str) {
return '"' + str.replace(/\x22/g, '\\"') + '"';
}
var quote_string = [
null,
quote_single,
quote_double,
function(str, quote) {
return quote == "'" ? quote_single(str) : quote_double(str);
},
][options.quote_style] || function(str, quote, dq, sq) {
return dq > sq ? quote_single(str) : quote_double(str);
};
function make_string(str, quote) {
var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) {
@@ -162,54 +202,11 @@ function OutputStream(options) {
}
return s;
});
function quote_single() {
return "'" + str.replace(/\x27/g, "\\'") + "'";
}
function quote_double() {
return '"' + str.replace(/\x22/g, '\\"') + '"';
}
str = to_utf8(str);
switch (options.quote_style) {
case 1:
return quote_single();
case 2:
return quote_double();
case 3:
return quote == "'" ? quote_single() : quote_double();
default:
return dq > sq ? quote_single() : quote_double();
}
}
function encode_string(str, quote) {
var ret = make_string(str, quote);
if (options.inline_script) {
ret = ret.replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2");
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
ret = ret.replace(/--\x3e/g, "--\\x3e");
}
return ret;
}
function make_name(name) {
name = name.toString();
name = to_utf8(name, true);
return name;
return quote_string(to_utf8(str), quote, dq, sq);
}
/* -----[ beautification/minification ]----- */
var has_parens = false;
var line_end = 0;
var line_fixed = true;
var might_need_space = false;
var might_need_semicolon = false;
var need_newline_indented = false;
var need_space = false;
var newline_insert = -1;
var last = "";
var mapping_token, mapping_name, mappings = options.source_map && [];
var adjust_mappings = mappings ? function(line, col) {
mappings.forEach(function(mapping) {
mapping.line += line;
@@ -257,8 +254,14 @@ function OutputStream(options) {
var requireSemicolonChars = makePredicate("( [ + * / - , .");
function print(str) {
str = String(str);
var print = options.beautify
|| options.comments
|| options.max_line_len
|| options.preserve_line
|| options.shebang
|| !options.semicolons
|| options.source_map
|| options.width ? function(str) {
var ch = str.charAt(0);
if (need_newline_indented && ch) {
need_newline_indented = false;
@@ -328,7 +331,6 @@ function OutputStream(options) {
}
OUTPUT += str;
has_parens = str.slice(-1) == "(";
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1;
current_line += n;
@@ -338,7 +340,30 @@ function OutputStream(options) {
current_col = a[n].length;
}
last = str;
}
} : function(str) {
var ch = str.charAt(0);
var prev = last.slice(-1);
if (might_need_semicolon) {
might_need_semicolon = false;
if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") {
OUTPUT += ";";
might_need_space = false;
}
}
if (might_need_space) {
if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") {
OUTPUT += " ";
}
if (prev != "<" || str != "!") might_need_space = false;
}
OUTPUT += str;
last = str;
};
var space = options.beautify ? function() {
print(" ");
@@ -351,14 +376,12 @@ function OutputStream(options) {
print(repeat_string(" ", half ? indentation - (options.indent_level >> 1) : indentation));
} : noop;
var with_indent = options.beautify ? function(col, cont) {
if (col === true) col = next_indent();
var with_indent = options.beautify ? function(cont) {
var save_indentation = indentation;
indentation = col;
var ret = cont();
indentation += options.indent_level;
cont();
indentation = save_indentation;
return ret;
} : function(col, cont) { return cont() };
} : function(cont) { cont() };
var may_add_newline = options.max_line_len || options.preserve_line ? function() {
fix_line();
@@ -387,41 +410,28 @@ function OutputStream(options) {
print(";");
}
function next_indent() {
return indentation + options.indent_level;
}
function with_block(cont) {
var ret;
print("{");
newline();
with_indent(next_indent(), function() {
ret = cont();
});
with_indent(cont);
indent();
print("}");
return ret;
}
function with_parens(cont) {
print("(");
may_add_newline();
//XXX: still nice to have that for argument lists
//var ret = with_indent(current_col, cont);
var ret = cont();
cont();
may_add_newline();
print(")");
return ret;
}
function with_square(cont) {
print("[");
may_add_newline();
//var ret = with_indent(current_col, cont);
var ret = cont();
cont();
may_add_newline();
print("]");
return ret;
}
function comma() {
@@ -472,31 +482,37 @@ function OutputStream(options) {
return true;
}
function should_merge_comments(node, parent) {
if (parent instanceof AST_Binary) return parent.left === node;
if (parent.TYPE == "Call") return parent.expression === node;
if (parent instanceof AST_Conditional) return parent.condition === node;
if (parent instanceof AST_Dot) return parent.expression === node;
if (parent instanceof AST_Exit) return true;
if (parent instanceof AST_Sequence) return parent.expressions[0] === node;
if (parent instanceof AST_Sub) return parent.expression === node;
if (parent instanceof AST_UnaryPostfix) return true;
if (parent instanceof AST_Yield) return true;
}
function prepend_comments(node) {
var self = this;
var scan = node instanceof AST_Exit && node.value;
var scan;
if (node instanceof AST_Exit) {
scan = node.value;
} else if (node instanceof AST_Yield) {
scan = node.expression;
}
var comments = dump(node);
if (!comments) comments = [];
if (scan) {
var tw = new TreeWalker(function(node) {
var parent = tw.parent();
if (parent instanceof AST_Exit
|| parent instanceof AST_Binary && parent.left === node
|| parent.TYPE == "Call" && parent.expression === node
|| parent instanceof AST_Conditional && parent.condition === node
|| parent instanceof AST_Dot && parent.expression === node
|| parent instanceof AST_Sequence && parent.expressions[0] === node
|| parent instanceof AST_Sub && parent.expression === node
|| parent instanceof AST_UnaryPostfix) {
var before = dump(node);
if (before) comments = comments.concat(before);
} else {
return true;
}
if (!should_merge_comments(node, tw.parent())) return true;
var before = dump(node);
if (before) comments = comments.concat(before);
});
tw.push(node);
node.value.walk(tw);
scan.walk(tw);
}
if (current_pos == 0) {
@@ -553,15 +569,14 @@ function OutputStream(options) {
if (OUTPUT.length > insert) newline_insert = insert;
}
var stack = [];
return {
get : get,
toString : get,
reset : reset,
indent : indent,
should_break : readonly ? noop : function() {
return options.width && current_col - indentation >= options.width;
},
has_parens : function() { return has_parens },
should_break : options.width ? function() {
return current_col - indentation >= options.width;
} : return_false,
has_parens : function() { return last.slice(-1) == "(" },
newline : newline,
print : print,
space : space,
@@ -571,20 +586,21 @@ function OutputStream(options) {
semicolon : semicolon,
force_semicolon : force_semicolon,
to_utf8 : to_utf8,
print_name : function(name) { print(make_name(name)) },
print_string : function(str, quote) { print(encode_string(str, quote)) },
next_indent : next_indent,
print_name : function(name) { print(to_utf8(name.toString(), true)) },
print_string : options.inline_script ? function(str, quote) {
str = make_string(str, quote).replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2");
print(str.replace(/\x3c!--/g, "\\x3c!--").replace(/--\x3e/g, "--\\x3e"));
} : function(str, quote) {
print(make_string(str, quote));
},
with_indent : with_indent,
with_block : with_block,
with_parens : with_parens,
with_square : with_square,
add_mapping : add_mapping,
option : function(opt) { return options[opt] },
prepend_comments: readonly ? noop : prepend_comments,
append_comments : readonly || comment_filter === return_false ? noop : append_comments,
line : function() { return current_line },
col : function() { return current_col },
pos : function() { return current_pos },
prepend_comments: options.comments || options.shebang ? prepend_comments : noop,
append_comments : options.comments ? append_comments : noop,
push_node : function(node) { stack.push(node) },
pop_node : options.preserve_line ? function() {
var node = stack.pop();
@@ -629,10 +645,19 @@ function OutputStream(options) {
stream.append_comments(self);
}
});
var readonly = OutputStream({
inline_script: false,
shebang: false,
width: false,
});
AST_Node.DEFMETHOD("print_to_string", function(options) {
var s = OutputStream(options);
this.print(s);
return s.get();
if (options) {
var stream = OutputStream(options);
this.print(stream);
return stream.get();
}
this.print(readonly);
return readonly.reset();
});
/* -----[ PARENTHESES ]----- */
@@ -684,6 +709,8 @@ function OutputStream(options) {
// (x++)[y]
// (typeof x).y
if (p instanceof AST_PropAccess) return p.expression === this;
// (~x)`foo`
if (p instanceof AST_Template) return p.tag === this;
}
PARENS(AST_Await, needs_parens_unary);
PARENS(AST_Unary, needs_parens_unary);
@@ -757,14 +784,29 @@ function OutputStream(options) {
if (p instanceof AST_Class) return true;
// (foo && bar)["prop"], (foo && bar).prop
if (p instanceof AST_PropAccess) return p.expression === this;
// (foo && bar)``
if (p instanceof AST_Template) return p.tag === this;
// typeof (foo && bar)
if (p instanceof AST_Unary) return true;
});
function lhs_has_optional(node, output) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === node && is_lhs(p, output.parent(1))) {
// ++(foo?.bar).baz
// (foo?.()).bar = baz
do {
if (node.optional) return true;
node = node.expression;
} while (node.TYPE == "Call" || node instanceof AST_PropAccess);
}
}
PARENS(AST_PropAccess, function(output) {
var node = this;
var p = output.parent();
if (p instanceof AST_New && p.expression === node) {
if (p instanceof AST_New) {
if (p.expression !== node) return false;
// i.e. new (foo().bar)
//
// if there's one call into this subtree, then we need
@@ -776,20 +818,22 @@ function OutputStream(options) {
} while (node instanceof AST_PropAccess);
return node.TYPE == "Call";
}
return lhs_has_optional(node, output);
});
PARENS(AST_Call, function(output) {
var node = this;
var p = output.parent();
if (p instanceof AST_New) return p.expression === this;
if (p instanceof AST_New) return p.expression === node;
// https://bugs.webkit.org/show_bug.cgi?id=123506
if (output.option("webkit")) {
if (output.option("webkit")
&& node.expression instanceof AST_Function
&& p instanceof AST_PropAccess
&& p.expression === node) {
var g = output.parent(1);
return this.expression instanceof AST_Function
&& p instanceof AST_PropAccess
&& p.expression === this
&& g instanceof AST_Assign
&& g.left === p;
if (g instanceof AST_Assign && g.left === p) return true;
}
return lhs_has_optional(node, output);
});
PARENS(AST_New, function(output) {
@@ -915,7 +959,7 @@ function OutputStream(options) {
});
function print_braced_empty(self, output) {
output.print("{");
output.with_indent(output.next_indent(), function() {
output.with_indent(function() {
output.append_comments(self, true);
});
output.print("}");
@@ -1431,7 +1475,7 @@ function OutputStream(options) {
parent = output.parent(level++);
if (parent instanceof AST_Call && parent.expression === node) return;
} while (parent instanceof AST_PropAccess && parent.expression === node);
output.print("/*" + self.pure + "*/");
output.print(typeof self.pure == "string" ? "/*" + self.pure + "*/" : "/*@__PURE__*/");
}
function print_call_args(self, output) {
if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
@@ -1448,6 +1492,7 @@ function OutputStream(options) {
var self = this;
print_annotation(self, output);
self.expression.print(output);
if (self.optional) output.print("?.");
print_call_args(self, output);
});
DEFPRINT(AST_New, function(output) {
@@ -1476,26 +1521,23 @@ function OutputStream(options) {
expr.print(output);
var prop = self.property;
if (output.option("ie8") && RESERVED_WORDS[prop]) {
output.print("[");
output.print(self.optional ? "?.[" : "[");
output.add_mapping(self.end);
output.print_string(prop);
output.print("]");
} else {
if (expr instanceof AST_Number && expr.value >= 0) {
if (!/[xa-f.)]/i.test(output.last())) {
output.print(".");
}
}
output.print(".");
if (expr instanceof AST_Number && !/[ex.)]/i.test(output.last())) output.print(".");
output.print(self.optional ? "?." : ".");
// the name after dot would be mapped about here.
output.add_mapping(self.end);
output.print_name(prop);
}
});
DEFPRINT(AST_Sub, function(output) {
this.expression.print(output);
output.print("[");
this.property.print(output);
var self = this;
self.expression.print(output);
output.print(self.optional ? "?.[" : "[");
self.property.print(output);
output.print("]");
});
DEFPRINT(AST_Spread, function(output) {
@@ -1735,7 +1777,7 @@ function OutputStream(options) {
output.print("`");
});
DEFPRINT(AST_Constant, function(output) {
output.print(this.value);
output.print("" + this.value);
});
DEFPRINT(AST_String, function(output) {
output.print_string(this.value, this.quote);
@@ -1791,7 +1833,7 @@ function OutputStream(options) {
function force_statement(stat, output) {
if (output.option("braces") && !(stat instanceof AST_Const || stat instanceof AST_Let)) {
make_block(stat, output);
} else if (!stat || stat instanceof AST_EmptyStatement) {
} else if (stat instanceof AST_EmptyStatement) {
output.force_semicolon();
} else {
stat.print(output);
@@ -1842,11 +1884,11 @@ function OutputStream(options) {
}
function make_block(stmt, output) {
if (!stmt || stmt instanceof AST_EmptyStatement)
output.print("{}");
else if (stmt instanceof AST_BlockStatement)
if (stmt instanceof AST_EmptyStatement) {
print_braced_empty(stmt, output);
} else if (stmt instanceof AST_BlockStatement) {
stmt.print(output);
else output.with_block(function() {
} else output.with_block(function() {
output.indent();
stmt.print(output);
output.newline();

View File

@@ -104,12 +104,15 @@ var OPERATORS = makePredicate([
">>=",
"<<=",
">>>=",
"&=",
"|=",
"^=",
"&=",
"&&",
"||",
"??",
"&&=",
"||=",
"??=",
]);
var NEWLINE_CHARS = "\n\r\u2028\u2029";
@@ -348,7 +351,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function read_while(pred) {
var ret = "", ch;
while ((ch = peek()) && pred(ch)) ret += next();
while ((ch = peek()) && pred(ch, ret)) ret += next();
return ret;
}
@@ -356,24 +359,27 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
}
function is_octal(num) {
return /^0[0-7_]+$/.test(num);
}
function read_num(prefix) {
var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
var num = read_while(function(ch) {
var code = ch.charCodeAt(0);
switch (code) {
case 120: case 88: // xX
var num = read_while(function(ch, str) {
switch (ch) {
case "x": case "X":
return has_x ? false : (has_x = true);
case 101: case 69: // eE
case "e": case "E":
return has_x ? true : has_e ? false : (has_e = after_e = true);
case 43: case 45: // +-
case "+": case "-":
return after_e;
case (after_e = false, 46): // .
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
case (after_e = false, "."):
return has_dot || has_e || has_x || is_octal(str) ? false : (has_dot = true);
}
return is_digit(code) || /[_0-9a-fo]/i.test(ch);
return /[_0-9a-dfo]/i.test(ch);
});
if (prefix) num = prefix + num;
if (/^0[0-7_]+$/.test(num)) {
if (is_octal(num)) {
if (next_token.has_directive("use strict")) parse_error("Legacy octal literals are not allowed in strict mode");
} else {
num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
@@ -653,7 +659,7 @@ var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +");
var UNARY_POSTFIX = makePredicate("-- ++");
var ASSIGNMENT = makePredicate("= += -= /= *= %= **= >>= <<= >>>= |= ^= &=");
var ASSIGNMENT = makePredicate("= += -= /= *= %= **= >>= <<= >>>= &= |= ^= &&= ||= ??=");
var PRECEDENCE = function(a, ret) {
for (var i = 0; i < a.length;) {
@@ -851,7 +857,8 @@ function parse($TEXT, options) {
next();
return export_();
case "import":
if (!is_token(peek(), "punc", "(")) {
var token = peek();
if (!(token.type == "punc" && /^[(.]$/.test(token.value))) {
next();
return import_();
}
@@ -1182,7 +1189,7 @@ function parse($TEXT, options) {
var await = is("name", "await") && next();
expect("(");
var init = null;
if (!is("punc", ";")) {
if (await || !is("punc", ";")) {
init = is("keyword", "const")
? (next(), const_(true))
: is("keyword", "let")
@@ -1233,6 +1240,7 @@ function parse($TEXT, options) {
}
function for_enum(ctor, init) {
handle_regexp();
var obj = expression();
expect(")");
return new ctor({
@@ -1488,6 +1496,7 @@ function parse($TEXT, options) {
body.start = start;
body.end = prev();
} else {
handle_regexp();
body = expression();
semicolon();
}
@@ -2236,44 +2245,52 @@ function parse($TEXT, options) {
});
}
var subscripts = function(expr, allow_calls) {
var subscripts = function(expr, allow_calls, optional) {
var start = expr.start;
if (is("punc", ".")) {
next();
return subscripts(new AST_Dot({
start : start,
expression : expr,
property : as_name(),
end : prev()
}), allow_calls);
}
if (is("punc", "[")) {
next();
var prop = expression();
expect("]");
return subscripts(new AST_Sub({
start : start,
expression : expr,
property : prop,
end : prev()
start: start,
optional: optional,
expression: expr,
property: prop,
end: prev(),
}), allow_calls);
}
if (allow_calls && is("punc", "(")) {
next();
var call = new AST_Call({
start : start,
expression : expr,
args : expr_list(")", !options.strict),
end : prev()
start: start,
optional: optional,
expression: expr,
args: expr_list(")", !options.strict),
end: prev(),
});
return subscripts(call, true);
}
if (optional || is("punc", ".")) {
if (!optional) next();
return subscripts(new AST_Dot({
start: start,
optional: optional,
expression: expr,
property: as_name(),
end: prev(),
}), allow_calls);
}
if (is("punc", "`")) {
var tmpl = template(expr);
tmpl.start = expr.start;
tmpl.end = prev();
return subscripts(tmpl, allow_calls);
}
if (is("operator", "?") && is_token(peek(), "punc", ".")) {
next();
next();
return subscripts(expr, allow_calls, true);
}
if (expr instanceof AST_Call && !expr.pure) {
var start = expr.start;
var comments = start.comments_before;
@@ -2396,7 +2413,7 @@ function parse($TEXT, options) {
};
function is_assignable(expr) {
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
return expr instanceof AST_PropAccess && !expr.optional || expr instanceof AST_SymbolRef;
}
function to_destructured(node) {
@@ -2518,7 +2535,7 @@ function parse($TEXT, options) {
while (!is("eof"))
body.push(statement());
S.input.pop_directives_stack();
var end = prev();
var end = prev() || start;
var toplevel = options.toplevel;
if (toplevel) {
toplevel.body = toplevel.body.concat(body);

View File

@@ -81,7 +81,9 @@ var builtins = function() {
function reserve_quoted_keys(ast, reserved) {
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectProperty) {
if (node instanceof AST_ClassProperty) {
if (node.start && node.start.quote) add(node.key);
} else if (node instanceof AST_ObjectProperty) {
if (node.start && node.start.quote) add(node.key);
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
@@ -163,6 +165,8 @@ function mangle_properties(ast, options) {
addStrings(node.args[0], add);
break;
}
} else if (node instanceof AST_ClassProperty) {
if (typeof node.key == "string") add(node.key);
} else if (node instanceof AST_Dot) {
add(node.property);
} else if (node instanceof AST_ObjectProperty) {
@@ -193,6 +197,8 @@ function mangle_properties(ast, options) {
mangleStrings(node.args[0]);
break;
}
} else if (node instanceof AST_ClassProperty) {
if (typeof node.key == "string") node.key = mangle(node.key);
} else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
} else if (node instanceof AST_ObjectProperty) {
@@ -222,9 +228,7 @@ function mangle_properties(ast, options) {
}
function mangle(name) {
if (!should_mangle(name)) {
return name;
}
if (!should_mangle(name)) return name;
var mangled = cache.get(name);
if (!mangled) {
if (debug) {
@@ -236,6 +240,7 @@ function mangle_properties(ast, options) {
if (!mangled) do {
mangled = base54(++cname);
} while (!can_mangle(mangled));
if (/^#/.test(name)) mangled = "#" + mangled;
cache.set(name, mangled);
}
return mangled;

View File

@@ -273,16 +273,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
return true;
}
if (node instanceof AST_SymbolDeclaration) {
var def = node.definition();
def.preinit = def.references.length;
if (node instanceof AST_SymbolCatch) {
// ensure mangling works if `catch` reuses a scope variable
var def = node.definition().redefined();
if (def) for (var s = node.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
var redef = def.redefined();
if (redef) for (var s = node.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, redef);
if (s === redef.scope) break;
}
} else if (node instanceof AST_SymbolConst) {
// ensure compression works if `const` reuses a scope variable
var redef = node.definition().redefined();
var redef = def.redefined();
if (redef) redef.const_redefs = true;
}
if (node.name != "arguments") return true;
@@ -798,6 +800,7 @@ var base54 = (function() {
var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_");
var chars, frequency;
function reset() {
chars = null;
frequency = Object.create(freq);
}
base54.consider = function(str, delta) {
@@ -809,19 +812,15 @@ var base54 = (function() {
return frequency[b] - frequency[a];
}
base54.sort = function() {
chars = leading.sort(compare).concat(digits.sort(compare));
chars = leading.sort(compare).concat(digits).sort(compare);
};
base54.reset = reset;
reset();
function base54(num) {
var ret = "", base = 54;
num++;
do {
num--;
ret += chars[num % base];
num = Math.floor(num / base);
base = 64;
} while (num > 0);
var ret = leading[num % 54];
for (num = Math.floor(num / 54); --num >= 0; num >>= 6) {
ret += chars[num & 0x3F];
}
return ret;
}
return base54;

View File

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

View File

@@ -475,3 +475,274 @@ issue_4521: {
}
expect_stdout: "42"
}
logical_assignments: {
input: {
var a = 42, b = null, c;
a &&= "foo";
b ||= "bar";
c ??= "baz";
console.log(a, b, c);
}
expect_exact: 'var a=42,b=null,c;a&&="foo";b||="bar";c??="baz";console.log(a,b,c);'
expect_stdout: "foo bar baz"
node_version: ">=15"
}
logical_collapse_vars: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL", b = false;
a = "PASS";
b ??= a;
console.log(a);
}
expect: {
var a = "FAIL", b = false;
a = "PASS";
b ??= a;
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
logical_reduce_vars: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
logical_side_effects: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS", b = 42;
b ??= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4815_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4815_2: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect: {
var a = "PASS";
42..p &&= a = "FAIL";
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4819: {
options = {
comparisons: true,
}
input: {
console.log(void 0 === ([].p &&= 42));
}
expect: {
console.log(void 0 === ([].p &&= 42));
}
expect_stdout: "true"
node_version: ">=15"
}
issue_4827_1: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
A = "FAIL";
var a = A, b = "PASS", c;
c &&= b = a, console.log(b);
}
expect: {
A = "FAIL";
var a = A, b = "PASS", c;
c &&= b = a, console.log(b);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4827_2: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 0, b = "PASS";
function f(c) {
a++,
c &&= b = a;
}
f();
console.log(b);
}
expect: {
var a = 0, b = "PASS";
a++,
c &&= b = a;
var c;
console.log(b);
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4827_3: {
options = {
merge_vars: true,
toplevel: true,
}
input: {
var a = 0, b, c;
a++;
c &&= b = a;
console.log(b);
}
expect: {
var a = 0, b, c;
a++;
c &&= b = a;
console.log(b);
}
expect_stdout: "undefined"
node_version: ">=15"
}
issue_4876: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
}
input: {
try {
var a = null;
var b = a &&= 42;
b.p;
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
var a = null;
var b = a &&= 42;
b.p;
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4924_1: {
options = {
collapse_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a, b;
console.log("PASS");
a = function() {};
b = function() {}(b ||= a);
}
expect: {
var b;
console.log("PASS");
b = void (b ||= function() {});
}
expect_stdout: "PASS"
node_version: ">=15"
}
issue_4924_2: {
options = {
collapse_vars: true,
dead_code: true,
passes: 2,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a, b;
console.log("PASS");
a = function() {};
b = function() {}(b ||= a);
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=15"
}

View File

@@ -583,6 +583,31 @@ single_use_6: {
node_version: ">=4"
}
single_use_7: {
options = {
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
class A {
static foo() {}
}
var a = "foo" in A;
console.log(a);
}
expect: {
"use strict";
console.log("foo" in class {
static foo() {}
});
}
expect_stdout: "true"
node_version: ">=4"
}
collapse_non_strict: {
options = {
collapse_vars: true,
@@ -654,6 +679,32 @@ collapse_rhs_static: {
node_version: ">=12"
}
self_comparison: {
options = {
booleans: true,
comparisons: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
class A {}
console.log(A == A, A != A);
console.log(A === A, A !== A);
}
expect: {
"use strict";
console.log(!0, !1);
console.log(!0, !1);
}
expect_stdout: [
"true false",
"true false",
]
node_version: ">=4"
}
property_side_effects: {
options = {
inline: true,
@@ -726,7 +777,7 @@ unused_await: {
})();
}
expect_stdout: true
node_version: ">=12"
node_version: ">=12 <16"
}
computed_key_side_effects: {
@@ -1296,3 +1347,219 @@ issue_4756: {
]
node_version: ">=12"
}
issue_4821_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a;
class A {
static p = void (a = this);
}
console.log(typeof a);
}
expect: {
var a;
class A {
static p = void (a = this);
}
console.log(typeof a);
}
expect_stdout: "function"
node_version: ">=12"
}
issue_4821_2: {
options = {
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
class A {
static p = void (a = this);
}
console.log(typeof a);
}
expect: {
var a;
(class {
static p = void (a = this);
});
console.log(typeof a);
}
expect_stdout: "function"
node_version: ">=12"
}
issue_4829_1: {
options = {
properties: true,
}
input: {
"use strict";
try {
class A extends { f(){} }.f {}
} catch (e) {
console.log("PASS");
}
}
expect: {
"use strict";
try {
class A extends [ () => {} ][0] {}
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4829_2: {
options = {
properties: true,
}
input: {
"use strict";
try {
class A extends {
f() {
return arguments;
},
}.f {}
} catch (e) {
console.log("PASS");
}
}
expect: {
"use strict";
try {
class A extends {
f() {
return arguments;
},
}.f {}
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
node_version: ">=4"
}
mangle_properties: {
mangle = {
properties: {
keep_quoted: true,
},
}
input: {
class A {
static #P = "PASS";
static get Q() {
return this.#P;
}
#p(n) {
return (this["q"] = n) * this.r;
}
set q(v) {
this.r = v + 1;
}
r = this.#p(6);
}
console.log(A.Q, new A().r);
}
expect: {
class A {
static #t = "PASS";
static get s() {
return this.#t;
}
#i(t) {
return (this["q"] = t) * this.e;
}
set q(t) {
this.e = t + 1;
}
e = this.#i(6);
}
console.log(A.s, new A().e);
}
expect_stdout: "PASS 42"
node_version: ">=14.6"
}
issue_4848: {
options = {
if_return: true,
}
input: {
"use strict";
function f(a) {
a(function() {
new A();
});
if (!console)
return;
class A {
constructor() {
console.log("PASS");
}
}
}
var g;
f(function(h) {
g = h;
});
g();
}
expect: {
"use strict";
function f(a) {
a(function() {
new A();
});
if (!console)
return;
class A {
constructor() {
console.log("PASS");
}
}
}
var g;
f(function(h) {
g = h;
});
g();
}
expect_stdout: "PASS"
node_version: ">=4"
}
drop_unused_self_reference: {
options = {
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
class A {}
(A.p = A).q = console.log("PASS");
}
expect: {
"use strict";
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -2868,7 +2868,7 @@ lvalues_def: {
expect_stdout: true
}
compound_assignment: {
compound_assignment_1: {
options = {
collapse_vars: true,
}
@@ -2887,6 +2887,23 @@ compound_assignment: {
expect_stdout: "4"
}
compound_assignment_2: {
options = {
collapse_vars: true,
}
input: {
var a;
a = 1;
for (a += a + 2; console.log(a););
}
expect: {
var a;
a = 1;
for (a += a + 2; console.log(a););
}
expect_stdout: "4"
}
issue_2187_1: {
options = {
collapse_vars: true,
@@ -4707,7 +4724,7 @@ cascade_statement: {
}
function f3(a, b) {
for (; a < b; a++)
if ((c = a) && b)
if (c = a, a && b)
var c = c = b(a);
}
}
@@ -8047,6 +8064,67 @@ mangleable_var: {
expect_stdout: "PASS"
}
mangleable_assignment_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var o = {
p: function() {
return 6;
},
};
(function() {
var a, b = a = o.p();
console.log(a * (b / a + b));
})();
}
expect: {
var o = {
p: function() {
return 6;
},
};
(function() {
var a;
a = o.p();
console.log(a * (a / a + a));
})();
}
expect_stdout: "42"
}
mangleable_assignment_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var o = {
p: function() {
return 6;
},
};
(function(a, b) {
b = a = o.p();
console.log(a * (b / a + b));
})();
}
expect: {
var o = {
p: function() {
return 6;
},
};
(function(a, b) {
a = o.p();
console.log(a * (a / a + a));
})();
}
expect_stdout: "42"
}
issue_3884_1: {
options = {
collapse_vars: true,
@@ -8482,7 +8560,7 @@ issue_4047_1: {
expect: {
var b = 1;
var a;
console.log((a = --b + ((a = 0) !== typeof A), +void ((a >>= 0) && console.log("PASS"))));
console.log((a = --b + (0 !== typeof A), +void ((a >>= 0) && console.log("PASS"))));
}
expect_stdout: [
"PASS",
@@ -8880,3 +8958,290 @@ issue_4806: {
}
expect_stdout: "PASS"
}
issue_4852: {
options = {
collapse_vars: true,
}
input: {
var a = "PASS";
(function(b) {
switch (b = a) {
case 42:
try {
console;
} catch (b) {
b.p;
}
case console.log(b):
}
})("FAIL");
}
expect: {
var a = "PASS";
(function(b) {
switch (a) {
case 42:
try {
console;
} catch (b) {
b.p;
}
case console.log(a):
}
})("FAIL");
}
expect_stdout: "PASS"
}
issue_4865: {
options = {
collapse_vars: true,
}
input: {
var NaN;
var a = NaN = "PASS";
console.log(a, NaN);
}
expect: {
var NaN;
var a = NaN = "PASS";
console.log(a, NaN);
}
expect_stdout: true
}
issue_4868: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a;
(function(b) {
console.log(b[0]);
})(a = [ "PASS" ], a = [ "FAIL" ]);
}
expect: {
var a;
(function(b) {
console.log(b[0]);
})(a = [ "PASS" ], a = [ "FAIL" ]);
}
expect_stdout: "PASS"
}
issue_4874: {
options = {
collapse_vars: true,
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a;
a = null;
(function(b) {
for (var c in b = b && b[console.log("PASS")])
console;
})(a = 42);
}
expect: {
var a;
null;
(function(b) {
for (var c in a && a[console.log("PASS")])
console;
})(a = 42);
}
expect_stdout: "PASS"
}
issue_4891: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 0, b;
a++;
console.log(b = a, b);
b--;
a.a += 0;
console.log(b);
}
expect: {
var a = 0, b;
a++;
console.log(a, b = a);
b--;
a.a += 0;
console.log(b);
}
expect_stdout: [
"1 1",
"0",
]
}
issue_4895: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var a, b;
(function f() {
a = 42;
})();
console.log((b = a) || b, b += 0);
}
expect: {
var a, b;
(function f() {
a = 42;
})();
console.log((b = a) || b, b += 0);
}
expect_stdout: "42 42"
}
issue_4908: {
options = {
collapse_vars: true,
join_vars: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 0;
var b;
console || a++;
var c = d = a, d = [ c && c, d += 42 ];
console.log(d[1]);
}
expect: {
var a = 0, b;
console || a++;
var c = a, d = [ (d = a) && d, d += 42 ];
console.log(d[1]);
}
expect_stdout: "42"
}
issue_4910: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = "foo", b;
var c = b = a;
1 && c[a = "bar"];
console.log(a, b);
}
expect: {
var a = "foo", b;
var c = b = a;
1 && b[a = "bar"];
console.log(a, b);
}
expect_stdout: "bar foo"
}
issue_4914: {
options = {
collapse_vars: true,
pure_getters: "strict",
}
input: {
console.log(typeof function f() {
f.__proto__ = 42;
return f.__proto__;
}());
}
expect: {
console.log(typeof function f() {
f.__proto__ = 42;
return f.__proto__;
}());
}
expect_stdout: "function"
}
issue_4918: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
({
get 42() {
console.log(a);
}
}[a = "PASS", 42] += "PASS");
}
expect: {
var a = "FAIL";
({
get 42() {
console.log(a);
}
}[a = "PASS", 42] += "PASS");
}
expect_stdout: "PASS"
}
issue_4920: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var a = "PASS", b;
({
get PASS() {
a = "FAIL";
},
})[b = a];
console.log(b);
}
expect: {
var a = "PASS", b;
({
get PASS() {
a = "FAIL";
},
})[b = a];
console.log(b);
}
expect_stdout: "PASS"
}
issue_4935: {
options = {
collapse_vars: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
var b;
var c = b = a;
console || c(a++);
--b;
console.log(a, b);
}
expect: {
var a = 1;
var b;
var c = b = a;
console || a(a++);
--b;
console.log(a, b);
}
expect_stdout: "1 0"
}

View File

@@ -1498,3 +1498,40 @@ issue_4691: {
}
expect_stdout: "PASS"
}
issue_4848: {
options = {
if_return: true,
}
input: {
function f(a) {
a(function() {
console.log(b);
});
if (!console)
return;
const b = "PASS";
}
var g;
f(function(h) {
g = h;
});
g();
}
expect: {
function f(a) {
a(function() {
console.log(b);
});
if (!console)
return;
const b = "PASS";
}
var g;
f(function(h) {
g = h;
});
g();
}
expect_stdout: "PASS"
}

View File

@@ -1661,3 +1661,71 @@ issue_4588_2_evaluate: {
expect_stdout: "1"
node_version: ">=6"
}
issue_4817: {
options = {
ie8: true,
inline: true,
unused: true,
}
input: {
(function f(a = console.log(typeof f)) {
return 42;
})();
}
expect: {
(function f(a = console.log(typeof f)) {
return 42;
})();
}
expect_stdout: "function"
node_version: ">=6"
}
issue_4854: {
options = {
collapse_vars: true,
inline: true,
side_effects: true,
unused: true,
}
input: {
console.log(function(a) {
(function(b = a = "foo") {
[] = "foo";
})();
a;
}());
}
expect: {
console.log(void ([] = "foo"));
}
expect_stdout: "undefined"
node_version: ">=6"
}
issue_4916: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
}
input: {
var log = console.log;
(function(b = "foo") {
b.value = "FAIL";
b;
log(b.value);
})();
}
expect: {
var log = console.log;
(function(b = "foo") {
b.value = "FAIL";
b;
log(b.value);
})();
}
expect_stdout: "undefined"
node_version: ">=6"
}

View File

@@ -2371,6 +2371,7 @@ function_parameter_ie8: {
issue_3664: {
options = {
pure_getters: "strict",
side_effects: true,
unused: true,
}
input: {
@@ -2381,7 +2382,7 @@ issue_3664: {
}
expect: {
console.log(function() {
var b = ([ b && console.log("FAIL") ].p = 0, 0);
var a, b = (a = (a = [ b && console.log("FAIL") ]).p = 0, 0);
return "PASS";
}());
}
@@ -2391,6 +2392,7 @@ issue_3664: {
issue_3673: {
options = {
pure_getters: "strict",
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
@@ -2401,8 +2403,6 @@ issue_3673: {
console.log("PASS");
}
expect: {
var a;
(a = [ a ]).p = 42;
console.log("PASS");
}
expect_stdout: "PASS"
@@ -3357,3 +3357,116 @@ issue_4806_3: {
}
expect_stdout: "PASS"
}
issue_4834: {
options = {
inline: true,
keep_fargs: false,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
try {
new function(a, b) {
b;
b.p;
}(42);
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
void b.p;
} catch (e) {
console.log("PASS");
}
var b;
}
expect_stdout: "PASS"
}
issue_4912_1: {
options = {
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = A = function() {};
A;
a.prototype = {
f: function() {
console.log("PASS");
},
};
new A().f();
}
expect: {
var a = A = function() {};
A;
a.prototype = {
f: function() {
console.log("PASS");
},
};
new A().f();
}
expect_stdout: "PASS"
}
issue_4912_2: {
options = {
pure_getters: "strict",
unused: true,
}
input: {
console.log(function() {
var g, f = function() {};
f.p = {};
(g = f.p.q = function() {}).r = "PASS";
return f;
}().p.q.r);
}
expect: {
console.log(function() {
var g, f = function() {};
f.p = {};
(f.p.q = function() {}).r = "PASS";
return f;
}().p.q.r);
}
expect_stdout: "PASS"
}
issue_4912_3: {
options = {
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
console.log(function(f, g) {
f = function() {};
f.p = {};
g = f.p.q = function() {};
g.r = "PASS";
return f;
}().p.q.r);
}
expect: {
console.log(function(f, g) {
f = function() {};
f.p = {};
g = f.p.q = function() {};
g.r = "PASS";
return f;
}().p.q.r);
}
expect_stdout: "PASS"
}

View File

@@ -3181,3 +3181,23 @@ issue_4552: {
}
expect_stdout: "NaN"
}
issue_4886: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("length" in {
__proto__: function() {},
length: void 0,
});
}
expect: {
console.log("length" in {
__proto__: function() {},
length: void 0,
});
}
expect_stdout: "true"
}

View File

@@ -99,8 +99,8 @@ issue_4664: {
expect: {
(function f() {
new function(a) {
console.log(typeof f, 2 ** 30, typeof this);
}(0, A = 0);
console.log(typeof f, 1073741824, typeof this);
}(A = 0);
})();
}
expect_stdout: "function 1073741824 object"

View File

@@ -91,6 +91,13 @@ defaults_parentheses_6: {
expect_exact: 'export default(function(){while(!console);})()?"FAIL":"PASS";'
}
defaults_regexp: {
input: {
export default /foo/;
}
expect_exact: "export default/foo/;"
}
foreign: {
input: {
export * from "foo";

View File

@@ -5991,3 +5991,220 @@ issue_4788: {
}
expect_stdout: "PASS"
}
issue_4823: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(typeof function() {
{
function f() {}
var arguments = f();
function g() {}
var arguments = g;
}
return f && arguments;
}());
}
expect: {
console.log(typeof function() {
{
function f() {}
f();
var arguments = function() {};
}
return f && arguments;
}());
}
expect_stdout: "function"
}
drop_unused_self_reference: {
options = {
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f() {}
(f.p = f).q = console.log("PASS");
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
reduce_cross_reference_1: {
options = {
passes: 3,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(a, b) {
a = b = function() {};
a.p = a;
b = a = function() {};
b.q = b;
})();
}
expect: {}
expect_stdout: true
}
reduce_cross_reference_1_toplevel: {
options = {
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = b = function() {};
a.p = a;
var b = a = function() {};
b.q = b;
}
expect: {}
expect_stdout: true
}
reduce_cross_reference_2: {
options = {
collapse_vars: true,
passes: 3,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(a, b) {
a = b = function() {};
b.p = a;
b = a = function() {};
a.q = b;
})();
}
expect: {}
expect_stdout: true
}
reduce_cross_reference_2_toplevel: {
options = {
collapse_vars: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = b = function() {};
b.p = a;
var b = a = function() {};
a.q = b;
}
expect: {}
expect_stdout: true
}
reduce_cross_reference_3: {
options = {
collapse_vars: true,
passes: 3,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(a, b) {
a = b = function() {};
a.p = b;
b = a = function() {};
b.q = a;
})();
}
expect: {}
expect_stdout: true
}
reduce_cross_reference_3_toplevel: {
options = {
collapse_vars: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = b = function() {};
a.p = b;
var b = a = function() {};
b.q = a;
}
expect: {}
expect_stdout: true
}
reduce_cross_reference_4: {
options = {
passes: 3,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function(a, b) {
a = b = function() {};
b.p = b;
b = a = function() {};
a.q = a;
})();
}
expect: {}
expect_stdout: true
}
reduce_cross_reference_4_toplevel: {
options = {
passes: 2,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = b = function() {};
b.p = b;
var b = a = function() {};
a.q = a;
}
expect: {}
expect_stdout: true
}

View File

@@ -140,7 +140,6 @@ issue_4487: {
functions: true,
hoist_vars: true,
keep_fnames: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
@@ -240,3 +239,160 @@ issue_4736: {
}
expect_stdout: "1073741824"
}
issue_4839: {
options = {
evaluate: true,
hoist_vars: true,
keep_fargs: false,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = function(a, b) {
return b && b;
}("foo");
for (var k in o)
throw "FAIL";
console.log("PASS");
}
expect: {
var k, o = void 0;
for (k in o)
throw "FAIL";
console.log("PASS");
}
expect_stdout: "PASS"
}
issue_4859: {
options = {
evaluate: true,
hoist_vars: true,
keep_infinity: true,
merge_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(a) {
var b = (a = 2, 1 / 0), c = 3;
var d = a + b;
console.log(d);
return f;
}
f();
}
expect: {
(function f(a) {
var d = 1 / 0, d = Infinity;
console.log(d);
return f;
})();
}
expect_stdout: "Infinity"
}
issue_4893_1: {
options = {
collapse_vars: true,
evaluate: true,
hoist_vars: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
function g() {}
var a = null;
var b = null;
var c = null;
b.p += a = 42;
f;
}
try {
f();
} catch (e) {
console.log("PASS");
}
}
expect: {
try{
(function f() {
var b;
b = null;
b.p += 42;
f;
})();
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}
issue_4893_2: {
options = {
collapse_vars: true,
hoist_vars: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function f() {
function g() {}
var a = null;
var b = null;
var c = null;
b.p += a = 42;
f;
}
try {
f();
} catch (e) {
console.log("PASS");
}
}
expect: {
try{
(function() {
var b;
b = null;
b.p += 42;
})();
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}
issue_4898: {
options = {
collapse_vars: true,
evaluate: true,
hoist_vars: true,
loops: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
do {
var b = [ console.log("PASS") ];
var c = b;
} while (c.p = 0);
}
expect: {
var b;
b = [ console.log("PASS") ];
b.p = 0;
}
expect_stdout: "PASS"
}

View File

@@ -2653,7 +2653,9 @@ issue_4019: {
try {
console.log("FAIL");
} catch (o) {}
}, o = (console.log(o.length), ++o);
};
console.log(o.length),
++o;
}
expect_stdout: "0"
}
@@ -2945,3 +2947,53 @@ issue_4729: {
}
expect_stdout: "PASS"
}
issue_4928_1: {
options = {
ie8: true,
toplevel: true,
unused: true,
}
input: {
var a = function f() {
f(a);
};
console.log(typeof f);
}
expect: {
var a = function f() {
f(a);
};
console.log(typeof f);
}
expect_stdout: "undefined"
}
issue_4928_2: {
options = {
ie8: true,
toplevel: true,
unused: true,
}
input: {
switch (42) {
case console:
var a = function f() {
f(a);
};
case 42:
var a = console.log("PASS");
}
}
expect: {
switch (42) {
case console:
var a = function f() {
f(a);
};
case 42:
a = console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -54,13 +54,22 @@ dynamic_nought: {
expect_exact: "import(foo);"
}
import_meta: {
import_meta_1: {
input: {
console.log(import.meta, import.meta.url);
}
expect_exact: "console.log(import.meta,import.meta.url);"
}
import_meta_2: {
input: {
import.meta.url.split("/").forEach(function(part, index) {
console.log(index, part);
});
}
expect_exact: 'import.meta.url.split("/").forEach(function(part,index){console.log(index,part)});'
}
same_quotes: {
beautify = {
beautify: true,

View File

@@ -1024,7 +1024,7 @@ issue_3856: {
expect_stdout: "undefined"
}
issue_3916: {
issue_3916_1: {
options = {
join_vars: true,
}
@@ -1044,8 +1044,8 @@ issue_3916: {
var o = {
p: "PASS",
__proto__: 42,
q: "FAIL",
};
o.q = "FAIL";
o.__proto__ = {
p: "FAIL",
q: "PASS",
@@ -1056,6 +1056,62 @@ issue_3916: {
expect_stdout: "object PASS true PASS"
}
issue_3916_2: {
options = {
join_vars: true,
}
input: {
var log = console.log, o = {};
o.p = "FAIL 1";
o.__proto__ = {
get p() {
return "FAIL 2";
},
set p(u) {
log("FAIL 3");
},
set q(v) {
log("PASS 1");
},
get q() {
return "PASS 3";
},
};
o.p = "PASS 2";
o.q = "FAIL 4";
log(o.p);
log(o.q);
}
expect: {
var log = console.log, o = {
p: "FAIL 1",
__proto__: {
get p() {
return "FAIL 2";
},
set p(u) {
log("FAIL 3");
},
set q(v) {
log("PASS 1");
},
get q() {
return "PASS 3";
},
},
};
o.p = "PASS 2";
o.q = "FAIL 4";
log(o.p);
log(o.q);
}
expect_stdout: [
"PASS 1",
"PASS 2",
"PASS 3",
]
}
assign_var: {
options = {
join_vars: true,

View File

@@ -1506,3 +1506,43 @@ issue_4691: {
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4848: {
options = {
if_return: true,
}
input: {
"use strict";
function f(a) {
a(function() {
console.log(b);
});
if (!console)
return;
let b = "PASS";
}
var g;
f(function(h) {
g = h;
});
g();
}
expect: {
"use strict";
function f(a) {
a(function() {
console.log(b);
});
if (!console)
return;
let b = "PASS";
}
var g;
f(function(h) {
g = h;
});
g();
}
expect_stdout: "PASS"
node_version: ">=4"
}

View File

@@ -829,6 +829,18 @@ empty_for_in_prop_init: {
}
for_of: {
input: {
var a = [ "PASS", 42 ];
a.p = "FAIL";
for (a of (null, a))
console.log(a);
}
expect_exact: 'var a=["PASS",42];a.p="FAIL";for(a of(null,a))console.log(a);'
expect_stdout: true
node_version: ">=0.12"
}
for_async_of: {
input: {
var async = [ "PASS", 42 ];
async.p = "FAIL";
@@ -837,7 +849,21 @@ for_of: {
}
expect_exact: 'var async=["PASS",42];async.p="FAIL";for(async of(null,async))console.log(async);'
expect_stdout: true
node_version: ">=0.12"
node_version: ">=0.12 <16"
}
for_of_regexp: {
input: {
for (var a of /foo/);
}
expect_exact: "for(var a of/foo/);"
}
for_await_of_regexp: {
input: {
for await (var a of /foo/);
}
expect_exact: "for await(var a of/foo/);"
}
issue_3631_1: {

View File

@@ -104,6 +104,40 @@ parentheses_for_prototype_functions_galio: {
expect_stdout: true
}
octal: {
beautify = {
beautify: true,
}
input: {
(function() {
console.log(052);
console.log(-052);
console.log(018);
console.log(-018);
console.log(052.toFixed(0));
console.log(-052.toFixed(0));
console.log(018..toFixed(0));
console.log(-018..toFixed(0));
})();
}
expect_exact: [
"(function() {",
" console.log(42);",
" console.log(-42);",
" console.log(18);",
" console.log(-18);",
" console.log(42..toFixed(0));",
" console.log(-42..toFixed(0));",
" console.log(18..toFixed(0));",
" console.log(-18..toFixed(0));",
"})();",
]
expect_stdout: true
}
comparisons: {
options = {
comparisons: true,

View File

@@ -0,0 +1,277 @@
call: {
input: {
console.log?.(undefined?.(console.log("FAIL")));
}
expect_exact: 'console.log?.((void 0)?.(console.log("FAIL")));'
expect_stdout: "undefined"
node_version: ">=14"
}
dot: {
input: {
console?.log((void 0)?.p);
}
expect_exact: "console?.log((void 0)?.p);"
expect_stdout: "undefined"
node_version: ">=14"
}
dot_in: {
input: {
var o = { in: 42 };
console.log(o.in, o?.in);
}
expect_exact: "var o={in:42};console.log(o.in,o?.in);"
expect_stdout: "42 42"
node_version: ">=14"
}
sub: {
input: {
console?.["log"](null?.[console.log("FAIL")]);
}
expect_exact: 'console?.["log"](null?.[console.log("FAIL")]);'
expect_stdout: "undefined"
node_version: ">=14"
}
ternary_decimal: {
input: {
null ? .42 : console.log("PASS");
}
expect_exact: 'null?.42:console.log("PASS");'
expect_stdout: "PASS"
}
assign_parentheses_call: {
input: {
var o = {};
((() => o)?.()).p = "PASS";
console.log(o.p);
}
expect_exact: 'var o={};((()=>o)?.()).p="PASS";console.log(o.p);'
expect_stdout: "PASS"
node_version: ">=14"
}
assign_parentheses_dot: {
input: {
(console?.log).name.p = console.log("PASS");
}
expect_exact: '(console?.log.name).p=console.log("PASS");'
expect_stdout: "PASS"
node_version: ">=14"
}
assign_no_parentheses: {
input: {
console[console.log?.("PASS")] = 42;
}
expect_exact: 'console[console.log?.("PASS")]=42;'
expect_stdout: "PASS"
node_version: ">=14"
}
unary_parentheses: {
input: {
var o = { p: 41 };
(function() {
return o;
}?.()).p++;
console.log(o.p);
}
expect_exact: "var o={p:41};(function(){return o}?.()).p++;console.log(o.p);"
expect_stdout: "42"
node_version: ">=14"
}
collapse_vars_1: {
options = {
collapse_vars: true,
}
input: {
var a;
A = 42;
a?.[42];
console.log(typeof A);
}
expect: {
var a;
A = 42;
a?.[42];
console.log(typeof A);
}
expect_stdout: "number"
node_version: ">=14"
}
collapse_vars_2: {
options = {
collapse_vars: true,
}
input: {
var a;
A = 42;
a?.(42);
console.log(typeof A);
}
expect: {
var a;
A = 42;
a?.(42);
console.log(typeof A);
}
expect_stdout: "number"
node_version: ">=14"
}
properties: {
options = {
evaluate: true,
properties: true,
}
input: {
var a;
console.log(a?.["FAIL"]);
}
expect: {
var a;
console.log(a?.FAIL);
}
expect_stdout: "undefined"
node_version: ">=14"
}
reduce_vars_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
null?.[a = 0];
console.log(a ? "PASS" : "FAIL");
}
expect: {
var a = 1;
null?.[a = 0];
console.log(a ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=14"
}
reduce_vars_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
null?.(a = 0);
console.log(a ? "PASS" : "FAIL");
}
expect: {
var a = 1;
null?.(a = 0);
console.log(a ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
node_version: ">=14"
}
side_effects: {
options = {
side_effects: true,
}
input: {
var a;
a?.[a = "FAIL"];
console.log(a);
}
expect: {
var a;
a?.[a = "FAIL"];
console.log(a);
}
expect_stdout: "undefined"
node_version: ">=14"
}
trim_1: {
options = {
evaluate: true,
optional_chains: true,
reduce_vars: true,
unsafe: true,
}
input: {
(function(a, b) {
console?.log?.(a?.p, b?.[console.log("FAIL")]);
})?.({ p: "PASS" });
}
expect: {
(function(a, b) {
console?.log?.(a.p, void 0);
})({ p: "PASS" });
}
expect_stdout: "PASS undefined"
node_version: ">=14"
}
trim_2: {
options = {
evaluate: true,
optional_chains: true,
side_effects: true,
}
input: {
(void console.log("PASS"))?.[console.log("FAIL")];
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=14"
}
issue_4906: {
options = {
toplevel: true,
unused: true,
}
input: {
do {
var a = a?.[42];
} while (console.log("PASS"));
}
expect: {
do {} while (console.log("PASS"));
}
expect_stdout: "PASS"
node_version: ">=14"
}
issue_4928: {
options = {
ie8: true,
toplevel: true,
unused: true,
}
input: {
var a = a?.[function f() {
f(a);
}];
console.log(typeof f);
}
expect: {
var a = a?.[function f() {
f(a);
}];
console.log(typeof f);
}
expect_stdout: "undefined"
node_version: ">=14"
}

View File

@@ -1400,3 +1400,66 @@ object_super: {
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4831_1: {
options = {
properties: true,
}
input: {
console.log({
f() {
return arguments;
},
}.f("PASS")[0]);
}
expect: {
console.log([
function() {
return arguments;
},
][0]("PASS")[0]);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4831_2: {
options = {
properties: true,
}
input: {
var f = {
f() {
return arguments;
},
}.f;
console.log(f("PASS")[0]);
}
expect: {
var f = {
f() {
return arguments;
},
}.f;
console.log(f("PASS")[0]);
}
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4888: {
options = {
properties: true,
}
input: {
console.log(typeof {
__proto__: 42,
}.__proto__);
}
expect: {
console.log(typeof {
__proto__: 42,
}.__proto__);
}
expect_stdout: "object"
}

View File

@@ -133,7 +133,7 @@ conditional: {
relational: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
side_effects: true,
}
input: {
foo() in new foo();
@@ -158,7 +158,7 @@ relational: {
arithmetic: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
side_effects: true,
}
input: {
foo() + foo();
@@ -183,7 +183,7 @@ arithmetic: {
boolean_and: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
side_effects: true,
}
input: {
foo() && foo();
@@ -208,7 +208,7 @@ boolean_and: {
boolean_or: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
side_effects: true,
}
input: {
foo() || foo();
@@ -233,7 +233,7 @@ boolean_or: {
assign: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
side_effects: true,
}
input: {
var a;
@@ -256,7 +256,7 @@ assign: {
unary: {
options = {
pure_funcs: [ "foo" ],
side_effects :true,
side_effects: true,
}
input: {
typeof foo();

View File

@@ -1564,3 +1564,103 @@ issue_4803: {
}
expect_stdout: "PASS"
}
nested_property_assignments_1: {
options = {
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var f;
((f = function() {
console.log("FAIL");
}).p = f).q = console.log("PASS");
}
expect: {
console.log("PASS");
}
expect_stdout: "PASS"
}
nested_property_assignments_2: {
options = {
pure_getters: "strict",
unused: true,
}
input: {
var o = {};
(function() {
var a;
(o.p = a = {}).q = "PASS";
})();
console.log(o.p.q);
}
expect: {
var o = {};
(function() {
(o.p = {}).q = "PASS";
})();
console.log(o.p.q);
}
expect_stdout: "PASS"
}
nested_property_assignments_3: {
options = {
collapse_vars: true,
pure_getters: true,
side_effects: true,
unused: true,
}
input: {
var o = { p: {} };
(function(a) {
console && a;
if (console) {
a = a.p;
a.q = a;
}
})(o);
console.log(o.p.q === o.p ? "PASS" : "FAIL");
}
expect: {
var o = { p: {} };
(function(a) {
console;
if (console)
(a = a.p).q = a;
})(o);
console.log(o.p.q === o.p ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
issue_4939: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
({
__proto__: {
get p() {
console.log("PASS");
},
},
}).p;
}
expect: {
({
__proto__: {
get p() {
console.log("PASS");
},
},
}).p;
}
expect_stdout: "PASS"
}

View File

@@ -7631,3 +7631,94 @@ issue_4568: {
}
expect_stdout: "PASS"
}
issue_4937: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
while (console.log("PASS"));
}
do {
function g() {
f();
}
} while (!g);
f();
}
expect: {
function f() {
while (console.log("PASS"));
}
do {
function g() {
f();
}
} while (!g);
f();
}
expect_stdout: "PASS"
}
issue_4943_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a, b = 1;
(function f() {
a = "foo";
b-- && f();
console.log(a);
a = "bar";
})();
}
expect: {
var a, b = 1;
(function f() {
a = "foo";
b-- && f();
console.log(a);
a = "bar";
})();
}
expect_stdout: [
"foo",
"bar",
]
}
issue_4943_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a, b = 1;
(function f() {
a = "foo";
b-- && f();
console.log(a);
a = "bar";
})();
}
expect: {
var a, b = 1;
(function f() {
a = "foo";
b-- && f();
console.log(a);
a = "bar";
})();
}
expect_stdout: [
"foo",
"bar",
]
}

View File

@@ -172,3 +172,32 @@ issue_4054: {
}
expect_stdout: "{ p: [Setter] }"
}
issue_4811_1: {
input: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect_stdout: "PASS [object global] true"
}
issue_4811_2: {
options = {
side_effects: true,
}
input: {
(async function() {});
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect: {
for (var PASS in this);
console.log(PASS, this, {} < this);
}
expect_stdout: "PASS [object global] true"
node_version: ">=8"
}

View File

@@ -198,6 +198,36 @@ global_fns: {
]
}
global_constructors: {
options = {
side_effects: true,
unsafe: true,
}
input: {
Map;
new Map(console.log("foo"));
Set;
new Set(console.log("bar"));
WeakMap;
new WeakMap(console.log("baz"));
WeakSet;
new WeakSet(console.log("moo"));
}
expect: {
console.log("foo");
console.log("bar");
console.log("baz");
console.log("moo");
}
expect_stdout: [
"foo",
"bar",
"baz",
"moo",
]
node_version: ">=0.12"
}
unsafe_builtin_1: {
options = {
side_effects: true,

View File

@@ -1045,3 +1045,114 @@ issue_4614: {
expect_stdout: true
node_version: ">=6"
}
issue_4849: {
options = {
reduce_vars: true,
unused: true,
}
input: {
while (function() {
while (!console);
}(new function(a) {
console.log(typeof { ...a });
}(function() {})));
}
expect: {
while (function() {
while (!console);
}(function(a) {
console.log(typeof { ...function() {} });
}()));
}
expect_stdout: "object"
node_version: ">=8"
}
issue_4882_1: {
options = {
objects: true,
spreads: true,
}
input: {
var o = {
p: "PASS",
... {
__proto__: {
p: "FAIL 1",
q: "FAIL 2",
},
},
};
console.log(o.p);
console.log(o.q);
}
expect: {
var o = {
p: "PASS",
};
console.log(o.p);
console.log(o.q);
}
expect_stdout: [
"PASS",
"undefined",
]
node_version: ">=8"
}
issue_4882_2: {
options = {
objects: true,
spreads: true,
}
input: {
console.log(null == Object.getPrototypeOf({
... {
__proto__: (console.log(42), null),
},
}) ? "FAIL" : "PASS");
}
expect: {
console.log(null == Object.getPrototypeOf({
... {
__proto__: (console.log(42), null),
},
}) ? "FAIL" : "PASS");
}
expect_stdout: [
"42",
"PASS",
]
node_version: ">=8"
}
issue_4882_3: {
options = {
objects: true,
spreads: true,
}
input: {
var o = {
__proto__: { p: 42 },
... {
set __proto__(v) {},
},
};
console.log(o.__proto__ === Object.getPrototypeOf(o) ? "FAIL" : "PASS");
console.log(o.p);
}
expect: {
var o = {
__proto__: { p: 42 },
["__proto__"]: void 0,
};
console.log(o.__proto__ === Object.getPrototypeOf(o) ? "FAIL" : "PASS");
console.log(o.p);
}
expect_stdout: [
"PASS",
"42",
]
node_version: ">=8"
}

View File

@@ -62,6 +62,23 @@ tag_parentheses_arrow: {
node_version: ">=4"
}
tag_parentheses_binary: {
options = {
collapse_vars: true,
toplevel: true,
unused: true,
}
input: {
var f = function() {
console.log("PASS");
} || console
f``;
}
expect_exact: '(function(){console.log("PASS")}||console)``;'
expect_stdout: "PASS"
node_version: ">=4"
}
tag_parentheses_new: {
input: {
(new function() {
@@ -87,6 +104,21 @@ tag_parentheses_sequence: {
node_version: ">=4"
}
tag_parentheses_unary: {
input: {
var a;
try {
(~a)``;
(a++)``;
} catch (e) {
console.log("PASS");
}
}
expect_exact: 'var a;try{(~a)``;(a++)``}catch(e){console.log("PASS")}'
expect_stdout: "PASS"
node_version: ">=4"
}
malformed_escape: {
input: {
(function(s) {
@@ -343,7 +375,7 @@ issue_4676: {
reduce_vars: true,
templates: true,
toplevel: true,
unsafe:true,
unsafe: true,
unused: true,
}
input: {
@@ -366,3 +398,24 @@ issue_4676: {
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4931: {
options = {
evaluate: true,
templates: true,
unsafe: true,
}
input: {
console.log(String.raw`${typeof A} ${"\r"}`);
console.log(String.raw`${"\\"} ${"`"}`);
}
expect: {
console.log(String.raw`${typeof A} ${"\r"}`);
console.log("\\ `");
}
expect_stdout: [
"undefined \r",
"\\ `",
]
node_version: ">=4"
}

View File

@@ -523,3 +523,55 @@ default_init: {
expect_stdout: "PASS"
node_version: ">=4"
}
issue_4933_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
varify: true,
}
input: {
console.log(f());
function f() {
var a;
for (console in a = [ f ]) {
const b = a;
}
}
}
expect: {
console.log(function f() {
var a;
for (console in a = [ f ]) {
const b = a;
}
}());
}
expect_stdout: "undefined"
}
issue_4933_2: {
options = {
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
varify: true,
}
input: {
console.log(f());
function f() {
var a;
for (console in a = [ f ]) {
const b = a;
}
}
}
expect: {
console.log(function f() {
for (console in [ f ]);
}());
}
expect_stdout: "undefined"
}

View File

@@ -150,6 +150,27 @@ for_await_of: {
node_version: ">=10"
}
comment_newline: {
beautify = {
comments: "all",
}
input: {
console.log(function*() {
yield (
/* */
"PASS"
);
}().next().value);
}
expect_exact: [
"console.log(function*(){",
"/* */",
'yield"PASS"}().next().value);',
]
expect_stdout: "PASS"
node_version: ">=4"
}
collapse_vars_1: {
options = {
collapse_vars: true,

View File

@@ -1 +1 @@
++null
console.log(4 || (null = 4));

View File

@@ -0,0 +1 @@
console.log(5 || ([]?.length ^= 5));

View File

@@ -0,0 +1 @@
for await (; console.log(42););

View File

@@ -427,16 +427,30 @@ describe("bin/uglifyjs", function() {
done();
});
});
it("Should throw syntax error (++null)", function(done) {
it("Should throw syntax error (null = 4)", function(done) {
var command = uglifyjscmd + " test/input/invalid/assign_4.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_4.js:1,0",
"++null",
"^",
"ERROR: Invalid use of ++ operator",
"Parse error at test/input/invalid/assign_4.js:1,23",
"console.log(4 || (null = 4));",
" ^",
"ERROR: Invalid assignment",
].join("\n"));
done();
});
});
it("Should throw syntax error ([]?.length ^= 5)", function(done) {
var command = uglifyjscmd + " test/input/invalid/assign_5.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/assign_5.js:1,29",
"console.log(5 || ([]?.length ^= 5));",
" ^",
"ERROR: Invalid assignment",
].join("\n"));
done();
});
@@ -679,6 +693,20 @@ describe("bin/uglifyjs", function() {
done();
});
});
it("Should throw syntax error (for-await)", function(done) {
var command = uglifyjscmd + " test/input/invalid/for-await.js";
exec(command, function(err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-await.js:1,11",
"for await (; console.log(42););",
" ^",
"ERROR: Unexpected token: punc «;»",
].join("\n"));
done();
});
});
it("Should throw syntax error (switch defaults)", function(done) {
var command = uglifyjscmd + " test/input/invalid/switch.js";
exec(command, function(err, stdout, stderr) {

View File

@@ -7,11 +7,11 @@ describe("let", function() {
// Produce a lot of variables in a function and run it through mangle.
var s = '"dddddeeeeelllllooooottttt"; function foo() {';
for (var i = 0; i < 18000; i++) {
s += "var v" + i + "=0;";
s += "var v" + i + "=[];";
}
s += '}';
var result = UglifyJS.minify(s, {
compress: false
compress: false,
}).code;
// Verify that select keywords and reserved keywords not produced

View File

@@ -183,6 +183,24 @@ describe("test/reduce.js", function() {
"// }",
].join("\n"));
});
it("Should reduce `for (const ... in ...)` without invalid intermediate AST", function() {
if (semver.satisfies(process.version, "<4")) return;
var code = [
"var a = 0;",
"",
"for (const b in [ 1, 2, 3 ]) {",
" a = +a + 1 - .2;",
" console.log(a);",
"}",
].join("\n");
var result = reduce_test(code, {
compress: {
unsafe_math: true,
},
});
if (result.error) throw result.error;
assert.deepEqual(result.warnings, []);
});
it("Should reduce infinite loops with reasonable performance", function() {
if (semver.satisfies(process.version, "<=0.10")) return;
this.timeout(120000);
@@ -361,4 +379,39 @@ describe("test/reduce.js", function() {
if (result.error) throw result.error;
assert.strictEqual(result.code, read("test/input/reduce/destructured_catch.reduced.js"));
});
it("Should not enumerate `toString` over global context", function() {
if (semver.satisfies(process.version, "<8")) return;
var code = [
"(async function() {});",
"for (var k in this);",
"console.log(k, 42 + this);",
].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 reduce object with method syntax without invalid intermediate AST", function() {
if (semver.satisfies(process.version, "<4")) return;
var code = [
"console.log({",
" f() {",
" return 1 - .8;",
" },",
"}.f());",
].join("\n");
var result = reduce_test(code, {
compress: {
unsafe_math: true,
},
});
if (result.error) throw result.error;
assert.deepEqual(result.warnings, []);
});
});

View File

@@ -5,23 +5,23 @@ var acorn = require("acorn");
var ufuzz = require("./ufuzz");
var UglifyJS = require("..");
function try_beautify(code) {
var beautified = UglifyJS.minify(code, {
function beautify(ast) {
var beautified = UglifyJS.minify(ast, {
compress: false,
mangle: false,
output: {
beautify: true,
braces: true
}
braces: true,
},
});
if (beautified.error) return beautified;
return UglifyJS.minify(beautified.code, {
compress: false,
mangle: false,
output: {
ast: true,
},
});
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
console.log(code);
} else {
console.log("// (beautified)");
console.log(beautified.code);
}
}
function validate(ast) {
@@ -35,16 +35,55 @@ function validate(ast) {
return UglifyJS.minify(ast, {
compress: false,
mangle: false,
output: {
ast: true,
},
validate: true,
});
}
function test(original, estree, description) {
var transformed = validate(UglifyJS.AST_Node.from_mozilla_ast(estree));
if (transformed.error || original !== transformed.code) {
function patch_import(code) {
return code.replace(/\bimport\s*\{\s*\}\s*from\s*(['"])/g, "import$1")
.replace(/\b(import\b.*?)\s*,\s*\{\s*\}\s*(from\s*['"])/g, "$1 $2");
}
function equals(input, transformed) {
if (input.code === transformed.code) return true;
return patch_import(input.code) === patch_import(transformed.code);
}
function test(input, to_moz, description, skip_on_error, beautified) {
try {
var ast = UglifyJS.AST_Node.from_mozilla_ast(to_moz(input));
} catch (e) {
if (skip_on_error) return true;
console.log("//=============================================================");
console.log("//", description, "failed... round", round);
console.log(e);
console.log("// original code");
if (beautified === true) console.log("// (beautified)");
console.log(input.code);
return false;
}
var transformed = validate(ast);
if (transformed.error || !equals(input, transformed)) {
if (!beautified) {
beautified = beautify(input.ast);
if (!beautified.error) {
beautified.raw = beautified.code;
if (!test(beautified, to_moz, description, skip_on_error, true)) return false;
}
}
console.log("//=============================================================");
console.log("// !!!!!! Failed... round", round);
console.log("// original code");
try_beautify(original);
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
} else if (beautified === true) {
console.log("// (beautified)");
}
console.log(input.raw);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
@@ -52,7 +91,15 @@ function test(original, estree, description) {
if (transformed.error) {
console.log(transformed.error.stack);
} else {
try_beautify(transformed.code);
beautified = beautify(transformed.ast);
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(beautified.error.stack);
console.log(transformed.code);
} else {
console.log("// (beautified)");
console.log(beautified.code);
}
}
console.log("!!!!!! Failed... round", round);
return false;
@@ -67,24 +114,33 @@ for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
var code = ufuzz.createTopLevelCode();
minify_options.forEach(function(options) {
var input = options ? UglifyJS.minify(code, JSON.parse(options)).code : code;
var uglified = UglifyJS.minify(input, {
var ok = true;
var input = UglifyJS.minify(options ? UglifyJS.minify(code, JSON.parse(options)).code : code, {
compress: false,
mangle: false,
output: {
ast: true
}
ast: true,
},
});
var ok = test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()");
try {
ok = test(uglified.code, acorn.parse(input), "acorn.parse()") && ok;
} catch (e) {
input.raw = options ? input.code : code;
if (input.error) {
ok = false;
console.log("//=============================================================");
console.log("// acorn parser failed... round", round);
console.log(e);
console.log("// minify() failed... round", round);
console.log(input.error);
console.log("// original code");
console.log(input);
console.log(code);
}
if (ok) ok = test(input, function(input) {
return input.ast.to_mozilla_ast();
}, "AST_Node.to_mozilla_ast()");
if (ok) ok = test(input, function(input) {
return acorn.parse(input.raw, {
ecmaVersion: "latest",
locations: true,
sourceType: "module",
});
}, "acorn.parse()", !ufuzz.verbose);
if (!ok) process.exit(1);
});
}

View File

@@ -132,6 +132,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return;
}
if (parent instanceof U.AST_VarDef && parent.name === node) return;
// preserve class methods
if (parent instanceof U.AST_ClassMethod && parent.value === node) return;
// preserve exports
if (parent instanceof U.AST_ExportDeclaration) return;
if (parent instanceof U.AST_ExportDefault) return;
@@ -147,6 +149,9 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node;
// preserve for (xxx in/of ...)
if (parent instanceof U.AST_ForEnumeration && parent.init === node) return node;
// preserve super(...)
if (node.TYPE == "Call" && node.expression instanceof U.AST_Super) return;
if (node instanceof U.AST_Super && parent.TYPE == "Call" && parent.expression === node) return node;
// node specific permutations with no parent logic
@@ -316,10 +321,11 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
var expr;
switch ((node.start._permute * steps | 0) % 3) {
case 0:
if (!(node.init instanceof U.AST_Definitions
&& node.init.definitions[0].name instanceof U.AST_Destructured)) {
expr = node.init;
if (node.init instanceof U.AST_Definitions) {
if (node.init instanceof U.AST_Const) break;
if (node.init.definitions[0].name instanceof U.AST_Destructured) break;
}
expr = node.init;
break;
case 1:
expr = node.object;
@@ -484,23 +490,27 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true;
return newNode;
}, function(node, in_list) {
if (node instanceof U.AST_Sequence) {
if (node instanceof U.AST_Definitions) {
// remove empty var statement
if (node.definitions.length == 0) return in_list ? List.skip : new U.AST_EmptyStatement({
start: {},
});
} else if (node instanceof U.AST_ObjectMethod) {
if (!/Function$/.test(node.value.TYPE)) return new U.AST_ObjectKeyVal({
key: node.key,
value: node.value,
start: {},
});
} else if (node instanceof U.AST_Sequence) {
// expand single-element sequence
if (node.expressions.length == 1) return node.expressions[0];
}
else if (node instanceof U.AST_Try) {
} else if (node instanceof U.AST_Try) {
// expand orphaned try block
if (!node.bcatch && !node.bfinally) return new U.AST_BlockStatement({
body: node.body,
start: {},
});
}
else if (node instanceof U.AST_Definitions) {
// remove empty var statement
if (node.definitions.length == 0) return in_list ? List.skip : new U.AST_EmptyStatement({
start: {},
});
}
});
var diff_error_message;

View File

@@ -1,3 +1,24 @@
echo "::group::GitHub Environment Variables"
echo "CI: $CI"
echo "GITHUB_WORKFLOW: $GITHUB_WORKFLOW"
echo "GITHUB_RUN_ID: $GITHUB_RUN_ID"
echo "GITHUB_RUN_NUMBER: $GITHUB_RUN_NUMBER"
echo "GITHUB_ACTION: $GITHUB_ACTION"
echo "GITHUB_ACTIONS: $GITHUB_ACTIONS"
echo "GITHUB_ACTOR: $GITHUB_ACTOR"
echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY"
echo "GITHUB_EVENT_NAME: $GITHUB_EVENT_NAME"
echo "GITHUB_EVENT_PATH: $GITHUB_EVENT_PATH"
echo "GITHUB_WORKSPACE: $GITHUB_WORKSPACE"
echo "GITHUB_SHA: $GITHUB_SHA"
echo "GITHUB_REF: $GITHUB_REF"
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
echo "GITHUB_BASE_REF: $GITHUB_BASE_REF"
echo "GITHUB_SERVER_URL: $GITHUB_SERVER_URL"
echo "GITHUB_API_URL: $GITHUB_API_URL"
echo "GITHUB_GRAPHQL_URL: $GITHUB_GRAPHQL_URL"
echo "::endgroup::"
if command -v timeout &> /dev/null; then NATIVE=1; fi
timeout() {
T=$1

View File

@@ -6,18 +6,22 @@ UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> esbuild' $DIRS
for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'`
do
echo "$i"
CODE=`cat "$i"`
node_modules/.bin/esbuild --loader=ts --target=esnext > "$i" <<EOF
$CODE
EOF
ARGS="$ARGS $i"
done
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
ARGS="$ARGS $i"
done
uglify-js $ARGS
for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'`
do
echo "$i"
node_modules/.bin/esbuild --loader=ts --target=node14 < "$i" \
| uglify-js $UGLIFY_OPTIONS -o "$i"
done
}
rm -rf tmp/rollup \

View File

@@ -6,6 +6,16 @@ UGLIFY_OPTIONS=$@
minify_in_situ() {
ARGS="$UGLIFY_OPTIONS --validate --in-situ"
DIRS="$1"
echo '> esbuild' $DIRS
for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'`
do
echo "$i"
CODE=`cat "$i"`
node_modules/.bin/esbuild --loader=ts --target=esnext > "$i" <<EOF
$CODE
EOF
ARGS="$ARGS $i"
done
echo '> uglify-js' $DIRS $UGLIFY_OPTIONS
for i in `find $DIRS -type f -name '*.js'`
do
@@ -16,12 +26,6 @@ minify_in_situ() {
ARGS="$ARGS $i"
done
uglify-js $ARGS
for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'`
do
echo "$i"
node_modules/.bin/esbuild --loader=ts --target=node14 < "$i" \
| uglify-js $UGLIFY_OPTIONS -o "$i"
done
}
rm -rf tmp/sucrase \

View File

@@ -1,6 +1,6 @@
#!/bin/sh
alias uglify-js="node --max-old-space-size=4096 $PWD/bin/uglifyjs"
alias uglify-js="node --max-old-space-size=8192 $PWD/bin/uglifyjs"
UGLIFY_OPTIONS=$@
minify_in_situ() {
@@ -23,6 +23,17 @@ rm -rf tmp/web-tooling-benchmark \
+++ b/package.json
@@ -12 +11,0 @@
- "postinstall": "npm run build:terser-bundled && npm run build:uglify-js-bundled && npm run build",
--- a/src/bootstrap.js
+++ b/src/bootstrap.js
@@ -6 +6 @@ const gmean = require("compute-gmean");
-const package = require("../package.json");
+const package_json = require("../package.json");
@@ -65 +65 @@ function initialize() {
- document.title = \`Web Tooling Benchmark v\${package.version}\`;
+ document.title = \`Web Tooling Benchmark v\${package_json.version}\`;
@@ -68 +68 @@ function initialize() {
- versionDiv.innerHTML = \`v\${package.version}\`;
+ versionDiv.innerHTML = \`v\${package_json.version}\`;
--- a/src/cli-flags-helper.js
+++ b/src/cli-flags-helper.js
@@ -7 +6,0 @@ const targetList = new Set([

View File

@@ -55,6 +55,8 @@ exports.patch_module_statements = function(code) {
if (!header) return "";
if (header.length == 1) return "0, " + header;
return header.slice(0, -1) + " _" + ++count + header.slice(-1);
}).replace(/\bimport\.meta\b/g, function() {
return '({ url: "https://example.com/path/index.html" })';
}).replace(/\bimport\b(?:\s*([^('"]+)\bfrom\b)?\s*(['"]).*?\2(?:$|\n|;)/g, function(match, symbols) {
if (symbols) {
if (!/^[{*]/.test(symbols)) symbols = "default:" + symbols;
@@ -200,9 +202,13 @@ function setup(global, builtins, setup_log, setup_tty) {
});
Object.defineProperties(global, props);
// for Node.js v8+
global.toString = function() {
return "[object global]";
};
if (global.toString !== Object.prototype.toString) {
global.__proto__ = Object.defineProperty(Object.create(global.__proto__), "toString", {
value: function() {
return "[object global]";
},
});
}
function self() {
return this;
@@ -216,7 +222,7 @@ function setup(global, builtins, setup_log, setup_tty) {
if (arg === global) return "[object global]";
if (/Error$/.test(arg.name)) return arg.toString();
if (typeof arg.then == "function") return "[object Promise]";
arg.constructor.toString();
if (arg.constructor) arg.constructor.toString();
var index = cache.original.indexOf(arg);
if (index >= 0) return cache.replaced[index];
if (--cache.level < 0) return "[object Object]";

View File

@@ -149,8 +149,10 @@ var SUPPORT = function(matrix) {
for_of: "for (var a of []);",
generator: "function* f(){}",
let: "let a;",
logical_assignment: "[].p ??= 0;",
new_target: "function f() { new.target; }",
nullish: "0 ?? 0",
optional_chaining: "0?.p",
rest: "var [...a] = [];",
rest_object: "var {...a} = {};",
spread: "[...[]];",
@@ -203,12 +205,20 @@ var VALUES = [
'"function"',
"this",
];
VALUES = VALUES.concat(VALUES);
VALUES = VALUES.concat(VALUES);
VALUES = VALUES.concat(VALUES);
if (SUPPORT.bigint) VALUES = VALUES.concat([
"(!0o644n)",
"([3n][0] > 2)",
"(-42n).toString()",
"Number(0XDEADn << 16n | 0xbeefn)",
]);
VALUES = VALUES.concat(VALUES);
VALUES = VALUES.concat(VALUES);
VALUES = VALUES.concat(VALUES);
VALUES = VALUES.concat(VALUES);
VALUES.push("import.meta");
var BINARY_OPS = [
" + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
@@ -262,10 +272,13 @@ ASSIGNMENTS = ASSIGNMENTS.concat([
">>=",
">>>=",
]);
if (SUPPORT.exponentiation) {
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
ASSIGNMENTS.push("**=");
}
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
if (SUPPORT.exponentiation) ASSIGNMENTS.push("**=");
if (SUPPORT.logical_assignment) ASSIGNMENTS = ASSIGNMENTS.concat([
"&&=",
"||=",
"??=",
]);
var UNARY_SAFE = [
"+",
@@ -343,6 +356,7 @@ var block_vars = [];
var lambda_vars = [];
var unique_vars = [];
var classes = [];
var allow_this = true;
var async = false;
var has_await = false;
var export_default = false;
@@ -389,6 +403,7 @@ function createTopLevelCode() {
lambda_vars.length = 0;
unique_vars.length = 0;
classes.length = 0;
allow_this = true;
async = false;
has_await = false;
export_default = false;
@@ -433,9 +448,9 @@ function addTrailingComma(list) {
function createParams(was_async, was_generator, noDuplicate) {
var save_async = async;
if (was_async) async = true;
if (!async) async = was_async;
var save_generator = generator;
if (was_generator) generator = true;
if (!generator) generator = was_generator;
var len = unique_vars.length;
var params = [];
for (var n = rng(4); --n >= 0;) {
@@ -557,9 +572,9 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
function createName() {
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
var save_async = async;
if (was_async) async = true;
if (!async) async = was_async;
var save_generator = generator;
if (was_generator) generator = true;
if (!generator) generator = was_generator;
var name = createVarName(MANDATORY);
generator = save_generator;
async = save_async;
@@ -970,8 +985,17 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
var label = createLabel(canBreak, canContinue);
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
var key = rng(10) ? "key" + loop : getVarName(NO_CONST);
var of = SUPPORT.for_of && rng(20) == 0;
var key;
if (rng(10)) {
key = "key" + loop;
} else if (bug_for_of_async && of) {
addAvoidVar("async");
key = getVarName(NO_CONST);
removeAvoidVar("async");
} else {
key = getVarName(NO_CONST);
}
var init = "";
if (!/^key/.test(key)) {
if (!(of && bug_for_of_var) && rng(10) == 0) init = "var ";
@@ -1167,7 +1191,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
var hadDefault = false;
var s = [""];
var s = [ "" ];
canBreak = enableLoopControl(canBreak, CAN_BREAK);
while (n-- > 0) {
//hadDefault = n > 0; // disables weird `default` clause positioning (use when handling destabilizes)
@@ -1466,12 +1490,16 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
return createValue() + " in " + createObjectLiteral(recurmax, stmtDepth, canThrow);
case p++:
var name = getVarName();
var s = name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
return canThrow && rng(20) == 0 ? s : name + " && " + s;
var prop = "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
if (SUPPORT.optional_chaining && rng(50) == 0) return name + "?." + prop;
if (canThrow && rng(20) == 0) return name + prop;
return name + " && " + name + prop;
case p++:
var name = getVarName();
var s = name + "." + getDotKey();
return canThrow && rng(20) == 0 ? s : name + " && " + s;
var prop = getDotKey();
if (SUPPORT.optional_chaining && rng(50) == 0) return name + "?." + prop;
if (canThrow && rng(20) == 0) return name + "." + prop;
return name + " && " + name + "." + prop;
case p++:
case p++:
var name = getVarName();
@@ -1513,7 +1541,16 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
} while (name in called && !called[name]);
called[name] = true;
return mayDefer("typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + createArgs(recurmax, stmtDepth, canThrow));
var args = createArgs(recurmax, stmtDepth, canThrow);
var call = "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + args;
if (canThrow) {
if (SUPPORT.optional_chaining && args[0] != "`" && rng(50) == 0) {
call = "--_calls_ >= 0 && " + name + "?." + args;
} else if (rng(20) == 0) {
call = "--_calls_ >= 0 && " + name + args;
}
}
return mayDefer(call);
}
_createExpression.N = p;
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
@@ -1598,6 +1635,10 @@ var KEYS = [
"1.5",
"3",
].concat(SAFE_KEYS);
SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS);
SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS);
SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS);
SAFE_KEYS.push("__proto__");
function getDotKey(assign) {
var key;
@@ -1692,6 +1733,8 @@ function createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz)
fn = function(defns) {
if (generator) name = "*" + name;
if (async) name = "async "+ name;
var save_allow = allow_this;
if (internal == "super") allow_this = false;
s = [
name + "(" + createParams(save_async, save_generator, NO_DUPLICATE) + "){",
strictMode(),
@@ -1699,6 +1742,7 @@ function createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz)
];
s.push(_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, stmtDepth));
if (internal == "super") s.push("super" + createArgs(recurmax, stmtDepth, canThrow, NO_TEMPLATE) + ";");
allow_this = save_allow;
if (/^(constructor|super)$/.test(internal) || rng(10) == 0) for (var i = rng(4); --i >= 0;) {
s.push(rng(2) ? createSuperAssignment(recurmax, stmtDepth, canThrow) : createThisAssignment(recurmax, stmtDepth, canThrow));
}
@@ -1715,8 +1759,9 @@ function createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz)
function createObjectLiteral(recurmax, stmtDepth, canThrow) {
recurmax--;
var obj = ["({"];
var obj = [ "({" ];
var offset = SUPPORT.spread_object ? 0 : SUPPORT.computed_key ? 2 : 4;
var has_proto = false;
for (var i = rng(6); --i >= 0;) switch (offset + rng(50 - offset)) {
case 0:
obj.push("..." + getVarName() + ",");
@@ -1732,7 +1777,12 @@ function createObjectLiteral(recurmax, stmtDepth, canThrow) {
obj.push(createObjectFunction(recurmax, stmtDepth, canThrow) + ",");
break;
default:
obj.push(createObjectKey(recurmax, stmtDepth, canThrow) + ": " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ",");
if (has_proto || rng(200)) {
obj.push(createObjectKey(recurmax, stmtDepth, canThrow) + ": " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ",");
} else {
obj.push("__proto__: " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || {},");
has_proto = true;
}
break;
}
obj.push("})");
@@ -1775,7 +1825,7 @@ function createClassLiteral(recurmax, stmtDepth, canThrow, name) {
if (SUPPORT.class_field && rng(2)) {
s += internal || createObjectKey(recurmax, stmtDepth, canThrow);
if (rng(5)) {
async = false;
async = bug_async_class_await && fixed && 0;
generator = false;
s += " = " + createExpression(recurmax, NO_COMMA, stmtDepth, fixed ? canThrow : CANNOT_THROW);
generator = save_generator;
@@ -1929,7 +1979,7 @@ function createValue() {
var v;
do {
v = VALUES[rng(VALUES.length)];
} while (v == "new.target" && rng(200));
} while (v == "new.target" && rng(200) || !allow_this && v == "this");
return v;
}
@@ -1979,7 +2029,7 @@ function isBannedKeyword(name) {
case "arguments":
return in_class;
case "await":
return async;
return async !== false;
case "yield":
return generator || in_class;
}
@@ -2024,6 +2074,7 @@ function createVarName(maybe, dontStore) {
if (require.main !== module) {
exports.createTopLevelCode = createTopLevelCode;
exports.num_iterations = num_iterations;
exports.verbose = verbose;
return;
}
@@ -2202,8 +2253,7 @@ function log(options) {
function sort_globals(code) {
var globals = run_code("throw Object.keys(this).sort(" + function(global) {
return function(m, n) {
return (n == "toString") - (m == "toString")
|| (typeof global[n] == "function") - (typeof global[m] == "function")
return (typeof global[n] == "function") - (typeof global[m] == "function")
|| (m < n ? -1 : m > n ? 1 : 0);
};
} + "(this));" + code);
@@ -2253,7 +2303,7 @@ function is_error_in(ex) {
}
function is_error_spread(ex) {
return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| is not a function/.test(ex.message);
return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| not a function/.test(ex.message);
}
function is_error_recursion(ex) {
@@ -2372,6 +2422,8 @@ if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && typeof sandbox.run_code("a
return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter";
};
}
var bug_async_class_await = SUPPORT.async && SUPPORT.class_field && typeof sandbox.run_code("var await; async function f() { class A { static p = await; } }") != "string";
var bug_for_of_async = SUPPORT.for_await_of && typeof sandbox.run_code("var async; for (async of []);") != "string";
var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && typeof sandbox.run_code("try {} catch (e) { for (var e of []); }") != "string";
if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") {
beautify_options.output.v8 = true;

View File

@@ -56,6 +56,9 @@ if (+process.env["UGLIFY_BUG_REPORT"]) exports.minify = function(files, options)
function describe_ast() {
var out = OutputStream({ beautify: true });
doitem(AST_Node);
return out.get() + "\n";
function doitem(ctor) {
out.print("AST_" + ctor.TYPE);
var props = ctor.SELF_PROPS.filter(function(prop) {
@@ -86,9 +89,7 @@ function describe_ast() {
});
});
}
};
doitem(AST_Node);
return out + "\n";
}
}
function infer_options(options) {