support async function (#4333)

This commit is contained in:
Alex Lam S.L
2020-12-06 21:22:40 +00:00
committed by GitHub
parent 3c384cf9a8
commit 2cbbf5c375
11 changed files with 434 additions and 62 deletions

218
test/compress/async.js Normal file
View File

@@ -0,0 +1,218 @@
await_await: {
input: {
(async function() {
console.log("PASS");
await await 42;
})();
}
expect: {
(async function() {
console.log("PASS");
await await 42;
})();
}
expect_stdout: "PASS"
node_version: ">=8"
}
defun_name: {
input: {
async function await() {
console.log("PASS");
}
await();
}
expect: {
async function await() {
console.log("PASS");
}
await();
}
expect_stdout: "PASS"
node_version: ">=8"
}
nested_await: {
input: {
(async function() {
console.log(function(await) {
return await;
}("PASS"));
})();
}
expect: {
(async function() {
console.log(function(await) {
return await;
}("PASS"));
})();
}
expect_stdout: "PASS"
node_version: ">=8"
}
reduce_single_use_defun: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
async function f(a) {
console.log(a);
}
f("PASS");
}
expect: {
(async function(a) {
console.log(a);
})("PASS");
}
expect_stdout: "PASS"
node_version: ">=8"
}
dont_inline: {
options = {
inline: true,
}
input: {
(async function() {
A;
})().catch(function() {});
console.log("PASS");
}
expect: {
(async function() {
A;
})().catch(function() {});
console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=8"
}
evaluate: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = async function() {}();
console.log(typeof a);
}
expect: {
var a = async function() {}();
console.log(typeof a);
}
expect_stdout: "object"
node_version: ">=8"
}
negate: {
options = {
side_effects: true,
}
input: {
console && async function() {} && console.log("PASS");
}
expect: {
console && async function() {} && console.log("PASS");
}
expect_stdout: "PASS"
node_version: ">=8"
}
negate_iife: {
options = {
negate_iife: true,
}
input: {
(async function() {
console.log("PASS");
})();
}
expect: {
!async function() {
console.log("PASS");
}();
}
expect_stdout: "PASS"
node_version: ">=8"
}
collapse_vars_1: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
(async function() {
a = "PASS";
await 42;
return "PASS";
})();
console.log(a);
}
expect: {
var a = "FAIL";
(async function() {
a = "PASS";
await 42;
return "PASS";
})();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=8"
}
collapse_vars_2: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
(async function() {
await (a = "PASS");
return "PASS";
})();
console.log(a);
}
expect: {
var a = "FAIL";
(async function() {
await (a = "PASS");
return "PASS";
})();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=8"
}
collapse_vars_3: {
options = {
collapse_vars: true,
}
input: {
var a = "FAIL";
(async function() {
await (a = "PASS", 42);
return "PASS";
})();
console.log(a);
}
expect: {
var a = "FAIL";
(async function() {
await (a = "PASS", 42);
return "PASS";
})();
console.log(a);
}
expect_stdout: "PASS"
node_version: ">=8"
}

23
test/mocha/async.js Normal file
View File

@@ -0,0 +1,23 @@
var assert = require("assert");
var UglifyJS = require("../node");
describe("async", function() {
it("Should reject `await` as symbol name within async functions only", function() {
[
"function await() {}",
"function(await) {}",
"function() { await; }",
"function() { await:{} }",
"function() { var await; }",
"function() { function await() {} }",
"function() { try {} catch (await) {} }",
].forEach(function(code) {
UglifyJS.parse("(" + code + ")();");
assert.throws(function() {
UglifyJS.parse("(async " + code + ")();");
}, function(e) {
return e instanceof UglifyJS.JS_Parse_Error;
}, code);
});
});
});

View File

@@ -618,7 +618,7 @@ function is_timed_out(result) {
}
function is_statement(node) {
return node instanceof U.AST_Statement && !(node instanceof U.AST_Function);
return node instanceof U.AST_Statement && !(node instanceof U.AST_AsyncFunction || node instanceof U.AST_Function);
}
function merge_sequence(array, node) {

View File

@@ -32,11 +32,12 @@ function createContext() {
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
case "function":
case "function":
return arg.toString();
case "object":
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);

View File

@@ -298,6 +298,8 @@ var VAR_NAMES = [
"arguments",
"Math",
"parseInt",
"async",
"await",
];
var INITIAL_NAMES_LEN = VAR_NAMES.length;
@@ -317,6 +319,7 @@ var TYPEOF_OUTCOMES = [
var avoid_vars = [];
var block_vars = [];
var unique_vars = [];
var async = false;
var loops = 0;
var funcs = 0;
var called = Object.create(null);
@@ -335,6 +338,7 @@ function createTopLevelCode() {
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
block_vars.length = 0;
unique_vars.length = 0;
async = false;
loops = 0;
funcs = 0;
called = Object.create(null);
@@ -393,7 +397,7 @@ function createArgs(recurmax, stmtDepth, canThrow) {
return args.join(", ");
}
function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames, maybe, dontStore) {
function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames, was_async) {
var avoid = [];
var len = unique_vars.length;
var pairs = createPairs(recurmax);
@@ -403,7 +407,10 @@ function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames,
function createAssignmentValue(recurmax) {
var current = VAR_NAMES;
VAR_NAMES = (varNames || VAR_NAMES).slice();
var save_async = async;
if (was_async != null) async = was_async;
var value = varNames && rng(2) ? createValue() : createExpression(recurmax, noComma, stmtDepth, canThrow);
async = save_async;
VAR_NAMES = current;
return value;
}
@@ -429,7 +436,7 @@ function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames,
}
if (i < m) {
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
var name = createVarName(maybe, dontStore);
var name = createVarName(MANDATORY);
unique_vars.length -= 6;
avoid.push(name);
unique_vars.push(name);
@@ -490,7 +497,7 @@ function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames,
break;
default:
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
var name = createVarName(maybe, dontStore);
var name = createVarName(MANDATORY);
unique_vars.length -= 6;
avoid.push(name);
unique_vars.push(name);
@@ -572,12 +579,18 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
}
}
function makeFunction(name) {
return (async ? "async function " : "function ") + name;
}
function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
if (--recurmax < 0) { return ";"; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
var s = [];
var name, args;
var varNames = VAR_NAMES.slice();
var save_async = async;
async = rng(50) == 0;
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
if (allowDefun || rng(5) > 0) {
name = "f" + funcs++;
@@ -589,13 +602,13 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
var params;
if ((!allowDefun || !(name in called)) && rng(2)) {
called[name] = false;
var pairs = createAssignmentPairs(recurmax, COMMA_OK, stmtDepth, canThrow, varNames, MANDATORY);
var pairs = createAssignmentPairs(recurmax, COMMA_OK, stmtDepth, canThrow, varNames, save_async);
params = pairs.names.join(", ");
args = pairs.values.join(", ");
} else {
params = createParams();
}
s.push("function " + name + "(" + params + "){", strictMode());
s.push(makeFunction(name) + "(" + params + "){", strictMode());
s.push(defns());
if (rng(5) === 0) {
// functions with functions. lower the recursion to prevent a mess.
@@ -607,6 +620,7 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
s.push("}", "");
s = filterDirective(s).join("\n");
});
async = save_async;
VAR_NAMES = varNames;
if (!allowDefun) {
@@ -733,7 +747,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
case STMT_VAR:
if (!rng(20)) {
var pairs = createAssignmentPairs(recurmax, NO_COMMA, stmtDepth, canThrow, null, MANDATORY);
var pairs = createAssignmentPairs(recurmax, NO_COMMA, stmtDepth, canThrow);
return "var " + pairs.names.map(function(name, index) {
return index in pairs.values ? name + " = " + pairs.values[index] : name;
}).join(", ") + ";";
@@ -853,7 +867,8 @@ function createExpression(recurmax, noComma, stmtDepth, canThrow) {
case 2:
return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented
default:
return "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")";
var expr = "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")";
return async && rng(50) == 0 ? "(await" + expr + ")" : expr;
}
}
@@ -909,6 +924,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
case p++:
var nameLenBefore = VAR_NAMES.length;
var save_async = async;
async = rng(50) == 0;
unique_vars.push("c");
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
unique_vars.pop();
@@ -916,7 +933,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
switch (rng(5)) {
case 0:
s.push(
"(function " + name + "(){",
"(" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
rng(2) == 0 ? "})" : "})()"
@@ -924,7 +941,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
break;
case 1:
s.push(
"+function " + name + "(){",
"+" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()"
@@ -932,7 +949,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
break;
case 2:
s.push(
"!function " + name + "(){",
"!" + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()"
@@ -940,13 +957,14 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
break;
case 3:
s.push(
"void function " + name + "(){",
"void " + makeFunction(name) + "(){",
strictMode(),
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
"}()"
);
break;
default:
async = false;
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
var instantiate = rng(4) ? "new " : "";
s.push(
@@ -965,6 +983,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
});
break;
}
async = save_async;
VAR_NAMES.length = nameLenBefore;
return filterDirective(s).join("\n");
case p++:
@@ -1361,7 +1380,7 @@ function createVarName(maybe, dontStore) {
do {
name = VAR_NAMES[rng(VAR_NAMES.length)];
if (suffix) name += "_" + suffix;
} while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0);
} while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0 || async && name == "await");
if (suffix && !dontStore) VAR_NAMES.push(name);
return name;
}