Skip to content

Commit

Permalink
Make allowAwaitOutsideFunction more accurate
Browse files Browse the repository at this point in the history
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
  • Loading branch information
marijnh committed Jun 23, 2021
1 parent 0b943ea commit 41068c9
Show file tree
Hide file tree
Showing 7 changed files with 25 additions and 10 deletions.
4 changes: 2 additions & 2 deletions acorn-loose/src/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ lp.parseExprOp = function(left, start, minPrec, noIn, indent, line) {
lp.parseMaybeUnary = function(sawUnary) {
let start = this.storeCurrentPos(), expr
if (this.options.ecmaVersion >= 8 && this.toks.isContextual("await") &&
(this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction))
) {
(this.inAsync || (this.toks.inModule && this.options.ecmaVersion >= 13) ||
(!this.inFunction && this.options.allowAwaitOutsideFunction))) {
expr = this.parseAwait()
sawUnary = true
} else if (this.tok.type.prefix) {
Expand Down
2 changes: 1 addition & 1 deletion acorn/src/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ pp.buildBinary = function(startPos, startLoc, left, right, op, logical) {

pp.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec) {
let startPos = this.start, startLoc = this.startLoc, expr
if (this.isContextual("await") && (this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction))) {
if (this.isContextual("await") && this.canAwait) {
expr = this.parseAwait()
sawUnary = true
} else if (this.type.prefix) {
Expand Down
2 changes: 0 additions & 2 deletions acorn/src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ export function getOptions(opts) {

if (options.allowReserved == null)
options.allowReserved = options.ecmaVersion < 5
if (options.allowAwaitOutsideFunction == null)
options.allowAwaitOutsideFunction = options.ecmaVersion >= 13

if (isArray(options.onToken)) {
let tokens = options.onToken
Expand Down
3 changes: 2 additions & 1 deletion acorn/src/scopeflags.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export const
SCOPE_ARROW = 16,
SCOPE_SIMPLE_CATCH = 32,
SCOPE_SUPER = 64,
SCOPE_DIRECT_SUPER = 128
SCOPE_DIRECT_SUPER = 128,
SCOPE_CLASS = 256

export function functionFlags(async, generator) {
return SCOPE_FUNCTION | (async ? SCOPE_ASYNC : 0) | (generator ? SCOPE_GENERATOR : 0)
Expand Down
13 changes: 12 additions & 1 deletion acorn/src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import {types as tt} from "./tokentype.js"
import {lineBreak} from "./whitespace.js"
import {getOptions} from "./options.js"
import {wordsRegexp} from "./util.js"
import {SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_SUPER, SCOPE_DIRECT_SUPER} from "./scopeflags.js"
import {
SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_CLASS, SCOPE_GENERATOR,
SCOPE_SUPER, SCOPE_DIRECT_SUPER
} from "./scopeflags.js"

export class Parser {
constructor(options, input, startPos) {
Expand Down Expand Up @@ -100,6 +103,14 @@ export class Parser {
get inFunction() { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 }
get inGenerator() { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 && !this.currentVarScope().inClassFieldInit }
get inAsync() { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 && !this.currentVarScope().inClassFieldInit }
get canAwait() {
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
let {flags} = this.scopeStack[i]
if (flags & SCOPE_FUNCTION) return (flags & SCOPE_ASYNC) > 0
if (flags & SCOPE_CLASS) return false
}
return (this.inModule && this.options.ecmaVersion >= 13) || this.options.allowAwaitOutsideFunction
}
get allowSuper() {
const {flags, inClassFieldInit} = this.currentThisScope()
return (flags & SCOPE_SUPER) > 0 || inClassFieldInit || this.options.allowSuperOutsideMethod
Expand Down
9 changes: 7 additions & 2 deletions acorn/src/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import {lineBreak, skipWhiteSpace} from "./whitespace.js"
import {isIdentifierStart, isIdentifierChar, keywordRelationalOperator} from "./identifier.js"
import {has} from "./util.js"
import {DestructuringErrors} from "./parseutil.js"
import {functionFlags, SCOPE_SIMPLE_CATCH, BIND_SIMPLE_CATCH, BIND_LEXICAL, BIND_VAR, BIND_FUNCTION} from "./scopeflags.js"
import {
functionFlags, SCOPE_SIMPLE_CATCH, SCOPE_CLASS, BIND_SIMPLE_CATCH,
BIND_LEXICAL, BIND_VAR, BIND_FUNCTION
} from "./scopeflags.js"

const pp = Parser.prototype

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

pp.parseForStatement = function(node) {
this.next()
let awaitAt = (this.options.ecmaVersion >= 9 && (this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction)) && this.eatContextual("await")) ? this.lastTokStart : -1
let awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual("await")) ? this.lastTokStart : -1
this.labels.push(loopLabel)
this.enterScope(0)
this.expect(tt.parenL)
Expand Down Expand Up @@ -581,6 +584,7 @@ pp.parseClass = function(node, isStatement) {
let hadConstructor = false
classBody.body = []
this.expect(tt.braceL)
this.enterScope(SCOPE_CLASS)
while (this.type !== tt.braceR) {
const element = this.parseClassElement(node.superClass !== null)
if (element) {
Expand All @@ -597,6 +601,7 @@ pp.parseClass = function(node, isStatement) {
this.next()
node.body = this.finishNode(classBody, "ClassBody")
this.exitClassBody()
this.exitScope()
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
}

Expand Down
2 changes: 1 addition & 1 deletion test/tests-await-top-level.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ test("await 1", {
}
}
]
}, {ecmaVersion: 13})
}, {ecmaVersion: 13, sourceType: "module"})
testFail("function foo() {return await 1}", "Unexpected token (1:29)", {ecmaVersion: 13})
testFail("await 1", "Unexpected token (1:6)", {
allowAwaitOutsideFunction: false,
Expand Down

0 comments on commit 41068c9

Please sign in to comment.