Skip to content

Commit

Permalink
Change approach to field init scope tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
marijnh committed Dec 29, 2024
1 parent 9e365f7 commit d880a29
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 22 deletions.
4 changes: 2 additions & 2 deletions acorn/src/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {types as tokenCtxTypes} from "./tokencontext.js"
import {Parser} from "./state.js"
import {DestructuringErrors} from "./parseutil.js"
import {lineBreak} from "./whitespace.js"
import {functionFlags, SCOPE_ARROW, SCOPE_SUPER, SCOPE_DIRECT_SUPER, BIND_OUTSIDE, BIND_VAR} from "./scopeflags.js"
import {functionFlags, SCOPE_ARROW, SCOPE_SUPER, SCOPE_DIRECT_SUPER, BIND_OUTSIDE, BIND_VAR, SCOPE_VAR} from "./scopeflags.js"

const pp = Parser.prototype

Expand Down Expand Up @@ -1043,7 +1043,7 @@ pp.checkUnreserved = function({start, end, name}) {
this.raiseRecoverable(start, "Cannot use 'yield' as identifier inside a generator")
if (this.inAsync && name === "await")
this.raiseRecoverable(start, "Cannot use 'await' as identifier inside an async function")
if (this.currentScope().inClassFieldInit && name === "arguments")
if (!(this.currentThisScope().flags & SCOPE_VAR) && name === "arguments")
this.raiseRecoverable(start, "Cannot use 'arguments' in class field initializer")
if (this.inClassStaticBlock && (name === "arguments" || name === "await"))
this.raise(start, `Cannot use ${name} in class static initialization block`)
Expand Down
10 changes: 5 additions & 5 deletions acorn/src/scope.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Parser} from "./state.js"
import {SCOPE_VAR, SCOPE_FUNCTION, SCOPE_TOP, SCOPE_ARROW, SCOPE_SIMPLE_CATCH, BIND_LEXICAL, BIND_SIMPLE_CATCH, BIND_FUNCTION} from "./scopeflags.js"
import {SCOPE_VAR, SCOPE_FUNCTION, SCOPE_TOP, SCOPE_ARROW, SCOPE_SIMPLE_CATCH, BIND_LEXICAL,

Check failure on line 2 in acorn/src/scope.js

View workflow job for this annotation

GitHub Actions / Build and test

Expected a line break after this opening brace
BIND_SIMPLE_CATCH, BIND_FUNCTION, SCOPE_CLASS_FIELD_INIT, SCOPE_CLASS_STATIC_BLOCK} from "./scopeflags.js"

Check failure on line 3 in acorn/src/scope.js

View workflow job for this annotation

GitHub Actions / Build and test

Expected indentation of 2 spaces but found 8

Check failure on line 3 in acorn/src/scope.js

View workflow job for this annotation

GitHub Actions / Build and test

Expected a line break before this closing brace

const pp = Parser.prototype

Expand All @@ -12,8 +13,6 @@ class Scope {
this.lexical = []
// A list of lexically-declared FunctionDeclaration names in the current lexical scope
this.functions = []
// A switch to disallow the identifier reference 'arguments'
this.inClassFieldInit = false
}
}

Expand Down Expand Up @@ -84,14 +83,15 @@ pp.currentScope = function() {
pp.currentVarScope = function() {
for (let i = this.scopeStack.length - 1;; i--) {
let scope = this.scopeStack[i]
if (scope.flags & SCOPE_VAR) return scope
if (scope.flags & (SCOPE_VAR | SCOPE_CLASS_FIELD_INIT | SCOPE_CLASS_STATIC_BLOCK)) return scope
}
}

// Could be useful for `this`, `new.target`, `super()`, `super.property`, and `super[property]`.
pp.currentThisScope = function() {
for (let i = this.scopeStack.length - 1;; i--) {
let scope = this.scopeStack[i]
if (scope.flags & SCOPE_VAR && !(scope.flags & SCOPE_ARROW)) return scope
if (scope.flags & (SCOPE_VAR | SCOPE_CLASS_FIELD_INIT | SCOPE_CLASS_STATIC_BLOCK) &&
!(scope.flags & SCOPE_ARROW)) return scope
}
}
1 change: 1 addition & 0 deletions acorn/src/scopeflags.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const
SCOPE_SUPER = 64,
SCOPE_DIRECT_SUPER = 128,
SCOPE_CLASS_STATIC_BLOCK = 256,
SCOPE_CLASS_FIELD_INIT = 512,
SCOPE_VAR = SCOPE_TOP | SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK

export function functionFlags(async, generator) {
Expand Down
25 changes: 15 additions & 10 deletions acorn/src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ 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, SCOPE_CLASS_STATIC_BLOCK} from "./scopeflags.js"
import {SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_SUPER, SCOPE_DIRECT_SUPER,

Check failure on line 6 in acorn/src/state.js

View workflow job for this annotation

GitHub Actions / Build and test

Expected a line break after this opening brace
SCOPE_VAR, SCOPE_ARROW, SCOPE_CLASS_STATIC_BLOCK, SCOPE_CLASS_FIELD_INIT} from "./scopeflags.js"

Check failure on line 7 in acorn/src/state.js

View workflow job for this annotation

GitHub Actions / Build and test

Expected indentation of 2 spaces but found 8

Check failure on line 7 in acorn/src/state.js

View workflow job for this annotation

GitHub Actions / Build and test

'SCOPE_VAR' is defined but never used

Check failure on line 7 in acorn/src/state.js

View workflow job for this annotation

GitHub Actions / Build and test

Expected a line break before this closing brace

export class Parser {
constructor(options, input, startPos) {
Expand Down Expand Up @@ -99,31 +100,35 @@ export class Parser {

get inFunction() { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 }

get inGenerator() { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 && !this.currentScope().inClassFieldInit }
get inGenerator() { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 }

get inAsync() { return (this.currentScope().flags & SCOPE_ASYNC) > 0 && !this.currentScope().inClassFieldInit }
get inAsync() { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 }

get canAwait() {
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
let scope = this.scopeStack[i]
if (scope.flags & SCOPE_CLASS_STATIC_BLOCK || scope.inClassFieldInit) return false
if (scope.flags & SCOPE_FUNCTION) return (scope.flags & SCOPE_ASYNC) > 0
let {flags} = this.scopeStack[i]
if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT)) return false
if (flags & SCOPE_FUNCTION) return (flags & SCOPE_ASYNC) > 0
}
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
const {flags} = this.currentThisScope()
return (flags & SCOPE_SUPER) > 0 || this.options.allowSuperOutsideMethod
}

get allowDirectSuper() { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 }

get treatFunctionsAsVar() { return this.treatFunctionsAsVarInScope(this.currentScope()) }

get allowNewDotTarget() {
const {flags} = this.currentThisScope()
return (flags & (SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK)) > 0 || this.currentScope().inClassFieldInit
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
let {flags} = this.scopeStack[i]
if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT) ||
((flags & SCOPE_FUNCTION) && !(flags & SCOPE_ARROW))) return true
}
return false
}

get inClassStaticBlock() {
Expand Down
8 changes: 3 additions & 5 deletions acorn/src/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {lineBreak, skipWhiteSpace} from "./whitespace.js"
import {isIdentifierStart, isIdentifierChar, keywordRelationalOperator} from "./identifier.js"
import {hasOwn, loneSurrogate} from "./util.js"
import {DestructuringErrors} from "./parseutil.js"
import {functionFlags, SCOPE_SIMPLE_CATCH, BIND_SIMPLE_CATCH, BIND_LEXICAL, BIND_VAR, BIND_FUNCTION, SCOPE_CLASS_STATIC_BLOCK, SCOPE_SUPER} from "./scopeflags.js"
import {functionFlags, SCOPE_SIMPLE_CATCH, BIND_SIMPLE_CATCH, BIND_LEXICAL, BIND_VAR, BIND_FUNCTION, SCOPE_CLASS_STATIC_BLOCK, SCOPE_SUPER, SCOPE_CLASS_FIELD_INIT} from "./scopeflags.js"

const pp = Parser.prototype

Expand Down Expand Up @@ -742,11 +742,9 @@ pp.parseClassField = function(field) {

if (this.eat(tt.eq)) {
// To raise SyntaxError if 'arguments' exists in the initializer.
const scope = this.currentScope()
const inClassFieldInit = scope.inClassFieldInit
scope.inClassFieldInit = true
this.enterScope(SCOPE_CLASS_FIELD_INIT | SCOPE_SUPER)
field.value = this.parseMaybeAssign()
scope.inClassFieldInit = inClassFieldInit
this.exitScope()
} else {
field.value = null
}
Expand Down

0 comments on commit d880a29

Please sign in to comment.