Skip to content

Commit 41068c9

Browse files
committed
Make allowAwaitOutsideFunction more accurate
Fixes an issue where class field initializers could contain await expressions when the option was enabled, and makes the ecmaVersion >= 13 behavior only apply to module sources. Closes #1048
1 parent 0b943ea commit 41068c9

File tree

7 files changed

+25
-10
lines changed

7 files changed

+25
-10
lines changed

acorn-loose/src/expression.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ lp.parseExprOp = function(left, start, minPrec, noIn, indent, line) {
113113
lp.parseMaybeUnary = function(sawUnary) {
114114
let start = this.storeCurrentPos(), expr
115115
if (this.options.ecmaVersion >= 8 && this.toks.isContextual("await") &&
116-
(this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction))
117-
) {
116+
(this.inAsync || (this.toks.inModule && this.options.ecmaVersion >= 13) ||
117+
(!this.inFunction && this.options.allowAwaitOutsideFunction))) {
118118
expr = this.parseAwait()
119119
sawUnary = true
120120
} else if (this.tok.type.prefix) {

acorn/src/expression.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ pp.buildBinary = function(startPos, startLoc, left, right, op, logical) {
225225

226226
pp.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec) {
227227
let startPos = this.start, startLoc = this.startLoc, expr
228-
if (this.isContextual("await") && (this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction))) {
228+
if (this.isContextual("await") && this.canAwait) {
229229
expr = this.parseAwait()
230230
sawUnary = true
231231
} else if (this.type.prefix) {

acorn/src/options.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,6 @@ export function getOptions(opts) {
119119

120120
if (options.allowReserved == null)
121121
options.allowReserved = options.ecmaVersion < 5
122-
if (options.allowAwaitOutsideFunction == null)
123-
options.allowAwaitOutsideFunction = options.ecmaVersion >= 13
124122

125123
if (isArray(options.onToken)) {
126124
let tokens = options.onToken

acorn/src/scopeflags.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ export const
88
SCOPE_ARROW = 16,
99
SCOPE_SIMPLE_CATCH = 32,
1010
SCOPE_SUPER = 64,
11-
SCOPE_DIRECT_SUPER = 128
11+
SCOPE_DIRECT_SUPER = 128,
12+
SCOPE_CLASS = 256
1213

1314
export function functionFlags(async, generator) {
1415
return SCOPE_FUNCTION | (async ? SCOPE_ASYNC : 0) | (generator ? SCOPE_GENERATOR : 0)

acorn/src/state.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import {types as tt} from "./tokentype.js"
33
import {lineBreak} from "./whitespace.js"
44
import {getOptions} from "./options.js"
55
import {wordsRegexp} from "./util.js"
6-
import {SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_SUPER, SCOPE_DIRECT_SUPER} from "./scopeflags.js"
6+
import {
7+
SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_CLASS, SCOPE_GENERATOR,
8+
SCOPE_SUPER, SCOPE_DIRECT_SUPER
9+
} from "./scopeflags.js"
710

811
export class Parser {
912
constructor(options, input, startPos) {
@@ -100,6 +103,14 @@ export class Parser {
100103
get inFunction() { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 }
101104
get inGenerator() { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 && !this.currentVarScope().inClassFieldInit }
102105
get inAsync() { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 && !this.currentVarScope().inClassFieldInit }
106+
get canAwait() {
107+
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
108+
let {flags} = this.scopeStack[i]
109+
if (flags & SCOPE_FUNCTION) return (flags & SCOPE_ASYNC) > 0
110+
if (flags & SCOPE_CLASS) return false
111+
}
112+
return (this.inModule && this.options.ecmaVersion >= 13) || this.options.allowAwaitOutsideFunction
113+
}
103114
get allowSuper() {
104115
const {flags, inClassFieldInit} = this.currentThisScope()
105116
return (flags & SCOPE_SUPER) > 0 || inClassFieldInit || this.options.allowSuperOutsideMethod

acorn/src/statement.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {lineBreak, skipWhiteSpace} from "./whitespace.js"
44
import {isIdentifierStart, isIdentifierChar, keywordRelationalOperator} from "./identifier.js"
55
import {has} from "./util.js"
66
import {DestructuringErrors} from "./parseutil.js"
7-
import {functionFlags, SCOPE_SIMPLE_CATCH, BIND_SIMPLE_CATCH, BIND_LEXICAL, BIND_VAR, BIND_FUNCTION} from "./scopeflags.js"
7+
import {
8+
functionFlags, SCOPE_SIMPLE_CATCH, SCOPE_CLASS, BIND_SIMPLE_CATCH,
9+
BIND_LEXICAL, BIND_VAR, BIND_FUNCTION
10+
} from "./scopeflags.js"
811

912
const pp = Parser.prototype
1013

@@ -209,7 +212,7 @@ pp.parseDoStatement = function(node) {
209212

210213
pp.parseForStatement = function(node) {
211214
this.next()
212-
let awaitAt = (this.options.ecmaVersion >= 9 && (this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction)) && this.eatContextual("await")) ? this.lastTokStart : -1
215+
let awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual("await")) ? this.lastTokStart : -1
213216
this.labels.push(loopLabel)
214217
this.enterScope(0)
215218
this.expect(tt.parenL)
@@ -581,6 +584,7 @@ pp.parseClass = function(node, isStatement) {
581584
let hadConstructor = false
582585
classBody.body = []
583586
this.expect(tt.braceL)
587+
this.enterScope(SCOPE_CLASS)
584588
while (this.type !== tt.braceR) {
585589
const element = this.parseClassElement(node.superClass !== null)
586590
if (element) {
@@ -597,6 +601,7 @@ pp.parseClass = function(node, isStatement) {
597601
this.next()
598602
node.body = this.finishNode(classBody, "ClassBody")
599603
this.exitClassBody()
604+
this.exitScope()
600605
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
601606
}
602607

test/tests-await-top-level.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ test("await 1", {
5959
}
6060
}
6161
]
62-
}, {ecmaVersion: 13})
62+
}, {ecmaVersion: 13, sourceType: "module"})
6363
testFail("function foo() {return await 1}", "Unexpected token (1:29)", {ecmaVersion: 13})
6464
testFail("await 1", "Unexpected token (1:6)", {
6565
allowAwaitOutsideFunction: false,

0 commit comments

Comments
 (0)