suppress invalid test case generation (#5439)

- document v8 quirks

closes #5438
This commit is contained in:
Alex Lam S.L
2022-05-11 21:38:11 +01:00
committed by GitHub
parent a9ef659bcb
commit 8946c87011
2 changed files with 36 additions and 23 deletions

View File

@@ -1389,3 +1389,16 @@ To allow for better optimizations, the compiler makes various assumptions:
// Actual: "FAIL" // Actual: "FAIL"
``` ```
UglifyJS may modify the input which in turn may suppress those errors. UglifyJS may modify the input which in turn may suppress those errors.
- Some versions of Chrome and Node.js will give incorrect results with the
following:
```javascript
(async function(a) {
(function() {
var b = await => console.log("PASS");
b();
})();
})().catch(console.error);
// Expected: "PASS"
// Actual: SyntaxError: Unexpected reserved word
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -210,10 +210,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
} }
if (node.expression instanceof U.AST_Function) { if (node.expression instanceof U.AST_Function) {
// hoist and return expressions from the IIFE function expression // hoist and return expressions from the IIFE function expression
var seq = []; var scope = tt.find_parent(U.AST_Scope), seq = [];
node.expression.body.forEach(function(node) { node.expression.body.forEach(function(node) {
var expr = node instanceof U.AST_Exit ? node.value : node.body; var expr = node instanceof U.AST_Exit ? node.value : node.body;
if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr)) { if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr, scope)) {
// collect expressions from each statement's body // collect expressions from each statement's body
seq.push(expr); seq.push(expr);
} }
@@ -264,11 +264,12 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true; CHANGED = true;
return List.skip; return List.skip;
default: default:
if (!has_exit(node) && can_hoist(node)) { if (can_hoist(node, tt.find_parent(U.AST_Scope))) {
// hoist function declaration body // hoist function declaration body
var body = node.body; var body = node.body;
node.body = []; node.body = [];
body.push(node); // retain function with empty body to be dropped later // retain function with empty body to be dropped later
body.push(node);
CHANGED = true; CHANGED = true;
return List.splice(body); return List.splice(body);
} }
@@ -382,7 +383,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) { if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
// hoist simple statement IIFE function expression body // hoist simple statement IIFE function expression body
node.start._permute++; node.start._permute++;
if (!has_exit(node.body.expression) && can_hoist(node.body.expression)) { if (can_hoist(node.body.expression, tt.find_parent(U.AST_Scope))) {
CHANGED = true; CHANGED = true;
return List.splice(node.body.expression.body); return List.splice(node.body.expression.body);
} }
@@ -647,21 +648,6 @@ function trim_trailing_whitespace(value) {
return ("" + value).replace(/\s+$/, ""); return ("" + value).replace(/\s+$/, "");
} }
function has_exit(fn) {
var found = false;
var tw = new U.TreeWalker(function(node) {
if (found) return found;
if (node instanceof U.AST_Exit) {
return found = true;
}
if (node instanceof U.AST_Scope && node !== fn) {
return true; // don't descend into nested functions
}
});
fn.walk(tw);
return found;
}
function has_loopcontrol(body, loop, label) { function has_loopcontrol(body, loop, label) {
var found = false; var found = false;
var tw = new U.TreeWalker(function(node) { var tw = new U.TreeWalker(function(node) {
@@ -676,17 +662,31 @@ function has_loopcontrol(body, loop, label) {
return found; return found;
} }
function can_hoist(body) { function can_hoist(body, scope) {
var found = false; var found = false;
body.walk(new U.TreeWalker(function(node) { var tw = new U.TreeWalker(function(node) {
if (found) return true; if (found) return true;
if (node instanceof U.AST_Exit) return found = true;
if (node instanceof U.AST_NewTarget) return found = true; if (node instanceof U.AST_NewTarget) return found = true;
if (node instanceof U.AST_Scope) { if (node instanceof U.AST_Scope) {
if (node === body) return; if (node === body) return;
if (node instanceof U.AST_Arrow || node instanceof U.AST_AsyncArrow) node.argnames.forEach(function(sym) {
sym.walk(tw);
});
// don't descend into nested functions
return true; return true;
} }
if (node instanceof U.AST_Super) return found = true; if (node instanceof U.AST_Super) return found = true;
})); if (node instanceof U.AST_SymbolDeclaration || node instanceof U.AST_SymbolRef) switch (node.name) {
case "await":
if (/^Async/.test(scope.TYPE)) return found = true;
return;
case "yield":
if (/Generator/.test(scope.TYPE)) return found = true;
return;
}
});
body.walk(tw);
return !found; return !found;
} }