Create parser plugin "topLevelAwait" (#10449)
* Create parser plugin "topLevelAwait" * Update test262 whitelist * Update ts typings * Fix "sourceType: unambiguous" with TLA * Ambiguous tokens after await * Update await %x(0) * typo [skip ci] * Typo [skip ci] Co-Authored-By: Brian Ng <bng412@gmail.com>
This commit is contained in:
@@ -25,15 +25,32 @@ export function parse(input: string, options?: Options): File {
|
||||
const parser = getParser(options, input);
|
||||
const ast = parser.parse();
|
||||
|
||||
// Rather than try to parse as a script first, we opt to parse as a module and convert back
|
||||
// to a script where possible to avoid having to do a full re-parse of the input content.
|
||||
if (!parser.sawUnambiguousESM) ast.program.sourceType = "script";
|
||||
if (parser.sawUnambiguousESM) {
|
||||
return ast;
|
||||
}
|
||||
|
||||
if (parser.ambiguousScriptDifferentAst) {
|
||||
// Top level await introduces code which can be both a valid script and
|
||||
// a valid module, but which produces different ASTs:
|
||||
// await
|
||||
// 0
|
||||
// can be parsed either as an AwaitExpression, or as two ExpressionStatements.
|
||||
try {
|
||||
options.sourceType = "script";
|
||||
return getParser(options, input).parse();
|
||||
} catch {}
|
||||
} else {
|
||||
// This is both a valid module and a valid script, but
|
||||
// we parse it as a script by default
|
||||
ast.program.sourceType = "script";
|
||||
}
|
||||
|
||||
return ast;
|
||||
} catch (moduleError) {
|
||||
try {
|
||||
options.sourceType = "script";
|
||||
return getParser(options, input).parse();
|
||||
} catch (scriptError) {}
|
||||
} catch {}
|
||||
|
||||
throw moduleError;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export default class BaseParser {
|
||||
plugins: PluginsMap;
|
||||
filename: ?string;
|
||||
sawUnambiguousESM: boolean = false;
|
||||
ambiguousScriptDifferentAst: boolean = false;
|
||||
|
||||
// Initialized by Tokenizer
|
||||
state: State;
|
||||
|
||||
@@ -2208,6 +2208,7 @@ export default class ExpressionParser extends LValParser {
|
||||
isAwaitAllowed(): boolean {
|
||||
if (this.scope.inFunction) return this.scope.inAsync;
|
||||
if (this.options.allowAwaitOutsideFunction) return true;
|
||||
if (this.hasPlugin("topLevelAwait")) return this.inModule;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2234,9 +2235,33 @@ export default class ExpressionParser extends LValParser {
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.scope.inFunction && !this.options.allowAwaitOutsideFunction) {
|
||||
if (
|
||||
this.hasPrecedingLineBreak() ||
|
||||
// All the following expressions are ambiguous:
|
||||
// await + 0, await - 0, await ( 0 ), await [ 0 ], await / 0 /u, await ``
|
||||
this.match(tt.plusMin) ||
|
||||
this.match(tt.parenL) ||
|
||||
this.match(tt.bracketL) ||
|
||||
this.match(tt.backQuote) ||
|
||||
// Sometimes the tokenizer generates tt.slash for regexps, and this is
|
||||
// handler by parseExprAtom
|
||||
this.match(tt.regexp) ||
|
||||
this.match(tt.slash) ||
|
||||
// This code could be parsed both as a modulo operator or as an intrinsic:
|
||||
// await %x(0)
|
||||
(this.hasPlugin("v8intrinsic") && this.match(tt.modulo))
|
||||
) {
|
||||
this.ambiguousScriptDifferentAst = true;
|
||||
} else {
|
||||
this.sawUnambiguousESM = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.state.soloAwait) {
|
||||
node.argument = this.parseMaybeUnary();
|
||||
}
|
||||
|
||||
return this.finishNode(node, "AwaitExpression");
|
||||
}
|
||||
|
||||
|
||||
@@ -495,11 +495,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
this.state.labels.push(loopLabel);
|
||||
|
||||
let awaitAt = -1;
|
||||
if (
|
||||
(this.scope.inAsync ||
|
||||
(!this.scope.inFunction && this.options.allowAwaitOutsideFunction)) &&
|
||||
this.eatContextual("await")
|
||||
) {
|
||||
if (this.isAwaitAllowed() && this.eatContextual("await")) {
|
||||
awaitAt = this.state.lastTokStart;
|
||||
}
|
||||
this.scope.enter(SCOPE_OTHER);
|
||||
|
||||
Reference in New Issue
Block a user