Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c84dfa151 | ||
|
|
5359900b78 | ||
|
|
739fa266f8 | ||
|
|
da24dfb59e | ||
|
|
a2f27c7640 | ||
|
|
3c556b8689 | ||
|
|
7110c6923b | ||
|
|
b27b6807cb | ||
|
|
ba6e29d6fd | ||
|
|
d4685640a0 | ||
|
|
ac7b5c07d7 | ||
|
|
0cd4a199b0 | ||
|
|
35435d4bd3 | ||
|
|
d0bb147639 | ||
|
|
4723b4541e | ||
|
|
9d23ba0a22 | ||
|
|
a08d42555a | ||
|
|
fd7ad8e779 | ||
|
|
a36c5472d2 | ||
|
|
8bfd891c09 | ||
|
|
ef9f7ca3e7 | ||
|
|
acc443b2cf | ||
|
|
f87e7be12c | ||
|
|
c0614654d9 | ||
|
|
0358637725 | ||
|
|
63b5b6d2b3 | ||
|
|
e675262d51 | ||
|
|
c1e771a89a | ||
|
|
bc7a88baea | ||
|
|
018e0350f8 | ||
|
|
d37ee4d41c |
2
.github/workflows/ufuzz.yml
vendored
2
.github/workflows/ufuzz.yml
vendored
@@ -32,6 +32,8 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install GNU Core Utilities
|
||||
if: ${{ startsWith(matrix.os, 'macos') }}
|
||||
env:
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||
shell: bash
|
||||
run: |
|
||||
brew install coreutils
|
||||
|
||||
16
README.md
16
README.md
@@ -769,6 +769,9 @@ to be `false` and all symbol names will be omitted.
|
||||
|
||||
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
|
||||
|
||||
- `templates` (default: `true`) -- compact template literals by embedding expressions
|
||||
and/or converting to string literals, e.g. `` `foo ${42}` → "foo 42"``
|
||||
|
||||
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
|
||||
variables from `unused` removal (can be array, comma-separated, RegExp or
|
||||
function. Implies `toplevel`)
|
||||
@@ -1254,3 +1257,16 @@ To allow for better optimizations, the compiler makes various assumptions:
|
||||
}()) => b)());
|
||||
```
|
||||
UglifyJS may modify the input which in turn may suppress those errors.
|
||||
- Some arithmetic operations with `BigInt` may throw `TypeError`:
|
||||
```javascript
|
||||
1n + 1;
|
||||
// TypeError: can't convert BigInt to number
|
||||
```
|
||||
UglifyJS may modify the input which in turn may suppress those errors.
|
||||
- Some versions of JavaScript will throw `SyntaxError` with the
|
||||
following:
|
||||
```javascript
|
||||
console.log(String.raw`\uFo`);
|
||||
// SyntaxError: Invalid Unicode escape sequence
|
||||
```
|
||||
UglifyJS may modify the input which in turn may suppress those errors.
|
||||
|
||||
45
lib/ast.js
45
lib/ast.js
@@ -249,7 +249,7 @@ var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_s
|
||||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||
functions: "[Object/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: "[Object/S] a map of name ---> SymbolDef for all variables/functions defined in this scope",
|
||||
},
|
||||
clone: function(deep) {
|
||||
var node = this._clone(deep);
|
||||
@@ -472,7 +472,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: "[Object/S] a map of name ---> SymbolDef for all undeclared names",
|
||||
},
|
||||
wrap: function(name) {
|
||||
var body = this.body;
|
||||
@@ -1418,6 +1418,34 @@ var AST_This = DEFNODE("This", null, {
|
||||
},
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_Template = DEFNODE("Template", "expressions strings tag", {
|
||||
$documentation: "A template literal, i.e. tag`str1${expr1}...strN${exprN}strN+1`",
|
||||
$propdoc: {
|
||||
expressions: "[AST_Node*] the placeholder expressions",
|
||||
strings: "[string*] the raw text segments",
|
||||
tag: "[AST_Node] tag function, or null if absent",
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
if (node.tag) node.tag.walk(visitor);
|
||||
node.expressions.forEach(function(expr) {
|
||||
expr.walk(visitor);
|
||||
});
|
||||
});
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.expressions.length + 1 != this.strings.length) {
|
||||
throw new Error("malformed template with " + this.expressions.length + " placeholder(s) but " + this.strings.length + " text segment(s)");
|
||||
}
|
||||
must_be_expressions(this, "expressions");
|
||||
this.strings.forEach(function(string) {
|
||||
if (typeof string != "string") throw new Error("strings must contain string");
|
||||
});
|
||||
if (this.tag != null) must_be_expression(this, "tag");
|
||||
},
|
||||
});
|
||||
|
||||
var AST_Constant = DEFNODE("Constant", null, {
|
||||
$documentation: "Base class for all constants",
|
||||
});
|
||||
@@ -1440,6 +1468,19 @@ var AST_Number = DEFNODE("Number", "value", {
|
||||
},
|
||||
_validate: function() {
|
||||
if (typeof this.value != "number") throw new Error("value must be number");
|
||||
if (!isFinite(this.value)) throw new Error("value must be finite");
|
||||
if (this.value < 0) throw new Error("value cannot be negative");
|
||||
},
|
||||
}, AST_Constant);
|
||||
|
||||
var AST_BigInt = DEFNODE("BigInt", "value", {
|
||||
$documentation: "A BigInt literal",
|
||||
$propdoc: {
|
||||
value: "[string] the numeric representation",
|
||||
},
|
||||
_validate: function() {
|
||||
if (typeof this.value != "string") throw new Error("value must be string");
|
||||
if (this.value[0] == "-") throw new Error("value cannot be negative");
|
||||
},
|
||||
}, AST_Constant);
|
||||
|
||||
|
||||
537
lib/compress.js
537
lib/compress.js
File diff suppressed because it is too large
Load Diff
@@ -684,37 +684,41 @@ function OutputStream(options) {
|
||||
|
||||
PARENS(AST_Unary, function(output) {
|
||||
var p = output.parent();
|
||||
// (-x) ** y
|
||||
if (p instanceof AST_Binary) return p.operator == "**" && p.left === this;
|
||||
// (x++).toString(3)
|
||||
// (typeof x).length
|
||||
return (p instanceof AST_Call || p instanceof AST_PropAccess) && p.expression === this;
|
||||
});
|
||||
|
||||
PARENS(AST_Sequence, function(output) {
|
||||
var p = output.parent();
|
||||
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||
// [ 1, (2, 3), 4 ] ---> [ 1, 3, 4 ]
|
||||
return p instanceof AST_Array
|
||||
// () => (foo, bar)
|
||||
// () ---> (foo, bar)
|
||||
|| is_arrow(p) && p.value === this
|
||||
// await (foo, bar)
|
||||
|| p instanceof AST_Await
|
||||
// 1 + (2, 3) + 4 ==> 8
|
||||
// 1 + (2, 3) + 4 ---> 8
|
||||
|| p instanceof AST_Binary
|
||||
// new (foo, bar) or foo(1, (2, 3), 4)
|
||||
|| p instanceof AST_Call
|
||||
// (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||
// ==> 20 (side effect, set a := 10 and b := 20)
|
||||
// ---> 20 (side effect, set a := 10 and b := 20)
|
||||
|| p instanceof AST_Conditional
|
||||
// [ a = (1, 2) ] = [] ==> a == 2
|
||||
// [ a = (1, 2) ] = [] ---> a == 2
|
||||
|| p instanceof AST_DefaultValue
|
||||
// { [(1, 2)]: 3 }[2] ==> 3
|
||||
// { foo: (1, 2) }.foo ==> 2
|
||||
// { [(1, 2)]: 3 }[2] ---> 3
|
||||
// { foo: (1, 2) }.foo ---> 2
|
||||
|| p instanceof AST_DestructuredKeyVal
|
||||
|| p instanceof AST_ObjectProperty
|
||||
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ---> 2
|
||||
|| p instanceof AST_PropAccess && p.expression === this
|
||||
// ...(foo, bar, baz)
|
||||
|| p instanceof AST_Spread
|
||||
// !(foo, bar, baz)
|
||||
|| p instanceof AST_Unary
|
||||
// var a = (1, 2), b = a + a; ==> b == 4
|
||||
// var a = (1, 2), b = a + a; ---> b == 4
|
||||
|| p instanceof AST_VarDef;
|
||||
});
|
||||
|
||||
@@ -722,11 +726,14 @@ function OutputStream(options) {
|
||||
var p = output.parent();
|
||||
// await (foo && bar)
|
||||
if (p instanceof AST_Await) return true;
|
||||
// this deals with precedence: 3 * (2 + 1)
|
||||
// this deals with precedence:
|
||||
// 3 * (2 + 1)
|
||||
// 3 - (2 - 1)
|
||||
// (1 ** 2) ** 3
|
||||
if (p instanceof AST_Binary) {
|
||||
var po = p.operator, pp = PRECEDENCE[po];
|
||||
var so = this.operator, sp = PRECEDENCE[so];
|
||||
return pp > sp || (pp == sp && this === p.right);
|
||||
return pp > sp || (pp == sp && this === p[po == "**" ? "left" : "right"]);
|
||||
}
|
||||
// (foo && bar)()
|
||||
if (p instanceof AST_Call) return p.expression === this;
|
||||
@@ -773,18 +780,16 @@ function OutputStream(options) {
|
||||
// (new foo)(bar)
|
||||
if (p instanceof AST_Call) return p.expression === this;
|
||||
// (new Date).getTime(), (new Date)["getTime"]()
|
||||
return p instanceof AST_PropAccess;
|
||||
if (p instanceof AST_PropAccess) return true;
|
||||
// (new foo)`bar`
|
||||
if (p instanceof AST_Template) return p.tag === this;
|
||||
});
|
||||
|
||||
PARENS(AST_Number, function(output) {
|
||||
if (!output.option("galio")) return false;
|
||||
// https://github.com/mishoo/UglifyJS/pull/1009
|
||||
var p = output.parent();
|
||||
if (p instanceof AST_PropAccess && p.expression === this) {
|
||||
var value = this.value;
|
||||
// https://github.com/mishoo/UglifyJS/issues/115
|
||||
return value < 0
|
||||
// https://github.com/mishoo/UglifyJS/pull/1009
|
||||
|| output.option("galio") && /^0/.test(make_num(value));
|
||||
}
|
||||
return p instanceof AST_PropAccess && p.expression === this && /^0/.test(make_num(this.value));
|
||||
});
|
||||
|
||||
function needs_parens_assign_cond(self, output) {
|
||||
@@ -799,6 +804,8 @@ function OutputStream(options) {
|
||||
if (p instanceof AST_Conditional) return p.condition === self;
|
||||
// (a = foo)["prop"] —or— (a = foo).prop
|
||||
if (p instanceof AST_PropAccess) return p.expression === self;
|
||||
// (a = foo)`bar`
|
||||
if (p instanceof AST_Template) return p.tag === self;
|
||||
// !(a = false) → true
|
||||
if (p instanceof AST_Unary) return true;
|
||||
}
|
||||
@@ -807,8 +814,8 @@ function OutputStream(options) {
|
||||
});
|
||||
PARENS(AST_Assign, function(output) {
|
||||
if (needs_parens_assign_cond(this, output)) return true;
|
||||
// v8 parser bug => workaround
|
||||
// f([1], [a] = []) => f([1], ([a] = []))
|
||||
// v8 parser bug ---> workaround
|
||||
// f([1], [a] = []) ---> f([1], ([a] = []))
|
||||
if (output.option("v8")) return this.left instanceof AST_Destructured;
|
||||
// ({ p: a } = o);
|
||||
if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output);
|
||||
@@ -822,6 +829,8 @@ function OutputStream(options) {
|
||||
|
||||
PARENS(AST_Await, function(output) {
|
||||
var p = output.parent();
|
||||
// (await x) ** y
|
||||
if (p instanceof AST_Binary) return p.operator == "**" && p.left === this;
|
||||
// new (await foo)
|
||||
// (await foo)(bar)
|
||||
if (p instanceof AST_Call) return p.expression === this;
|
||||
@@ -1481,6 +1490,19 @@ function OutputStream(options) {
|
||||
DEFPRINT(AST_This, function(output) {
|
||||
output.print("this");
|
||||
});
|
||||
DEFPRINT(AST_Template, function(output) {
|
||||
var self = this;
|
||||
if (self.tag) self.tag.print(output);
|
||||
output.print("`");
|
||||
for (var i = 0; i < self.expressions.length; i++) {
|
||||
output.print(self.strings[i]);
|
||||
output.print("${");
|
||||
self.expressions[i].print(output);
|
||||
output.print("}");
|
||||
}
|
||||
output.print(self.strings[i]);
|
||||
output.print("`");
|
||||
});
|
||||
DEFPRINT(AST_Constant, function(output) {
|
||||
output.print(this.value);
|
||||
});
|
||||
|
||||
175
lib/parse.js
175
lib/parse.js
@@ -81,6 +81,7 @@ var OPERATORS = makePredicate([
|
||||
"*",
|
||||
"/",
|
||||
"%",
|
||||
"**",
|
||||
">>",
|
||||
"<<",
|
||||
">>>",
|
||||
@@ -112,7 +113,7 @@ var OPERATORS = makePredicate([
|
||||
var NEWLINE_CHARS = "\n\r\u2028\u2029";
|
||||
var OPERATOR_CHARS = "+-*&%=<>!?|~^";
|
||||
var PUNC_BEFORE_EXPRESSION = "[{(,;:";
|
||||
var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + ")}]";
|
||||
var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + "`)}]";
|
||||
var WHITESPACE_CHARS = NEWLINE_CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF";
|
||||
var NON_IDENTIFIER_CHARS = makePredicate(characters("./'\"" + OPERATOR_CHARS + PUNC_CHARS + WHITESPACE_CHARS));
|
||||
|
||||
@@ -144,6 +145,43 @@ function is_identifier_string(str) {
|
||||
return /^[a-z_$][a-z0-9_$]*$/i.test(str);
|
||||
}
|
||||
|
||||
function decode_escape_sequence(seq) {
|
||||
switch (seq[0]) {
|
||||
case "b": return "\b";
|
||||
case "f": return "\f";
|
||||
case "n": return "\n";
|
||||
case "r": return "\r";
|
||||
case "t": return "\t";
|
||||
case "u":
|
||||
var code;
|
||||
if (seq.length == 5) {
|
||||
code = seq.slice(1);
|
||||
} else if (seq[1] == "{" && seq.slice(-1) == "}") {
|
||||
code = seq.slice(2, -1);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
var num = parseInt(code, 16);
|
||||
if (num < 0 || isNaN(num)) return;
|
||||
if (num < 0x10000) return String.fromCharCode(num);
|
||||
if (num > 0x10ffff) return;
|
||||
return String.fromCharCode((num >> 10) + 0xd7c0) + String.fromCharCode((num & 0x03ff) + 0xdc00);
|
||||
case "v": return "\u000b";
|
||||
case "x":
|
||||
if (seq.length != 3) return;
|
||||
var num = parseInt(seq.slice(1), 16);
|
||||
if (num < 0 || isNaN(num)) return;
|
||||
return String.fromCharCode(num);
|
||||
case "\r":
|
||||
case "\n":
|
||||
return "";
|
||||
default:
|
||||
if (seq == "0") return "\0";
|
||||
if (seq[0] >= "0" && seq[0] <= "9") return;
|
||||
return seq;
|
||||
}
|
||||
}
|
||||
|
||||
function parse_js_number(num) {
|
||||
var match;
|
||||
if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2);
|
||||
@@ -190,7 +228,28 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
regex_allowed : false,
|
||||
comments_before : [],
|
||||
directives : {},
|
||||
directive_stack : []
|
||||
directive_stack : [],
|
||||
read_template : with_eof_error("Unterminated template literal", function(strings) {
|
||||
var s = "";
|
||||
for (;;) {
|
||||
var ch = next(true, true);
|
||||
switch (ch) {
|
||||
case "\\":
|
||||
ch += next(true, true);
|
||||
break;
|
||||
case "`":
|
||||
strings.push(s);
|
||||
return;
|
||||
case "$":
|
||||
if (peek() == "{") {
|
||||
next();
|
||||
strings.push(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
s += ch;
|
||||
}
|
||||
}),
|
||||
};
|
||||
var prev_was_dot = false;
|
||||
|
||||
@@ -280,9 +339,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
}
|
||||
|
||||
function read_while(pred) {
|
||||
var ret = "", ch, i = 0;
|
||||
while ((ch = peek()) && pred(ch, i++))
|
||||
ret += next();
|
||||
var ret = "", ch;
|
||||
while ((ch = peek()) && pred(ch)) ret += next();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -292,16 +350,14 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
|
||||
function read_num(prefix) {
|
||||
var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
|
||||
var num = read_while(function(ch, i) {
|
||||
var num = read_while(function(ch) {
|
||||
var code = ch.charCodeAt(0);
|
||||
switch (code) {
|
||||
case 120: case 88: // xX
|
||||
return has_x ? false : (has_x = true);
|
||||
case 101: case 69: // eE
|
||||
return has_x ? true : has_e ? false : (has_e = after_e = true);
|
||||
case 45: // -
|
||||
return after_e || (i == 0 && !prefix);
|
||||
case 43: // +
|
||||
case 43: case 45: // +-
|
||||
return after_e;
|
||||
case (after_e = false, 46): // .
|
||||
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
|
||||
@@ -315,41 +371,29 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
|
||||
}
|
||||
var valid = parse_js_number(num);
|
||||
if (!isNaN(valid)) return token("num", valid);
|
||||
parse_error("Invalid syntax: " + num);
|
||||
if (isNaN(valid)) parse_error("Invalid syntax: " + num);
|
||||
if (has_dot || has_e || peek() != "n") return token("num", valid);
|
||||
return token("bigint", num.toLowerCase() + next());
|
||||
}
|
||||
|
||||
function read_escaped_char(in_string) {
|
||||
var ch = next(true, in_string);
|
||||
switch (ch.charCodeAt(0)) {
|
||||
case 110: return "\n";
|
||||
case 114: return "\r";
|
||||
case 116: return "\t";
|
||||
case 98: return "\b";
|
||||
case 118: return "\u000b"; // \v
|
||||
case 102: return "\f";
|
||||
case 120: return String.fromCharCode(hex_bytes(2)); // \x
|
||||
case 117: // \u
|
||||
if (peek() != "{") return String.fromCharCode(hex_bytes(4));
|
||||
next();
|
||||
var num = 0;
|
||||
do {
|
||||
var digit = parseInt(next(true), 16);
|
||||
if (isNaN(digit)) parse_error("Invalid hex-character pattern in string");
|
||||
num = num * 16 + digit;
|
||||
} while (peek() != "}");
|
||||
next();
|
||||
if (num < 0x10000) return String.fromCharCode(num);
|
||||
if (num > 0x10ffff) parse_error("Invalid character code: " + num);
|
||||
return String.fromCharCode((num >> 10) + 0xd7c0) + String.fromCharCode((num & 0x03ff) + 0xdc00);
|
||||
case 13: // \r
|
||||
// DOS newline
|
||||
if (peek() == "\n") next(true, in_string);
|
||||
case 10: return ""; // \n
|
||||
var seq = next(true, in_string);
|
||||
if (seq >= "0" && seq <= "7") return read_octal_escape_sequence(seq);
|
||||
if (seq == "u") {
|
||||
var ch = next(true, in_string);
|
||||
seq += ch;
|
||||
if (ch != "{") {
|
||||
seq += next(true, in_string) + next(true, in_string) + next(true, in_string);
|
||||
} else do {
|
||||
ch = next(true, in_string);
|
||||
seq += ch;
|
||||
} while (ch != "}");
|
||||
} else if (seq == "x") {
|
||||
seq += next(true, in_string) + next(true, in_string);
|
||||
}
|
||||
if (ch >= "0" && ch <= "7")
|
||||
return read_octal_escape_sequence(ch);
|
||||
return ch;
|
||||
var str = decode_escape_sequence(seq);
|
||||
if (typeof str != "string") parse_error("Invalid escape sequence: \\" + seq);
|
||||
return str;
|
||||
}
|
||||
|
||||
function read_octal_escape_sequence(ch) {
|
||||
@@ -368,17 +412,6 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
return String.fromCharCode(parseInt(ch, 8));
|
||||
}
|
||||
|
||||
function hex_bytes(n) {
|
||||
var num = 0;
|
||||
for (; n > 0; --n) {
|
||||
var digit = parseInt(next(true), 16);
|
||||
if (isNaN(digit))
|
||||
parse_error("Invalid hex-character pattern in string");
|
||||
num = (num << 4) | digit;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
var read_string = with_eof_error("Unterminated string constant", function(quote_char) {
|
||||
var quote = next(), ret = "";
|
||||
for (;;) {
|
||||
@@ -632,10 +665,11 @@ var PRECEDENCE = function(a, ret) {
|
||||
["<", ">", "<=", ">=", "in", "instanceof"],
|
||||
[">>", "<<", ">>>"],
|
||||
["+", "-"],
|
||||
["*", "/", "%"]
|
||||
["*", "/", "%"],
|
||||
["**"],
|
||||
], {});
|
||||
|
||||
var ATOMIC_START_TOKEN = makePredicate("atom num regexp string");
|
||||
var ATOMIC_START_TOKEN = makePredicate("atom bigint num regexp string");
|
||||
|
||||
/* -----[ Parser ]----- */
|
||||
|
||||
@@ -783,6 +817,7 @@ function parse($TEXT, options) {
|
||||
semicolon();
|
||||
return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body });
|
||||
case "num":
|
||||
case "bigint":
|
||||
case "regexp":
|
||||
case "operator":
|
||||
case "atom":
|
||||
@@ -815,6 +850,7 @@ function parse($TEXT, options) {
|
||||
});
|
||||
case "[":
|
||||
case "(":
|
||||
case "`":
|
||||
return simple_statement();
|
||||
case ";":
|
||||
S.in_directives = false;
|
||||
@@ -1361,6 +1397,9 @@ function parse($TEXT, options) {
|
||||
case "num":
|
||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||
break;
|
||||
case "bigint":
|
||||
ret = new AST_BigInt({ start: tok, end: tok, value: tok.value });
|
||||
break;
|
||||
case "string":
|
||||
ret = new AST_String({
|
||||
start : tok,
|
||||
@@ -1397,6 +1436,11 @@ function parse($TEXT, options) {
|
||||
var start = S.token;
|
||||
if (is("punc")) {
|
||||
switch (start.value) {
|
||||
case "`":
|
||||
var tmpl = template(null);
|
||||
tmpl.start = start;
|
||||
tmpl.end = prev();
|
||||
return subscripts(tmpl, allow_calls);
|
||||
case "(":
|
||||
next();
|
||||
if (is("punc", ")")) {
|
||||
@@ -1767,6 +1811,23 @@ function parse($TEXT, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function template(tag) {
|
||||
var read = S.input.context().read_template;
|
||||
var strings = [];
|
||||
var expressions = [];
|
||||
while (read(strings)) {
|
||||
next();
|
||||
expressions.push(expression());
|
||||
if (!is("punc", "}")) unexpected();
|
||||
}
|
||||
next();
|
||||
return new AST_Template({
|
||||
expressions: expressions,
|
||||
strings: strings,
|
||||
tag: tag,
|
||||
});
|
||||
}
|
||||
|
||||
var subscripts = function(expr, allow_calls) {
|
||||
var start = expr.start;
|
||||
if (is("punc", ".")) {
|
||||
@@ -1800,6 +1861,12 @@ function parse($TEXT, options) {
|
||||
mark_pure(call);
|
||||
return subscripts(call, true);
|
||||
}
|
||||
if (is("punc", "`")) {
|
||||
var tmpl = template(expr);
|
||||
tmpl.start = expr.start;
|
||||
tmpl.end = prev();
|
||||
return subscripts(tmpl, allow_calls);
|
||||
}
|
||||
return expr;
|
||||
};
|
||||
|
||||
@@ -1858,7 +1925,7 @@ function parse($TEXT, options) {
|
||||
var prec = op != null ? PRECEDENCE[op] : null;
|
||||
if (prec != null && prec > min_prec) {
|
||||
next();
|
||||
var right = expr_op(maybe_await(), prec, no_in);
|
||||
var right = expr_op(maybe_await(), op == "**" ? prec - 1 : prec, no_in);
|
||||
return expr_op(new AST_Binary({
|
||||
start : left.start,
|
||||
left : left,
|
||||
|
||||
@@ -228,7 +228,7 @@ function mangle_properties(ast, options) {
|
||||
var mangled = cache.get(name);
|
||||
if (!mangled) {
|
||||
if (debug) {
|
||||
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
|
||||
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo ---> o._$foo$NNN_.
|
||||
var debug_mangled = "_$" + name + "$" + debug_suffix + "_";
|
||||
if (can_mangle(debug_mangled)) mangled = debug_mangled;
|
||||
}
|
||||
|
||||
11
lib/scope.js
11
lib/scope.js
@@ -101,6 +101,14 @@ SymbolDef.prototype = {
|
||||
|
||||
var unary_side_effects = makePredicate("delete ++ --");
|
||||
|
||||
function is_lhs(node, parent) {
|
||||
if (parent instanceof AST_Assign) return parent.left === node && node;
|
||||
if (parent instanceof AST_DefaultValue) return parent.name === node && node;
|
||||
if (parent instanceof AST_Destructured) return node;
|
||||
if (parent instanceof AST_DestructuredKeyVal) return node;
|
||||
if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression;
|
||||
}
|
||||
|
||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||
options = defaults(options, {
|
||||
cache: null,
|
||||
@@ -269,8 +277,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||
sym = self.def_global(node);
|
||||
} else if (name == "arguments" && is_arguments(sym)) {
|
||||
var parent = tw.parent();
|
||||
if (parent instanceof AST_Assign && parent.left === node
|
||||
|| parent instanceof AST_Unary && unary_side_effects[parent.operator]) {
|
||||
if (is_lhs(node, parent)) {
|
||||
sym.scope.uses_arguments = 3;
|
||||
} else if (sym.scope.uses_arguments < 2
|
||||
&& !(parent instanceof AST_PropAccess && parent.expression === node)) {
|
||||
|
||||
@@ -201,6 +201,10 @@ TreeTransformer.prototype = new TreeWalker;
|
||||
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
||||
self.value = self.value.transform(tw);
|
||||
});
|
||||
DEF(AST_Template, function(self, tw) {
|
||||
if (self.tag) self.tag = self.tag.transform(tw);
|
||||
self.expressions = do_list(self.expressions, tw);
|
||||
});
|
||||
})(function(node, descend) {
|
||||
node.DEFMETHOD("transform", function(tw, in_list) {
|
||||
var x, y;
|
||||
|
||||
@@ -255,6 +255,8 @@ function first_in_statement(stack, arrow) {
|
||||
if (p.expressions[0] === node) continue;
|
||||
} else if (p instanceof AST_Statement) {
|
||||
return p.body === node;
|
||||
} else if (p instanceof AST_Template) {
|
||||
if (p.tag === node) continue;
|
||||
} else if (p instanceof AST_UnaryPostfix) {
|
||||
if (p.expression === node) continue;
|
||||
}
|
||||
|
||||
@@ -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.12.5",
|
||||
"version": "3.12.7",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -209,7 +209,7 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
|
||||
} else {
|
||||
var toplevel = sandbox.has_toplevel(options);
|
||||
var expected = stdout[toplevel ? 1 : 0];
|
||||
var actual = run_code(result.code, toplevel);
|
||||
var actual = sandbox.run_code(result.code, toplevel);
|
||||
if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) {
|
||||
actual = expected;
|
||||
}
|
||||
@@ -244,11 +244,6 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function run_code(code, toplevel) {
|
||||
var result = sandbox.run_code(code, toplevel);
|
||||
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
|
||||
}
|
||||
|
||||
function test_case(test) {
|
||||
log(" Running test [{name}]", { name: test.name });
|
||||
U.AST_Node.enable_validation();
|
||||
@@ -380,7 +375,7 @@ function test_case(test) {
|
||||
}
|
||||
}
|
||||
if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
|
||||
var stdout = [ run_code(input_code), run_code(input_code, true) ];
|
||||
var stdout = [ sandbox.run_code(input_code), sandbox.run_code(input_code, true) ];
|
||||
var toplevel = sandbox.has_toplevel({
|
||||
compress: test.options,
|
||||
mangle: test.mangle
|
||||
@@ -409,7 +404,7 @@ function test_case(test) {
|
||||
});
|
||||
return false;
|
||||
}
|
||||
actual = run_code(output_code, toplevel);
|
||||
actual = sandbox.run_code(output_code, toplevel);
|
||||
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
|
||||
log([
|
||||
"!!! failed",
|
||||
|
||||
@@ -13,9 +13,10 @@ holes_and_undefined: {
|
||||
}
|
||||
}
|
||||
|
||||
constant_join: {
|
||||
constant_join_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
strings: true,
|
||||
unsafe: true,
|
||||
}
|
||||
@@ -57,7 +58,7 @@ constant_join: {
|
||||
var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join();
|
||||
var c6 = [ "1,2,,,foo,bar", baz() ].join();
|
||||
var d = "foo-3bar-baz";
|
||||
var e = [].join(foo + bar);
|
||||
var e = (foo, bar, "");
|
||||
var f = "";
|
||||
var g = "";
|
||||
}
|
||||
|
||||
@@ -434,6 +434,46 @@ collapse_value: {
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
collapse_property_lambda: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
}
|
||||
input: {
|
||||
console.log(function f() {
|
||||
f.g = () => 42;
|
||||
return f.g();
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function f() {
|
||||
return (f.g = () => 42)();
|
||||
}());
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
drop_return: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
(a => {
|
||||
while (!console);
|
||||
return console.log(a);
|
||||
})(42);
|
||||
}
|
||||
expect: {
|
||||
(a => {
|
||||
while (!console);
|
||||
console.log(a);
|
||||
})(42);
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
reduce_iife_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
|
||||
@@ -519,6 +519,160 @@ collapse_vars_3: {
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
collapse_property_lambda: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
}
|
||||
input: {
|
||||
(async function f() {
|
||||
f.g = () => 42;
|
||||
return f.g();
|
||||
})().then(console.log);
|
||||
}
|
||||
expect: {
|
||||
(async function f() {
|
||||
return (f.g = () => 42)();
|
||||
})().then(console.log);
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
drop_return: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
(async function(a) {
|
||||
while (!console);
|
||||
return console.log(a);
|
||||
})(42);
|
||||
}
|
||||
expect: {
|
||||
(async function(a) {
|
||||
while (!console);
|
||||
console.log(a);
|
||||
})(42);
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
functions: {
|
||||
options = {
|
||||
functions: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
!async function() {
|
||||
var a = async function a() {
|
||||
return a && "a";
|
||||
};
|
||||
var b = async function x() {
|
||||
return !!x;
|
||||
};
|
||||
var c = async function(c) {
|
||||
return c;
|
||||
};
|
||||
if (await c(await b(await a()))) {
|
||||
var d = async function() {};
|
||||
var e = async function y() {
|
||||
return typeof y;
|
||||
};
|
||||
var f = async function(f) {
|
||||
return f;
|
||||
};
|
||||
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
|
||||
}
|
||||
}();
|
||||
}
|
||||
expect: {
|
||||
!async function() {
|
||||
async function a() {
|
||||
return a && "a";
|
||||
}
|
||||
async function b() {
|
||||
return !!b;
|
||||
}
|
||||
var c = async function(c) {
|
||||
return c;
|
||||
};
|
||||
if (await c(await b(await a()))) {
|
||||
async function d() {}
|
||||
async function e() {
|
||||
return typeof e;
|
||||
}
|
||||
var f = async function(f) {
|
||||
return f;
|
||||
};
|
||||
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
|
||||
}
|
||||
}();
|
||||
}
|
||||
expect_stdout: "a true 42 function function function"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
functions_use_strict: {
|
||||
options = {
|
||||
functions: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
!async function() {
|
||||
var a = async function a() {
|
||||
return a && "a";
|
||||
};
|
||||
var b = async function x() {
|
||||
return !!x;
|
||||
};
|
||||
var c = async function(c) {
|
||||
return c;
|
||||
};
|
||||
if (await c(await b(await a()))) {
|
||||
var d = async function() {};
|
||||
var e = async function y() {
|
||||
return typeof y;
|
||||
};
|
||||
var f = async function(f) {
|
||||
return f;
|
||||
};
|
||||
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
|
||||
}
|
||||
}();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
!async function() {
|
||||
async function a() {
|
||||
return a && "a";
|
||||
}
|
||||
async function b() {
|
||||
return !!b;
|
||||
}
|
||||
var c = async function(c) {
|
||||
return c;
|
||||
};
|
||||
if (await c(await b(await a()))) {
|
||||
var d = async function() {};
|
||||
var e = async function y() {
|
||||
return typeof y;
|
||||
};
|
||||
var f = async function(f) {
|
||||
return f;
|
||||
};
|
||||
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
|
||||
}
|
||||
}();
|
||||
}
|
||||
expect_stdout: "a true 42 function function function"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
issue_4335_1: {
|
||||
options = {
|
||||
inline: true,
|
||||
@@ -612,22 +766,32 @@ issue_4340: {
|
||||
call_expression: {
|
||||
input: {
|
||||
console.log(typeof async function(log) {
|
||||
(await log)("FAIL");
|
||||
(await log)("foo");
|
||||
}(console.log).then);
|
||||
console.log("bar");
|
||||
}
|
||||
expect_exact: 'console.log(typeof async function(log){(await log)("FAIL")}(console.log).then);'
|
||||
expect_stdout: "function"
|
||||
expect_exact: 'console.log(typeof async function(log){(await log)("foo")}(console.log).then);console.log("bar");'
|
||||
expect_stdout: [
|
||||
"function",
|
||||
"bar",
|
||||
"foo",
|
||||
]
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
property_access_expression: {
|
||||
input: {
|
||||
console.log(typeof async function(con) {
|
||||
(await con).log("FAIL");
|
||||
(await con).log("foo");
|
||||
}(console).then);
|
||||
console.log("bar");
|
||||
}
|
||||
expect_exact: 'console.log(typeof async function(con){(await con).log("FAIL")}(console).then);'
|
||||
expect_stdout: "function"
|
||||
expect_exact: 'console.log(typeof async function(con){(await con).log("foo")}(console).then);console.log("bar");'
|
||||
expect_stdout: [
|
||||
"function",
|
||||
"bar",
|
||||
"foo",
|
||||
]
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
@@ -685,20 +849,18 @@ reduce_iife_3: {
|
||||
input: {
|
||||
var a = "foo";
|
||||
(async function() {
|
||||
console.log(a);
|
||||
console.log(await a);
|
||||
console.log(a, await a, a, await a);
|
||||
})();
|
||||
a = "bar";
|
||||
}
|
||||
expect: {
|
||||
var a = "foo";
|
||||
(async function() {
|
||||
console.log(a);
|
||||
console.log(await a);
|
||||
console.log(a, await a, a, await a);
|
||||
})();
|
||||
a = "bar";
|
||||
}
|
||||
expect_stdout: "foo"
|
||||
expect_stdout: "foo foo bar bar"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
@@ -995,3 +1157,64 @@ issue_4534: {
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
issue_4581: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a = "PASS";
|
||||
(async () => (A, a = "FAIL"))();
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = "PASS";
|
||||
(async () => (A, a = "FAIL"))();
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
issue_4595: {
|
||||
options = {
|
||||
awaits: true,
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
(async function() {
|
||||
await async function f() {
|
||||
console.log(f.length);
|
||||
}();
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(async function() {
|
||||
await async function f() {
|
||||
console.log(f.length);
|
||||
}();
|
||||
})();
|
||||
}
|
||||
expect_stdout: "0"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
issue_4598: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
if (console.log("PASS")) {
|
||||
async function f() {}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
async function f() {}
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
62
test/compress/bigint.js
Normal file
62
test/compress/bigint.js
Normal file
@@ -0,0 +1,62 @@
|
||||
arithmetic: {
|
||||
input: {
|
||||
console.log(((1n + 0x2n) * (0o3n - -4n)) >> (5n - 6n));
|
||||
}
|
||||
expect_exact: "console.log((1n+0x2n)*(0o3n- -4n)>>5n-6n);"
|
||||
expect_stdout: "42n"
|
||||
node_version: ">=10"
|
||||
}
|
||||
|
||||
minus_dot: {
|
||||
input: {
|
||||
console.log(typeof -42n.toString(), typeof (-42n).toString());
|
||||
}
|
||||
expect_exact: "console.log(typeof-42n.toString(),typeof(-42n).toString());"
|
||||
expect_stdout: "number string"
|
||||
node_version: ">=10"
|
||||
}
|
||||
|
||||
evaluate: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log((0xDEAD_BEEFn).toString(16));
|
||||
}
|
||||
expect: {
|
||||
console.log(0xdeadbeefn.toString(16));
|
||||
}
|
||||
expect_stdout: "deadbeef"
|
||||
node_version: ">=10"
|
||||
}
|
||||
|
||||
Number: {
|
||||
options = {
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(Number(-0xfeed_dead_beef_badn));
|
||||
}
|
||||
expect: {
|
||||
console.log(+("" + -0xfeed_dead_beef_badn));
|
||||
}
|
||||
expect_stdout: "-1148098955808013200"
|
||||
node_version: ">=10"
|
||||
}
|
||||
|
||||
issue_4590: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
A = 1;
|
||||
0n || console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
A = 1;
|
||||
0n || console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=10"
|
||||
}
|
||||
@@ -5608,6 +5608,7 @@ collapse_rhs_array: {
|
||||
collapse_rhs_boolean_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
@@ -5633,6 +5634,7 @@ collapse_rhs_boolean_1: {
|
||||
collapse_rhs_boolean_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
var a;
|
||||
@@ -5667,6 +5669,7 @@ collapse_rhs_boolean_3: {
|
||||
booleans: true,
|
||||
collapse_vars: true,
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
var a, f, g, h, i, n, s, t, x, y;
|
||||
@@ -5720,6 +5723,7 @@ collapse_rhs_function: {
|
||||
collapse_rhs_number: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
@@ -5799,6 +5803,7 @@ collapse_rhs_regexp: {
|
||||
collapse_rhs_string: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
var a, b;
|
||||
@@ -8705,3 +8710,48 @@ collapse_or_assign: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_4586_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 42;
|
||||
(function f(b) {
|
||||
var b = a;
|
||||
if (b === arguments[0])
|
||||
console.log("PASS");
|
||||
})(console);
|
||||
}
|
||||
expect: {
|
||||
var a = 42;
|
||||
(function f(b) {
|
||||
var b = a;
|
||||
if (b === arguments[0])
|
||||
console.log("PASS");
|
||||
})(console);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_4586_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 42;
|
||||
(function f(b) {
|
||||
b = a;
|
||||
if (b === arguments[0])
|
||||
console.log("PASS");
|
||||
})(console);
|
||||
}
|
||||
expect: {
|
||||
var a = 42;
|
||||
(function f(b) {
|
||||
if ((b = a) === arguments[0])
|
||||
console.log("PASS");
|
||||
})(console);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ process_boolean_returns: {
|
||||
collapse_value_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
keep_fargs: false,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
@@ -169,7 +170,7 @@ collapse_value_1: {
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a) {
|
||||
console.log(function() {
|
||||
return "PASS";
|
||||
}());
|
||||
}
|
||||
@@ -180,6 +181,7 @@ collapse_value_1: {
|
||||
collapse_value_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
keep_fargs: false,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
@@ -188,7 +190,7 @@ collapse_value_2: {
|
||||
})().log("PASS");
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
(function() {
|
||||
return console;
|
||||
})().log("PASS");
|
||||
}
|
||||
@@ -554,8 +556,9 @@ drop_fargs: {
|
||||
"bar",
|
||||
]
|
||||
expect_warnings: [
|
||||
"WARN: Dropping unused function argument c [test/compress/default-values.js:1,61]",
|
||||
"WARN: Dropping unused default argument c [test/compress/default-values.js:1,61]",
|
||||
"WARN: Side effects in default value of unused variable b [test/compress/default-values.js:1,37]",
|
||||
"WARN: Dropping unused default argument assignment a [test/compress/default-values.js:1,29]",
|
||||
]
|
||||
node_version: ">=6"
|
||||
}
|
||||
@@ -1596,3 +1599,65 @@ issue_4548: {
|
||||
]
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4588_1_unused: {
|
||||
options = {
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a = 42) {}.length);
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a = 0) {}.length);
|
||||
}
|
||||
expect_stdout: "0"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4588_2_unused: {
|
||||
options = {
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a, b = void 0, c, d = "foo") {}.length);
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a, b = 0, c, d) {}.length);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
expect_warnings: [
|
||||
"WARN: Dropping unused default argument assignment d [test/compress/default-values.js:1,47]",
|
||||
"WARN: Dropping unused default argument value b [test/compress/default-values.js:1,32]",
|
||||
]
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4588_1_evaluate: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a = 42) {}.length);
|
||||
}
|
||||
expect: {
|
||||
console.log(0);
|
||||
}
|
||||
expect_stdout: "0"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4588_2_evaluate: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a, b = void 0, c, d = "foo") {}.length);
|
||||
}
|
||||
expect: {
|
||||
console.log(1);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
@@ -2487,3 +2487,72 @@ issue_4554: {
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4584: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
try {
|
||||
(function f({
|
||||
[console.log(a = "FAIL")]: a,
|
||||
}) {})(0);
|
||||
} catch (e) {
|
||||
console.log("PASS");
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
try {
|
||||
(function f({
|
||||
[console.log(a = "FAIL")]: a,
|
||||
}) {})(0);
|
||||
} catch (e) {
|
||||
console.log("PASS");
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4608_1: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: false,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
[ arguments ] = [ "foo" ];
|
||||
console.log(arguments[0]);
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
[ arguments ] = [ "foo" ];
|
||||
console.log(arguments[0]);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "f"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4608_2: {
|
||||
options = {
|
||||
arguments: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function(a) {
|
||||
[ arguments ] = [ "foo" ];
|
||||
console.log(arguments[0]);
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function(a) {
|
||||
[ arguments ] = [ "foo" ];
|
||||
console.log(arguments[0]);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "f"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
@@ -2138,6 +2138,7 @@ issue_3497: {
|
||||
issue_3515_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
@@ -2189,6 +2190,7 @@ issue_3515_2: {
|
||||
issue_3515_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
@@ -2256,6 +2258,7 @@ function_assign: {
|
||||
issue_3598: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
|
||||
@@ -703,6 +703,7 @@ prototype_function: {
|
||||
var g = 0();
|
||||
var h = 0();
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
call_args: {
|
||||
@@ -2800,7 +2801,7 @@ operator_in: {
|
||||
console.log("PASS" in { });
|
||||
console.log("FAIL" in { });
|
||||
console.log("toString" in { });
|
||||
console.log(true);
|
||||
console.log("toString" in { toString: 3 });
|
||||
}
|
||||
expect_stdout: [
|
||||
"true",
|
||||
|
||||
58
test/compress/exponentiation.js
Normal file
58
test/compress/exponentiation.js
Normal file
@@ -0,0 +1,58 @@
|
||||
precedence_1: {
|
||||
input: {
|
||||
console.log(-4 ** 3 ** 2);
|
||||
}
|
||||
expect_exact: "console.log((-4)**3**2);"
|
||||
expect_stdout: "-262144"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
precedence_2: {
|
||||
input: {
|
||||
console.log(-4 ** (3 ** 2));
|
||||
}
|
||||
expect_exact: "console.log((-4)**3**2);"
|
||||
expect_stdout: "-262144"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
precedence_3: {
|
||||
input: {
|
||||
console.log(-(4 ** 3) ** 2);
|
||||
}
|
||||
expect_exact: "console.log((-(4**3))**2);"
|
||||
expect_stdout: "4096"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
precedence_4: {
|
||||
input: {
|
||||
console.log((-4 ** 3) ** 2);
|
||||
}
|
||||
expect_exact: "console.log(((-4)**3)**2);"
|
||||
expect_stdout: "4096"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
await: {
|
||||
input: {
|
||||
(async a => a * await a ** ++a % a)(2).then(console.log);
|
||||
}
|
||||
expect_exact: "(async a=>a*(await a)**++a%a)(2).then(console.log);"
|
||||
expect_stdout: "1"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
evaluate: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
console.log(1 + 2 ** 3 - 4);
|
||||
}
|
||||
expect: {
|
||||
console.log(5);
|
||||
}
|
||||
expect_stdout: "5"
|
||||
node_version: ">=8"
|
||||
}
|
||||
@@ -5283,3 +5283,127 @@ issue_4471: {
|
||||
"PASS",
|
||||
]
|
||||
}
|
||||
|
||||
issue_4612_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
function f() {
|
||||
return g();
|
||||
}
|
||||
function g(a) {
|
||||
return a || f();
|
||||
}
|
||||
return g("PASS");
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_4612_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
function fn() {
|
||||
return h();
|
||||
}
|
||||
function g() {
|
||||
return fn();
|
||||
}
|
||||
function h(a) {
|
||||
return a || fn();
|
||||
}
|
||||
return h("PASS");
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_4612_3: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof function() {
|
||||
return g();
|
||||
function f() {
|
||||
return g;
|
||||
}
|
||||
function g() {
|
||||
{
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(typeof function() {
|
||||
return g();
|
||||
function f() {
|
||||
return g;
|
||||
}
|
||||
function g() {
|
||||
return f;
|
||||
}
|
||||
}());
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_4612_4: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
function f() {
|
||||
return h();
|
||||
}
|
||||
function g() {
|
||||
{
|
||||
return h();
|
||||
}
|
||||
}
|
||||
function h() {
|
||||
{
|
||||
return g();
|
||||
}
|
||||
}
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
function f() {
|
||||
return h();
|
||||
}
|
||||
function g() {
|
||||
return h();
|
||||
}
|
||||
function h() {
|
||||
return g();
|
||||
}
|
||||
}());
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ issue_269_1: {
|
||||
expect: {
|
||||
var x = {};
|
||||
console.log(
|
||||
x + "", +x, !!x,
|
||||
"" + x, +("" + x), !!x,
|
||||
"", 0, false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ evaluate_3: {
|
||||
console.log(1 + Number(x) + 2);
|
||||
}
|
||||
expect: {
|
||||
console.log(+x + 3);
|
||||
console.log(+("" + x) + 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -248,6 +248,35 @@ issue_2110_2: {
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_2110_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
function g() {
|
||||
return this;
|
||||
}
|
||||
console.log(typeof function() {
|
||||
function f() {}
|
||||
f.g = g;
|
||||
return f.g();
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
function g() {
|
||||
return this;
|
||||
}
|
||||
console.log(typeof function() {
|
||||
function f() {}
|
||||
f.g = g;
|
||||
return f.g();
|
||||
}());
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
set_immutable_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
@@ -979,6 +1008,7 @@ collapse_vars_2_strict: {
|
||||
collapse_rhs_true: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
pure_getters: true,
|
||||
}
|
||||
input: {
|
||||
@@ -1015,6 +1045,7 @@ collapse_rhs_true: {
|
||||
collapse_rhs_false: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
pure_getters: false,
|
||||
}
|
||||
input: {
|
||||
@@ -1051,6 +1082,7 @@ collapse_rhs_false: {
|
||||
collapse_rhs_strict: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
pure_getters: "strict",
|
||||
}
|
||||
input: {
|
||||
@@ -1087,6 +1119,7 @@ collapse_rhs_strict: {
|
||||
collapse_rhs_setter: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
pure_getters: "strict",
|
||||
}
|
||||
input: {
|
||||
|
||||
@@ -663,3 +663,36 @@ issue_4562: {
|
||||
expect_stdout: "f"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4575: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
ie8: true,
|
||||
reduce_vars: true,
|
||||
rests: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
A = "PASS";
|
||||
(function() {
|
||||
var a = 0, b = a;
|
||||
var c = function a(...b) {
|
||||
A;
|
||||
var d = A;
|
||||
console.log(d, b.length);
|
||||
}();
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
A = "PASS";
|
||||
(function() {
|
||||
(function(b) {
|
||||
A;
|
||||
var d = A;
|
||||
console.log(d, b.length);
|
||||
})([]);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS 0"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
@@ -13,6 +13,23 @@ console_log: {
|
||||
]
|
||||
}
|
||||
|
||||
console_log_console: {
|
||||
input: {
|
||||
var log = console.log;
|
||||
log(console);
|
||||
log(typeof console.log);
|
||||
}
|
||||
expect: {
|
||||
var log = console.log;
|
||||
log(console);
|
||||
log(typeof console.log);
|
||||
}
|
||||
expect_stdout: [
|
||||
"{ log: 'function(){}' }",
|
||||
"function",
|
||||
]
|
||||
}
|
||||
|
||||
typeof_arguments: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
@@ -81,6 +98,63 @@ log_global: {
|
||||
expect_stdout: "[object global]"
|
||||
}
|
||||
|
||||
log_nested: {
|
||||
options = {
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var o = { p: 42 };
|
||||
for (var i = 0; i < 10; i++)
|
||||
o = {
|
||||
p: o,
|
||||
q: function foo() {},
|
||||
};
|
||||
console.log(o);
|
||||
}
|
||||
expect: {
|
||||
var o = { p: 42 };
|
||||
for (var i = 0; i < 10; i++)
|
||||
o = {
|
||||
p: o,
|
||||
q: function() {},
|
||||
};
|
||||
console.log(o);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
timers: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var count = 0, interval = 1000, duration = 3210;
|
||||
var timer = setInterval(function() {
|
||||
console.log(++count);
|
||||
}, interval);
|
||||
setTimeout(function() {
|
||||
clearInterval(timer);
|
||||
}, duration);
|
||||
}
|
||||
expect: {
|
||||
var count = 0;
|
||||
var timer = setInterval(function() {
|
||||
console.log(++count);
|
||||
}, 1000);
|
||||
setTimeout(function() {
|
||||
clearInterval(timer);
|
||||
}, 3210);
|
||||
}
|
||||
expect_stdout: [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
]
|
||||
node_version: ">=0.12"
|
||||
}
|
||||
|
||||
issue_4054: {
|
||||
input: {
|
||||
console.log({
|
||||
|
||||
@@ -918,3 +918,32 @@ issue_4560_3: {
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_4614: {
|
||||
options = {
|
||||
pure_getters: "strict",
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
try {
|
||||
(function(...[]) {
|
||||
var arguments;
|
||||
arguments[0];
|
||||
})();
|
||||
} catch (e) {
|
||||
console.log("PASS");
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
try {
|
||||
(function(...[]) {
|
||||
var arguments;
|
||||
arguments[0];
|
||||
})();
|
||||
} catch (e) {
|
||||
console.log("PASS");
|
||||
}
|
||||
}
|
||||
expect_stdout: true
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
285
test/compress/templates.js
Normal file
285
test/compress/templates.js
Normal file
@@ -0,0 +1,285 @@
|
||||
simple: {
|
||||
input: {
|
||||
console.log(`foo
|
||||
bar\nbaz`);
|
||||
}
|
||||
expect_exact: "console.log(`foo\n bar\\nbaz`);"
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
" bar",
|
||||
"baz",
|
||||
]
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
placeholder: {
|
||||
input: {
|
||||
console.log(`foo ${ function(a, b) {
|
||||
return a * b;
|
||||
}(6, 7) }`);
|
||||
}
|
||||
expect_exact: "console.log(`foo ${function(a,b){return a*b}(6,7)}`);"
|
||||
expect_stdout: "foo 42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
nested: {
|
||||
input: {
|
||||
console.log(`P${`A${"S"}`}S`);
|
||||
}
|
||||
expect_exact: 'console.log(`P${`A${"S"}`}S`);'
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
tagged: {
|
||||
input: {
|
||||
console.log(String.raw`foo\nbar`);
|
||||
}
|
||||
expect_exact: "console.log(String.raw`foo\\nbar`);"
|
||||
expect_stdout: "foo\\nbar"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
tagged_chain: {
|
||||
input: {
|
||||
function f(strings) {
|
||||
return strings.join("") || f;
|
||||
}
|
||||
console.log(f```${42}``pass`.toUpperCase());
|
||||
}
|
||||
expect_exact: 'function f(strings){return strings.join("")||f}console.log(f```${42}``pass`.toUpperCase());'
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
tag_parenthesis_arrow: {
|
||||
input: {
|
||||
console.log((s => s.raw[0])`\tPASS`.slice(2));
|
||||
}
|
||||
expect_exact: "console.log((s=>s.raw[0])`\\tPASS`.slice(2));"
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
tag_parenthesis_new: {
|
||||
input: {
|
||||
(new function() {
|
||||
return console.log;
|
||||
})`foo`;
|
||||
}
|
||||
expect_exact: "(new function(){return console.log})`foo`;"
|
||||
expect_stdout: true
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
malformed_escape: {
|
||||
input: {
|
||||
(function(s) {
|
||||
s.forEach((c, i) => console.log(i, c, s.raw[i]));
|
||||
return () => console.log(arguments);
|
||||
})`\uFo${42}`();
|
||||
}
|
||||
expect_exact: "(function(s){s.forEach((c,i)=>console.log(i,c,s.raw[i]));return()=>console.log(arguments)})`\\uFo${42}`();"
|
||||
expect_stdout: true
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
evaluate: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: false,
|
||||
}
|
||||
input: {
|
||||
console.log(`foo ${ function(a, b) {
|
||||
return a * b;
|
||||
}(6, 7) }`);
|
||||
}
|
||||
expect: {
|
||||
console.log(`foo ${42}`);
|
||||
}
|
||||
expect_stdout: "foo 42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
evaluate_templates: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: true,
|
||||
}
|
||||
input: {
|
||||
console.log(`foo ${ function(a, b) {
|
||||
return a * b;
|
||||
}(6, 7) }`);
|
||||
}
|
||||
expect: {
|
||||
console.log("foo 42");
|
||||
}
|
||||
expect_stdout: "foo 42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
partial_evaluate: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: true,
|
||||
}
|
||||
input: {
|
||||
console.log(`${6 * 7} foo ${console ? `PA` + "SS" : `FA` + `IL`}`);
|
||||
}
|
||||
expect: {
|
||||
console.log(`42 foo ${console ? "PASS" : "FAIL"}`);
|
||||
}
|
||||
expect_stdout: "42 foo PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
malformed_evaluate_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: true,
|
||||
}
|
||||
input: {
|
||||
console.log(`\67 ${6 * 7}`);
|
||||
}
|
||||
expect: {
|
||||
console.log(`\67 42`);
|
||||
}
|
||||
expect_stdout: true
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
malformed_evaluate_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: true,
|
||||
}
|
||||
input: {
|
||||
console.log(`\u0${0}b${5}`);
|
||||
}
|
||||
expect: {
|
||||
console.log(`\u0${0}b5`);
|
||||
}
|
||||
expect_stdout: true
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
malformed_evaluate_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: true,
|
||||
}
|
||||
input: {
|
||||
console.log(`\u${0}b${5}`);
|
||||
}
|
||||
expect: {
|
||||
console.log(`\u0b5`);
|
||||
}
|
||||
expect_stdout: true
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
malformed_evaluate_4: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(String.raw`\u0${0}b${5}`);
|
||||
}
|
||||
expect: {
|
||||
console.log("\\u00b5");
|
||||
}
|
||||
expect_stdout: "\\u00b5"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
unsafe_evaluate: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(String.raw`\uFo`);
|
||||
}
|
||||
expect: {
|
||||
console.log("\\uFo");
|
||||
}
|
||||
expect_stdout: "\\uFo"
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
side_effects: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
`42`;
|
||||
`${console.log("foo")}`;
|
||||
console.log`\nbar`;
|
||||
}
|
||||
expect: {
|
||||
console.log("foo");
|
||||
console.log`\nbar`;
|
||||
}
|
||||
expect_stdout: true
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
unsafe_side_effects: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
`42`;
|
||||
`${console.log("foo")}`;
|
||||
String.raw`\nbar`;
|
||||
}
|
||||
expect: {
|
||||
console.log("foo");
|
||||
}
|
||||
expect_stdout: "foo"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4604: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 0, log = console.log;
|
||||
a = "FAIL";
|
||||
(function() {
|
||||
a = "PASS";
|
||||
})``;
|
||||
log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = 0, log = console.log;
|
||||
a = "FAIL";
|
||||
(function() {
|
||||
a = "PASS";
|
||||
})``;
|
||||
log(a);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4606: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
templates: true,
|
||||
}
|
||||
input: {
|
||||
console.log(`${typeof A} ${"\r"} ${"\\"} ${"`"}`);
|
||||
}
|
||||
expect: {
|
||||
console.log(`${typeof A} \r \\ \``);
|
||||
}
|
||||
expect_stdout: "undefined \r \\ `"
|
||||
node_version: ">=4"
|
||||
}
|
||||
@@ -56,7 +56,7 @@ describe("bin/uglifyjs", function() {
|
||||
"--source-map", [
|
||||
"names=true",
|
||||
"url=inline",
|
||||
].join(","),
|
||||
].join(),
|
||||
].join(" "), function(err, stdout) {
|
||||
if (err) throw err;
|
||||
var expected = [
|
||||
@@ -84,7 +84,7 @@ describe("bin/uglifyjs", function() {
|
||||
"--source-map", [
|
||||
"names=false",
|
||||
"url=inline",
|
||||
].join(","),
|
||||
].join(),
|
||||
].join(" "), function(err, stdout) {
|
||||
if (err) throw err;
|
||||
var expected = [
|
||||
@@ -171,7 +171,7 @@ describe("bin/uglifyjs", function() {
|
||||
"content=" + mapFile,
|
||||
"includeSources",
|
||||
"url=inline",
|
||||
].join(","),
|
||||
].join(),
|
||||
].join(" ");
|
||||
|
||||
var child = exec(command, function(err, stdout) {
|
||||
|
||||
@@ -115,8 +115,8 @@ describe("String literals", function() {
|
||||
UglifyJS.parse(test);
|
||||
}, function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error
|
||||
&& e.message === "Invalid hex-character pattern in string";
|
||||
});
|
||||
&& /^Invalid escape sequence: \\u/.test(e.message);
|
||||
}, test);
|
||||
});
|
||||
});
|
||||
it("Should reject invalid code points in Unicode escape sequence", function() {
|
||||
@@ -130,8 +130,8 @@ describe("String literals", function() {
|
||||
UglifyJS.parse(test);
|
||||
}, function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error
|
||||
&& /^Invalid character code: /.test(e.message);
|
||||
});
|
||||
&& /^Invalid escape sequence: \\u{1/.test(e.message);
|
||||
}, test);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
67
test/mocha/templates.js
Normal file
67
test/mocha/templates.js
Normal file
@@ -0,0 +1,67 @@
|
||||
var assert = require("assert");
|
||||
var run_code = require("../sandbox").run_code;
|
||||
var semver = require("semver");
|
||||
var UglifyJS = require("../node");
|
||||
|
||||
describe("Template literals", function() {
|
||||
it("Should reject invalid literal", function() {
|
||||
[
|
||||
"`foo\\`",
|
||||
"`foo${bar`",
|
||||
"`foo${bar}",
|
||||
].forEach(function(input) {
|
||||
assert.throws(function() {
|
||||
UglifyJS.parse(input);
|
||||
}, function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error
|
||||
&& e.message === "Unterminated template literal";
|
||||
}, input);
|
||||
});
|
||||
});
|
||||
it("Should reject invalid expression", function() {
|
||||
[
|
||||
"`foo${bar;}`",
|
||||
"`foo${42bar}`",
|
||||
].forEach(function(input) {
|
||||
assert.throws(function() {
|
||||
UglifyJS.parse(input);
|
||||
}, function(e) {
|
||||
return e instanceof UglifyJS.JS_Parse_Error;
|
||||
}, input);
|
||||
});
|
||||
});
|
||||
it("Should process line-break characters correctly", function() {
|
||||
[
|
||||
// native line breaks
|
||||
[ "`foo\nbar`", "`foo\nbar`" ],
|
||||
[ "`foo\rbar`", "`foo\rbar`" ],
|
||||
[ "`foo\r\nbar`", "`foo\nbar`" ],
|
||||
[ "`foo\r\n\rbar`", "`foo\n\rbar`" ],
|
||||
// escaped line breaks
|
||||
[ "`foo\\nbar`", "`foo\\nbar`" ],
|
||||
[ "`foo\\rbar`", "`foo\\rbar`" ],
|
||||
[ "`foo\r\\nbar`", "`foo\r\\nbar`" ],
|
||||
[ "`foo\\r\nbar`", "`foo\\r\nbar`" ],
|
||||
[ "`foo\\r\\nbar`", "`foo\\r\\nbar`" ],
|
||||
// continuation
|
||||
[ "`foo\\\nbar`", "`foo\\\nbar`" ],
|
||||
[ "`foo\\\rbar`", "`foo\\\rbar`" ],
|
||||
[ "`foo\\\r\nbar`", "`foo\\\nbar`" ],
|
||||
[ "`foo\\\r\n\rbar`", "`foo\\\n\rbar`" ],
|
||||
[ "`foo\\\\nbar`", "`foo\\\\nbar`" ],
|
||||
[ "`foo\\\\rbar`", "`foo\\\\rbar`" ],
|
||||
[ "`foo\\\\r\nbar`", "`foo\\\\r\nbar`" ],
|
||||
].forEach(function(test) {
|
||||
var input = "console.log(" + test[0] + ");";
|
||||
var result = UglifyJS.minify(input, {
|
||||
compress: false,
|
||||
mangle: false,
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
var expected = "console.log(" + test[1] + ");";
|
||||
assert.strictEqual(result.code, expected, test[0]);
|
||||
if (semver.satisfies(process.version, "<4")) return;
|
||||
assert.strictEqual(run_code(result.code), run_code(input), test[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -70,8 +70,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
||||
} else if (differs.error) {
|
||||
differs.warnings = warnings;
|
||||
return differs;
|
||||
} else if (is_error(differs.unminified_result)
|
||||
&& is_error(differs.minified_result)
|
||||
} else if (sandbox.is_error(differs.unminified_result)
|
||||
&& sandbox.is_error(differs.minified_result)
|
||||
&& differs.unminified_result.name == differs.minified_result.name) {
|
||||
return {
|
||||
code: [
|
||||
@@ -558,8 +558,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
||||
log(code);
|
||||
log(diff.error.stack);
|
||||
log("*** Discarding permutation and continuing.");
|
||||
} else if (is_error(diff.unminified_result)
|
||||
&& is_error(diff.minified_result)
|
||||
} else if (sandbox.is_error(diff.unminified_result)
|
||||
&& sandbox.is_error(diff.minified_result)
|
||||
&& diff.unminified_result.name == diff.minified_result.name) {
|
||||
// ignore difference in error messages caused by minification
|
||||
diff_error_message = testcase;
|
||||
@@ -600,10 +600,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
||||
}
|
||||
var lines = [ "" ];
|
||||
if (isNaN(max_timeout)) {
|
||||
lines.push("// minify error: " + to_comment(strip_color_codes(differs.minified_result.stack)));
|
||||
lines.push("// minify error: " + to_comment(differs.minified_result.stack));
|
||||
} else {
|
||||
var unminified_result = strip_color_codes(differs.unminified_result);
|
||||
var minified_result = strip_color_codes(differs.minified_result);
|
||||
var unminified_result = differs.unminified_result;
|
||||
var minified_result = differs.minified_result;
|
||||
if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) {
|
||||
lines.push(
|
||||
"// (stringified)",
|
||||
@@ -624,10 +624,6 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
||||
}
|
||||
};
|
||||
|
||||
function strip_color_codes(value) {
|
||||
return ("" + value).replace(/\u001b\[\d+m/g, "");
|
||||
}
|
||||
|
||||
function to_comment(value) {
|
||||
return ("" + value).replace(/\n/g, "\n// ");
|
||||
}
|
||||
@@ -665,12 +661,8 @@ function has_loopcontrol(body, loop, label) {
|
||||
return found;
|
||||
}
|
||||
|
||||
function is_error(result) {
|
||||
return result && typeof result.name == "string" && typeof result.message == "string";
|
||||
}
|
||||
|
||||
function is_timed_out(result) {
|
||||
return is_error(result) && /timed out/.test(result.message);
|
||||
return sandbox.is_error(result) && /timed out/.test(result.message);
|
||||
}
|
||||
|
||||
function is_statement(node) {
|
||||
|
||||
344
test/sandbox.js
344
test/sandbox.js
@@ -1,103 +1,39 @@
|
||||
var readFileSync = require("fs").readFileSync;
|
||||
var semver = require("semver");
|
||||
var spawnSync = require("child_process").spawnSync;
|
||||
var vm = require("vm");
|
||||
|
||||
var setupContext = new vm.Script([
|
||||
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {",
|
||||
" f.toString = Function.prototype.toString;",
|
||||
"});",
|
||||
"Function.prototype.toString = function() {",
|
||||
" var id = 100000;",
|
||||
" return function() {",
|
||||
" var n = this.name;",
|
||||
" if (!/^F[0-9]{6}N$/.test(n)) {",
|
||||
' n = "F" + ++id + "N";',
|
||||
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
|
||||
' Object.defineProperty(this, "name", {',
|
||||
" get: function() {",
|
||||
" return n;",
|
||||
" }",
|
||||
" });",
|
||||
] : [], [
|
||||
" }",
|
||||
' return "function(){}";',
|
||||
" };",
|
||||
"}();",
|
||||
"this;",
|
||||
]).join("\n"));
|
||||
|
||||
function createContext() {
|
||||
var ctx = vm.createContext(Object.defineProperties({}, {
|
||||
console: { value: { log: log } },
|
||||
global: { get: self },
|
||||
self: { get: self },
|
||||
window: { get: self },
|
||||
}));
|
||||
var global = setupContext.runInContext(ctx);
|
||||
return ctx;
|
||||
|
||||
function self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
function safe_log(arg, level) {
|
||||
if (arg) switch (typeof arg) {
|
||||
case "function":
|
||||
return arg.toString();
|
||||
case "object":
|
||||
if (arg === global) return "[object global]";
|
||||
if (/Error$/.test(arg.name)) return arg.toString();
|
||||
if (typeof arg.then == "function") return "[object Promise]";
|
||||
arg.constructor.toString();
|
||||
if (level--) for (var key in arg) {
|
||||
var desc = Object.getOwnPropertyDescriptor(arg, key);
|
||||
if (!desc || !desc.get && !desc.set) arg[key] = safe_log(arg[key], level);
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
function log(msg) {
|
||||
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
|
||||
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
||||
return safe_log(arg, 3);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function run_code(code, toplevel, timeout) {
|
||||
timeout = timeout || 5000;
|
||||
var stdout = "";
|
||||
var original_write = process.stdout.write;
|
||||
process.stdout.write = function(chunk) {
|
||||
stdout += chunk;
|
||||
};
|
||||
try {
|
||||
vm.runInContext(toplevel ? "(function(){" + code + "})()" : code, createContext(), { timeout: timeout });
|
||||
return stdout;
|
||||
} catch (ex) {
|
||||
return ex;
|
||||
} finally {
|
||||
process.stdout.write = original_write;
|
||||
}
|
||||
}
|
||||
|
||||
setup_log();
|
||||
var setup_code = "(" + setup + ")(" + [
|
||||
"this",
|
||||
find_builtins(),
|
||||
setup_log,
|
||||
"function(process) {" + readFileSync(require.resolve("../tools/tty", "utf8")) + "}",
|
||||
].join(",\n") + ");\n";
|
||||
exports.has_toplevel = function(options) {
|
||||
return options.toplevel
|
||||
|| options.mangle && options.mangle.toplevel
|
||||
|| options.compress && options.compress.toplevel;
|
||||
};
|
||||
exports.is_error = is_error;
|
||||
exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) {
|
||||
var stdout = run_code(code, toplevel, timeout);
|
||||
var stdout = run_code_vm(code, toplevel, timeout);
|
||||
if (typeof stdout != "string" || !/arguments/.test(code)) return stdout;
|
||||
do {
|
||||
var prev = stdout;
|
||||
stdout = run_code(code, toplevel, timeout);
|
||||
stdout = run_code_vm(code, toplevel, timeout);
|
||||
} while (prev !== stdout);
|
||||
return stdout;
|
||||
} : run_code;
|
||||
|
||||
function strip_func_ids(text) {
|
||||
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
|
||||
}
|
||||
|
||||
} : semver.satisfies(process.version, "<0.12") ? run_code_vm : function(code, toplevel, timeout) {
|
||||
if (/\basync([ \t]+[^\s()[\]{},.&|!~=*%/+-]+|[ \t]*\([\s\S]*?\))[ \t]*=>|\b(async[ \t]+function|setInterval|setTimeout)\b/.test(code)) {
|
||||
return run_code_exec(code, toplevel, timeout);
|
||||
} else {
|
||||
return run_code_vm(code, toplevel, timeout);
|
||||
}
|
||||
};
|
||||
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
|
||||
if (typeof expected != typeof actual) return false;
|
||||
if (typeof expected == "object" && typeof expected.name == "string" && typeof expected.message == "string") {
|
||||
if (is_error(expected)) {
|
||||
if (expected.name !== actual.name) return false;
|
||||
if (typeof actual.message != "string") return false;
|
||||
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
|
||||
@@ -107,8 +43,228 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
|
||||
} : function(expected, actual) {
|
||||
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
|
||||
};
|
||||
exports.has_toplevel = function(options) {
|
||||
return options.toplevel
|
||||
|| options.mangle && options.mangle.toplevel
|
||||
|| options.compress && options.compress.toplevel;
|
||||
};
|
||||
|
||||
function is_error(result) {
|
||||
return result && typeof result.name == "string" && typeof result.message == "string";
|
||||
}
|
||||
|
||||
function strip_color_codes(value) {
|
||||
return value.replace(/\u001b\[\d+m/g, "");
|
||||
}
|
||||
|
||||
function strip_func_ids(text) {
|
||||
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
|
||||
}
|
||||
|
||||
function setup_log() {
|
||||
var inspect = require("util").inspect;
|
||||
if (inspect.defaultOptions) {
|
||||
var log_options = {
|
||||
breakLength: Infinity,
|
||||
colors: false,
|
||||
compact: true,
|
||||
customInspect: false,
|
||||
depth: Infinity,
|
||||
maxArrayLength: Infinity,
|
||||
maxStringLength: Infinity,
|
||||
showHidden: false,
|
||||
};
|
||||
for (var name in log_options) {
|
||||
if (name in inspect.defaultOptions) inspect.defaultOptions[name] = log_options[name];
|
||||
}
|
||||
}
|
||||
return inspect;
|
||||
}
|
||||
|
||||
function find_builtins() {
|
||||
setup_code = "console.log(Object.keys(this));";
|
||||
var builtins = run_code_vm("");
|
||||
if (semver.satisfies(process.version, ">=0.12")) builtins += ".concat(" + run_code_exec("") + ")";
|
||||
return builtins;
|
||||
}
|
||||
|
||||
function setup(global, builtins, setup_log, setup_tty) {
|
||||
[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {
|
||||
f.toString = Function.prototype.toString;
|
||||
});
|
||||
Function.prototype.toString = function() {
|
||||
var configurable = Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable;
|
||||
var id = 100000;
|
||||
return function() {
|
||||
var n = this.name;
|
||||
if (!/^F[0-9]{6}N$/.test(n)) {
|
||||
n = "F" + ++id + "N";
|
||||
if (configurable) Object.defineProperty(this, "name", {
|
||||
get: function() {
|
||||
return n;
|
||||
}
|
||||
});
|
||||
}
|
||||
return "function(){}";
|
||||
};
|
||||
}();
|
||||
var process = global.process;
|
||||
if (process) {
|
||||
setup_tty(process);
|
||||
var inspect = setup_log();
|
||||
process.on("uncaughtException", function(ex) {
|
||||
var value = ex;
|
||||
if (value instanceof Error) {
|
||||
value = {};
|
||||
for (var name in ex) {
|
||||
value[name] = ex[name];
|
||||
delete ex[name];
|
||||
}
|
||||
}
|
||||
process.stderr.write(inspect(value) + "\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n");
|
||||
throw ex;
|
||||
}).on("unhandledRejection", function() {});
|
||||
}
|
||||
var log = console.log;
|
||||
var safe_console = {
|
||||
log: function(msg) {
|
||||
if (arguments.length == 1 && typeof msg == "string") return log("%s", msg);
|
||||
return log.apply(null, [].map.call(arguments, function(arg) {
|
||||
return safe_log(arg, {
|
||||
level: 5,
|
||||
original: [],
|
||||
replaced: [],
|
||||
});
|
||||
}));
|
||||
},
|
||||
};
|
||||
var props = {
|
||||
// for Node.js v8
|
||||
console: {
|
||||
get: function() {
|
||||
return safe_console;
|
||||
},
|
||||
},
|
||||
global: { get: self },
|
||||
self: { get: self },
|
||||
window: { get: self },
|
||||
};
|
||||
[
|
||||
// for Node.js v0.12
|
||||
"Buffer",
|
||||
"clearInterval",
|
||||
"clearTimeout",
|
||||
// for Node.js v0.12
|
||||
"DTRACE_NET_STREAM_END",
|
||||
// for Node.js v8
|
||||
"process",
|
||||
"setInterval",
|
||||
"setTimeout",
|
||||
].forEach(function(name) {
|
||||
var value = global[name];
|
||||
props[name] = {
|
||||
get: function() {
|
||||
return value;
|
||||
},
|
||||
};
|
||||
});
|
||||
builtins.forEach(function(name) {
|
||||
try {
|
||||
delete global[name];
|
||||
} catch (e) {}
|
||||
});
|
||||
Object.defineProperties(global, props);
|
||||
// for Node.js v8+
|
||||
global.toString = function() {
|
||||
return "[object global]";
|
||||
};
|
||||
|
||||
function self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
function safe_log(arg, cache) {
|
||||
if (arg) switch (typeof arg) {
|
||||
case "function":
|
||||
return arg.toString();
|
||||
case "object":
|
||||
if (arg === global) return "[object global]";
|
||||
if (/Error$/.test(arg.name)) return arg.toString();
|
||||
if (typeof arg.then == "function") return "[object Promise]";
|
||||
arg.constructor.toString();
|
||||
var index = cache.original.indexOf(arg);
|
||||
if (index >= 0) return cache.replaced[index];
|
||||
if (--cache.level < 0) return "[object Object]";
|
||||
var value = {};
|
||||
cache.original.push(arg);
|
||||
cache.replaced.push(value);
|
||||
for (var key in arg) {
|
||||
var desc = Object.getOwnPropertyDescriptor(arg, key);
|
||||
if (desc && (desc.get || desc.set)) {
|
||||
Object.defineProperty(value, key, desc);
|
||||
} else {
|
||||
value[key] = safe_log(arg[key], cache);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
function run_code_vm(code, toplevel, timeout) {
|
||||
timeout = timeout || 5000;
|
||||
var stdout = "";
|
||||
var original_write = process.stdout.write;
|
||||
process.stdout.write = function(chunk) {
|
||||
stdout += chunk;
|
||||
};
|
||||
try {
|
||||
var ctx = vm.createContext({ console: console });
|
||||
// for Node.js v6
|
||||
vm.runInContext(setup_code, ctx);
|
||||
vm.runInContext(toplevel ? "(function(){" + code + "})();" : code, ctx, { timeout: timeout });
|
||||
return strip_color_codes(stdout);
|
||||
} catch (ex) {
|
||||
return ex;
|
||||
} finally {
|
||||
process.stdout.write = original_write;
|
||||
}
|
||||
}
|
||||
|
||||
function run_code_exec(code, toplevel, timeout) {
|
||||
if (toplevel) {
|
||||
code = setup_code + "(function(){" + code + "})();";
|
||||
} else {
|
||||
code = code.replace(/^((["'])[^"']*\2(;|$))?/, function(directive) {
|
||||
return directive + setup_code;
|
||||
});
|
||||
}
|
||||
var result = spawnSync(process.argv[0], [ '--max-old-space-size=2048' ], {
|
||||
encoding: "utf8",
|
||||
input: code,
|
||||
stdio: "pipe",
|
||||
timeout: timeout || 5000,
|
||||
});
|
||||
if (result.status === 0) return result.stdout;
|
||||
if (result.error && result.error.code == "ETIMEDOUT" || /FATAL ERROR:/.test(msg)) {
|
||||
return new Error("Script execution timed out.");
|
||||
}
|
||||
if (result.error) return result.error;
|
||||
var msg = result.stderr.replace(/\r\n/g, "\n");
|
||||
var end = msg.indexOf("\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n");
|
||||
var details;
|
||||
if (end >= 0) {
|
||||
details = msg.slice(0, end).replace(/<([1-9][0-9]*) empty items?>/g, function(match, count) {
|
||||
return new Array(+count).join();
|
||||
});
|
||||
try {
|
||||
details = vm.runInNewContext("(" + details + ")");
|
||||
} catch (e) {}
|
||||
}
|
||||
var match = /\n([^:\s]*Error)(?:: ([\s\S]+?))?\n( at [\s\S]+)\n$/.exec(msg);
|
||||
if (!match) return details;
|
||||
var ex = new global[match[1]](match[2]);
|
||||
ex.stack = ex.stack.slice(0, ex.stack.indexOf(" at ")) + match[3];
|
||||
if (typeof details == "object") {
|
||||
for (var name in details) ex[name] = details[name];
|
||||
} else if (end >= 0) {
|
||||
ex.details = details;
|
||||
}
|
||||
return ex;
|
||||
}
|
||||
|
||||
@@ -10,16 +10,17 @@ exports.init = function(url, auth, num) {
|
||||
exports.should_stop = function(callback) {
|
||||
read(base + "/actions/runs?per_page=100", function(reply) {
|
||||
if (!reply || !Array.isArray(reply.workflow_runs)) return;
|
||||
var runs = reply.workflow_runs.filter(function(workflow) {
|
||||
return workflow.status != "completed";
|
||||
}).sort(function(a, b) {
|
||||
var runs = reply.workflow_runs.sort(function(a, b) {
|
||||
return b.run_number - a.run_number;
|
||||
});
|
||||
var found = false, remaining = 20;
|
||||
(function next() {
|
||||
if (!runs.length) return;
|
||||
var workflow = runs.pop();
|
||||
if (workflow.event == "schedule" && workflow.run_number == run_number) found = true;
|
||||
var workflow;
|
||||
do {
|
||||
workflow = runs.pop();
|
||||
if (!workflow) return;
|
||||
if (workflow.event == "schedule" && workflow.run_number == run_number) found = true;
|
||||
} while (!found && workflow.status == "completed");
|
||||
read(workflow.jobs_url, function(reply) {
|
||||
if (!reply || !Array.isArray(reply.jobs)) return;
|
||||
if (!reply.jobs.every(function(job) {
|
||||
|
||||
@@ -134,16 +134,19 @@ var SUPPORT = function(matrix) {
|
||||
}({
|
||||
arrow: "a => 0;",
|
||||
async: "async function f(){}",
|
||||
bigint: "42n",
|
||||
catch_omit_var: "try {} catch {}",
|
||||
computed_key: "({[0]: 0});",
|
||||
const_block: "var a; { const a = 0; }",
|
||||
default_value: "[ a = 0 ] = [];",
|
||||
destructuring: "[] = [];",
|
||||
exponentiation: "0 ** 0",
|
||||
let: "let a;",
|
||||
rest: "var [...a] = [];",
|
||||
rest_object: "var {...a} = {};",
|
||||
spread: "[...[]];",
|
||||
spread_object: "({...0});",
|
||||
template: "``",
|
||||
trailing_comma: "function f(a,) {}",
|
||||
});
|
||||
|
||||
@@ -167,7 +170,7 @@ var VALUES = [
|
||||
"4",
|
||||
"5",
|
||||
"22",
|
||||
"-0", // 0/-0 !== 0
|
||||
"(-0)", // 0/-0 !== 0
|
||||
"23..toString()",
|
||||
"24 .toString()",
|
||||
"25. ",
|
||||
@@ -188,6 +191,12 @@ var VALUES = [
|
||||
'"function"',
|
||||
"this",
|
||||
];
|
||||
if (SUPPORT.bigint) VALUES = VALUES.concat([
|
||||
"(!0o644n)",
|
||||
"([3n][0] > 2)",
|
||||
"(-42n).toString()",
|
||||
"Number(0XDEADn << 16n | 0xbeefn)",
|
||||
]);
|
||||
|
||||
var BINARY_OPS = [
|
||||
" + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
|
||||
@@ -217,6 +226,7 @@ var BINARY_OPS = [
|
||||
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
|
||||
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
|
||||
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
|
||||
if (SUPPORT.exponentiation) BINARY_OPS.push("**");
|
||||
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
|
||||
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
|
||||
BINARY_OPS.push(" in ");
|
||||
@@ -317,8 +327,6 @@ var VAR_NAMES = [
|
||||
"NaN",
|
||||
"Infinity",
|
||||
"arguments",
|
||||
"Math",
|
||||
"parseInt",
|
||||
"async",
|
||||
"await",
|
||||
];
|
||||
@@ -402,8 +410,9 @@ function createParams(was_async, noDuplicate) {
|
||||
return addTrailingComma(params.join(", "));
|
||||
}
|
||||
|
||||
function createArgs(recurmax, stmtDepth, canThrow) {
|
||||
function createArgs(recurmax, stmtDepth, canThrow, noTemplate) {
|
||||
recurmax--;
|
||||
if (SUPPORT.template && !noTemplate && rng(20) == 0) return createTemplateLiteral(recurmax, stmtDepth, canThrow);
|
||||
var args = [];
|
||||
for (var n = rng(4); --n >= 0;) switch (SUPPORT.spread ? rng(50) : 3) {
|
||||
case 0:
|
||||
@@ -422,7 +431,7 @@ function createArgs(recurmax, stmtDepth, canThrow) {
|
||||
args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
|
||||
break;
|
||||
}
|
||||
return addTrailingComma(args.join(", "));
|
||||
return "(" + addTrailingComma(args.join(", ")) + ")";
|
||||
}
|
||||
|
||||
function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async) {
|
||||
@@ -433,6 +442,22 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
|
||||
unique_vars.length = len;
|
||||
return pairs;
|
||||
|
||||
function mapShuffled(values, fn) {
|
||||
var declare_only = [];
|
||||
var side_effects = [];
|
||||
values.forEach(function(value, index) {
|
||||
value = fn(value, index);
|
||||
if (/]:|=/.test(value) ? canThrow && rng(10) == 0 : rng(5)) {
|
||||
declare_only.splice(rng(declare_only.length + 1), 0, value);
|
||||
} else if (canThrow && rng(5) == 0) {
|
||||
side_effects.splice(rng(side_effects.length + 1), 0, value);
|
||||
} else {
|
||||
side_effects.push(value);
|
||||
}
|
||||
});
|
||||
return declare_only.concat(side_effects);
|
||||
}
|
||||
|
||||
function convertToRest(names) {
|
||||
var last = names.length - 1;
|
||||
if (last >= 0 && SUPPORT.rest && rng(20) == 0) {
|
||||
@@ -563,19 +588,24 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
|
||||
}
|
||||
});
|
||||
fill(function() {
|
||||
var last = pairs.names.length - 1, has_rest = false;
|
||||
var s = pairs.names.map(function(name, index) {
|
||||
var last = pairs.names.length - 1, rest;
|
||||
if (last >= 0 && !(last in keys) && SUPPORT.rest_object && rng(20) == 0) {
|
||||
rest = pairs.names.pop();
|
||||
if (!/=/.test(rest)) rest = "..." + rest;
|
||||
}
|
||||
var s = mapShuffled(pairs.names, function(name, index) {
|
||||
if (index in keys) return keys[index] + ": " + name;
|
||||
if (index == last && SUPPORT.rest_object && rng(20) == 0 && name.indexOf("=") < 0) {
|
||||
has_rest = true;
|
||||
return "..." + name;
|
||||
}
|
||||
return rng(10) == 0 ? name : createKey(recurmax, keys) + ": " + name;
|
||||
}).join(", ");
|
||||
if (!has_rest) s = addTrailingComma(s);
|
||||
});
|
||||
if (rest) {
|
||||
s.push(rest);
|
||||
s = s.join(", ");
|
||||
} else {
|
||||
s = addTrailingComma(s.join(", "));
|
||||
}
|
||||
names.unshift("{ " + s + " }" + createDefaultValue(recurmax, noDefault));
|
||||
}, function() {
|
||||
values.unshift("{ " + addTrailingComma(pairs.values.map(function(value, index) {
|
||||
values.unshift("{ " + addTrailingComma(mapShuffled(pairs.values, function(value, index) {
|
||||
var key = index in keys ? keys[index] : createKey(recurmax, keys);
|
||||
return key + ": " + value;
|
||||
}).join(", ")) + " }");
|
||||
@@ -702,7 +732,7 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||
params = pairs.names.join(", ");
|
||||
if (!pairs.has_rest) params = addTrailingComma(params);
|
||||
args = addTrailingComma(pairs.values.join(", "));
|
||||
args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
|
||||
} else {
|
||||
params = createParams(save_async);
|
||||
}
|
||||
@@ -724,10 +754,10 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
||||
if (!allowDefun) {
|
||||
// avoid "function statements" (decl inside statements)
|
||||
s = "var " + createVarName(MANDATORY) + " = " + s;
|
||||
s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")";
|
||||
s += args || createArgs(recurmax, stmtDepth, canThrow);
|
||||
} else if (!(name in called) || args || rng(3)) {
|
||||
s += "var " + createVarName(MANDATORY) + " = " + name;
|
||||
s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")";
|
||||
s += args || createArgs(recurmax, stmtDepth, canThrow);
|
||||
}
|
||||
|
||||
return s + ";";
|
||||
@@ -1010,6 +1040,11 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
case p++:
|
||||
return rng(2) + " === 1 ? a : b";
|
||||
case p++:
|
||||
if (SUPPORT.template && rng(20) == 0) {
|
||||
var tmpl = createTemplateLiteral(recurmax, stmtDepth, canThrow);
|
||||
if (rng(10) == 0) tmpl = "String.raw" + tmpl;
|
||||
return tmpl;
|
||||
}
|
||||
case p++:
|
||||
return createValue();
|
||||
case p++:
|
||||
@@ -1020,9 +1055,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
case 0:
|
||||
return [
|
||||
"[ ",
|
||||
new Array(rng(3)).join(","),
|
||||
new Array(rng(3)).join(),
|
||||
getVarName(NO_CONST),
|
||||
new Array(rng(3)).join(","),
|
||||
new Array(rng(3)).join(),
|
||||
" ] = ",
|
||||
createArrayLiteral(recurmax, stmtDepth, canThrow),
|
||||
].join("");
|
||||
@@ -1063,7 +1098,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||
params = pairs.names.join(", ");
|
||||
if (!pairs.has_rest) params = addTrailingComma(params);
|
||||
args = addTrailingComma(pairs.values.join(", "));
|
||||
args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
|
||||
} else {
|
||||
params = createParams(save_async, NO_DUPLICATE);
|
||||
}
|
||||
@@ -1095,7 +1130,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
async = save_async;
|
||||
VAR_NAMES.length = nameLenBefore;
|
||||
if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow);
|
||||
if (args) suffix += "(" + args + ")";
|
||||
if (args) suffix += args;
|
||||
s.push(suffix);
|
||||
} else {
|
||||
s.push(
|
||||
@@ -1132,8 +1167,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
break;
|
||||
default:
|
||||
async = false;
|
||||
var instantiate = rng(4) ? "new " : "";
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
var instantiate = rng(4) ? "new " : "";
|
||||
s.push(
|
||||
instantiate + "function " + name + "(" + createParams(save_async) + "){",
|
||||
strictMode(),
|
||||
@@ -1147,7 +1182,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
});
|
||||
async = save_async;
|
||||
VAR_NAMES.length = nameLenBefore;
|
||||
s.push(rng(2) ? "}" : "}(" + createArgs(recurmax, stmtDepth, canThrow) + ")");
|
||||
s.push(rng(2) ? "}" : "}" + createArgs(recurmax, stmtDepth, canThrow, instantiate));
|
||||
break;
|
||||
}
|
||||
async = save_async;
|
||||
@@ -1225,7 +1260,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
case p++:
|
||||
var name = getVarName();
|
||||
var s = name + "." + getDotKey();
|
||||
s = "typeof " + s + ' == "function" && --_calls_ >= 0 && ' + s + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
|
||||
s = "typeof " + s + ' == "function" && --_calls_ >= 0 && ' + s + createArgs(recurmax, stmtDepth, canThrow);
|
||||
return canThrow && rng(8) == 0 ? s : name + " && " + s;
|
||||
case p++:
|
||||
case p++:
|
||||
@@ -1236,7 +1271,7 @@ 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 "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
|
||||
return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + createArgs(recurmax, stmtDepth, canThrow);
|
||||
}
|
||||
_createExpression.N = p;
|
||||
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||
@@ -1270,6 +1305,28 @@ function createArrayLiteral(recurmax, stmtDepth, canThrow) {
|
||||
return "[" + arr.join(", ") + "]";
|
||||
}
|
||||
|
||||
function createTemplateLiteral(recurmax, stmtDepth, canThrow) {
|
||||
recurmax--;
|
||||
var s = [];
|
||||
addText();
|
||||
for (var i = rng(6); --i >= 0;) {
|
||||
s.push("${", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "}");
|
||||
addText();
|
||||
}
|
||||
return "`" + s.join(rng(5) ? "" : "\n") + "`";
|
||||
|
||||
function addText() {
|
||||
while (rng(5) == 0) s.push([
|
||||
" ",
|
||||
"$",
|
||||
"}",
|
||||
"\\`",
|
||||
"\\\\",
|
||||
"tmpl",
|
||||
][rng(6)]);
|
||||
}
|
||||
}
|
||||
|
||||
var SAFE_KEYS = [
|
||||
"length",
|
||||
"foo",
|
||||
@@ -1312,10 +1369,11 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||
var save_async = async;
|
||||
var s;
|
||||
var name = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
switch (rng(SUPPORT.computed_key ? 3 : 2)) {
|
||||
case 0:
|
||||
async = false;
|
||||
var fn;
|
||||
switch (rng(SUPPORT.computed_key ? 3 : 2)) {
|
||||
case 0:
|
||||
async = false;
|
||||
fn = function(defns) {
|
||||
s = [
|
||||
"get " + name + "(){",
|
||||
strictMode(),
|
||||
@@ -1324,13 +1382,15 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
|
||||
"},",
|
||||
];
|
||||
break;
|
||||
case 1:
|
||||
var prop;
|
||||
do {
|
||||
prop = getDotKey();
|
||||
} while (name == prop);
|
||||
async = false;
|
||||
};
|
||||
break;
|
||||
case 1:
|
||||
var prop;
|
||||
do {
|
||||
prop = getDotKey();
|
||||
} while (name == prop);
|
||||
async = false;
|
||||
fn = function(defns) {
|
||||
s = [
|
||||
"set " + name + "(" + createVarName(MANDATORY) + "){",
|
||||
strictMode(),
|
||||
@@ -1339,9 +1399,11 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||
"this." + prop + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
|
||||
"},",
|
||||
];
|
||||
break;
|
||||
default:
|
||||
async = SUPPORT.async && rng(50) == 0;
|
||||
};
|
||||
break;
|
||||
default:
|
||||
async = SUPPORT.async && rng(50) == 0;
|
||||
fn = function(defns) {
|
||||
s = [
|
||||
(async ? "async " : "") + name + "(" + createParams(save_async, NO_DUPLICATE) + "){",
|
||||
strictMode(),
|
||||
@@ -1349,9 +1411,10 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||
"},",
|
||||
]
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
break;
|
||||
}
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, fn);
|
||||
async = save_async;
|
||||
VAR_NAMES.length = nameLenBefore;
|
||||
return filterDirective(s).join("\n");
|
||||
@@ -1557,9 +1620,10 @@ function getVarName(noConst) {
|
||||
function createVarName(maybe, dontStore) {
|
||||
if (!maybe || rng(2)) {
|
||||
var suffix = rng(3);
|
||||
var name;
|
||||
var name, tries = 10;
|
||||
do {
|
||||
name = VAR_NAMES[rng(VAR_NAMES.length)];
|
||||
if (--tries < 0) suffix++;
|
||||
if (suffix) name += "_" + suffix;
|
||||
} while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0 || async && name == "await");
|
||||
if (!dontStore) VAR_NAMES.push(name);
|
||||
@@ -1743,24 +1807,52 @@ function log(options) {
|
||||
}
|
||||
|
||||
function sort_globals(code) {
|
||||
var globals = sandbox.run_code("throw Object.keys(this).sort();" + code);
|
||||
var globals = sandbox.run_code("throw Object.keys(this).sort(" + function(global) {
|
||||
return function(m, n) {
|
||||
return (n == "toString") - (m == "toString")
|
||||
|| (typeof global[n] == "function") - (typeof global[m] == "function")
|
||||
|| (m < n ? -1 : m > n ? 1 : 0);
|
||||
};
|
||||
} + "(this));" + code);
|
||||
if (!Array.isArray(globals)) {
|
||||
errorln();
|
||||
errorln();
|
||||
errorln("//-------------------------------------------------------------");
|
||||
errorln("// !!! sort_globals() failed !!!");
|
||||
errorln("// expected Array, got:");
|
||||
if (!sandbox.is_error(globals)) try {
|
||||
globals = JSON.stringify(globals);
|
||||
} catch (e) {}
|
||||
errorln(globals);
|
||||
errorln("//");
|
||||
errorln(code);
|
||||
errorln();
|
||||
return code;
|
||||
}
|
||||
return globals.length ? "var " + globals.map(function(name) {
|
||||
return name + "=" + name;
|
||||
}).join(",") + ";" + code : code;
|
||||
}).join() + ";" + code : code;
|
||||
}
|
||||
|
||||
function fuzzy_match(original, uglified) {
|
||||
uglified = uglified.split(" ");
|
||||
var i = uglified.length;
|
||||
original = original.split(" ", i);
|
||||
while (--i >= 0) {
|
||||
if (original[i] === uglified[i]) continue;
|
||||
var a = +original[i];
|
||||
var b = +uglified[i];
|
||||
if (Math.abs((b - a) / a) < 1e-10) continue;
|
||||
return false;
|
||||
var m = [], n = [];
|
||||
if (collect(original, m) !== collect(uglified, n)) return false;
|
||||
for (var i = 0; i < m.length; i++) {
|
||||
var a = m[i];
|
||||
var b = n[i];
|
||||
if (Math.abs((b - a) / a) > 1e-10) return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
function collect(input, nums) {
|
||||
return input.replace(/-?([1-9][0-9]*(\.[0-9]+)?|0\.[0-9]+)(e-?[1-9][0-9]*)?/ig, function(num) {
|
||||
return "<|" + nums.push(+num) + "|>";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function is_error_timeout(ex) {
|
||||
return /timed out/.test(ex.message);
|
||||
}
|
||||
|
||||
function is_error_in(ex) {
|
||||
@@ -1768,13 +1860,17 @@ function is_error_in(ex) {
|
||||
}
|
||||
|
||||
function is_error_spread(ex) {
|
||||
return ex.name == "TypeError" && /Found non-callable @@iterator|is not iterable|undefined is not a function/.test(ex.message);
|
||||
return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| is not a function/.test(ex.message);
|
||||
}
|
||||
|
||||
function is_error_recursion(ex) {
|
||||
return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message);
|
||||
}
|
||||
|
||||
function is_error_destructuring(ex) {
|
||||
return ex.name == "TypeError" && /^Cannot destructure /.test(ex.message);
|
||||
}
|
||||
|
||||
function patch_try_catch(orig, toplevel) {
|
||||
var stack = [ {
|
||||
code: orig,
|
||||
@@ -1782,6 +1878,7 @@ function patch_try_catch(orig, toplevel) {
|
||||
offset: 0,
|
||||
tries: [],
|
||||
} ];
|
||||
var tail_throw = '\nif (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;\n';
|
||||
var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)[{]+)\)|}\s*finally)\s*(?={)/g;
|
||||
while (stack.length) {
|
||||
var code = stack[0].code;
|
||||
@@ -1798,7 +1895,7 @@ function patch_try_catch(orig, toplevel) {
|
||||
var insert;
|
||||
if (/}\s*finally\s*$/.test(match[0])) {
|
||||
tries.shift();
|
||||
insert = 'if (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;';
|
||||
insert = tail_throw;
|
||||
} else {
|
||||
while (tries.length && tries[0].catch) tries.shift();
|
||||
tries[0].catch = index - offset;
|
||||
@@ -1812,9 +1909,9 @@ function patch_try_catch(orig, toplevel) {
|
||||
"throw " + match[1] + ";",
|
||||
].join("\n");
|
||||
}
|
||||
var new_code = code.slice(0, index) + insert + code.slice(index);
|
||||
var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw;
|
||||
var result = sandbox.run_code(new_code, toplevel);
|
||||
if (typeof result != "object" || typeof result.name != "string" || typeof result.message != "string") {
|
||||
if (!sandbox.is_error(result)) {
|
||||
if (!stack.filled && match[1]) stack.push({
|
||||
code: code,
|
||||
index: index && index - 1,
|
||||
@@ -1832,6 +1929,9 @@ function patch_try_catch(orig, toplevel) {
|
||||
} else if (is_error_recursion(result)) {
|
||||
index = result.ufuzz_try;
|
||||
return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
|
||||
} else if (is_error_destructuring(result)) {
|
||||
index = result.ufuzz_catch;
|
||||
return orig.slice(0, index) + result.ufuzz_var + ' = new Error("cannot destructure");' + orig.slice(index);
|
||||
}
|
||||
}
|
||||
stack.filled = true;
|
||||
@@ -1887,6 +1987,7 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
println("original result:");
|
||||
println(orig_result[0]);
|
||||
println();
|
||||
// ignore v8 parser bug
|
||||
if (is_bug_async_arrow_rest(orig_result[0])) continue;
|
||||
}
|
||||
minify_options.forEach(function(options) {
|
||||
@@ -1899,13 +2000,19 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
uglify_code = uglify_code.code;
|
||||
uglify_result = sandbox.run_code(uglify_code, toplevel);
|
||||
ok = sandbox.same_stdout(original_result, uglify_result);
|
||||
// ignore v8 parser bug
|
||||
if (!ok && is_bug_async_arrow_rest(uglify_result)) ok = true;
|
||||
// ignore declaration order of global variables
|
||||
if (!ok && !toplevel) {
|
||||
ok = sandbox.same_stdout(sandbox.run_code(sort_globals(original_code)), sandbox.run_code(sort_globals(uglify_code)));
|
||||
}
|
||||
// ignore numerical imprecision caused by `unsafe_math`
|
||||
if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == "string" && typeof uglify_result == "string") {
|
||||
ok = fuzzy_match(original_result, uglify_result);
|
||||
if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == typeof uglify_result) {
|
||||
if (typeof original_result == "string") {
|
||||
ok = fuzzy_match(original_result, uglify_result);
|
||||
} else if (sandbox.is_error(original_result)) {
|
||||
ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message);
|
||||
}
|
||||
if (!ok) {
|
||||
var fuzzy_result = sandbox.run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel);
|
||||
ok = sandbox.same_stdout(fuzzy_result, uglify_result);
|
||||
@@ -1913,10 +2020,15 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
}
|
||||
// ignore difference in error message caused by Temporal Dead Zone
|
||||
if (!ok && errored && uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError") ok = true;
|
||||
// ignore spurious time-outs
|
||||
if (!ok && errored && /timed out/.test(original_result.message) && !/timed out/.test(uglify_result.message)) {
|
||||
if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = sandbox.run_code(original_code, toplevel, 10000);
|
||||
ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result);
|
||||
if (!ok && errored && is_error_timeout(original_result)) {
|
||||
if (is_error_timeout(uglify_result)) {
|
||||
// ignore difference in error message
|
||||
ok = true;
|
||||
} else {
|
||||
// ignore spurious time-outs
|
||||
if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = sandbox.run_code(original_code, toplevel, 10000);
|
||||
ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result);
|
||||
}
|
||||
}
|
||||
// ignore difference in error message caused by `in`
|
||||
if (!ok && errored && is_error_in(uglify_result) && is_error_in(original_result)) ok = true;
|
||||
@@ -1926,6 +2038,10 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
if (!ok && errored && is_error_recursion(original_result)) {
|
||||
if (is_error_recursion(uglify_result) || typeof uglify_result == "string") ok = true;
|
||||
}
|
||||
// ignore difference in error message caused by destructuring
|
||||
if (!ok && errored && is_error_destructuring(uglify_result) && is_error_destructuring(original_result)) {
|
||||
ok = true;
|
||||
}
|
||||
// ignore errors above when caught by try-catch
|
||||
if (!ok) {
|
||||
var orig_skipped = patch_try_catch(original_code, toplevel);
|
||||
|
||||
Reference in New Issue
Block a user