From 3221fa54f9dea30338228b97210c4f1fd332652d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 29 Dec 2024 22:07:39 +0100 Subject: [PATCH] Change approach to field init scope tracking --- acorn/src/expression.js | 4 ++-- acorn/src/scope.js | 12 +++++++----- acorn/src/scopeflags.js | 1 + acorn/src/state.js | 27 +++++++++++++++++---------- acorn/src/statement.js | 8 +++----- 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/acorn/src/expression.js b/acorn/src/expression.js index 9a38bfd8b..fb25a3ea5 100644 --- a/acorn/src/expression.js +++ b/acorn/src/expression.js @@ -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 @@ -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`) diff --git a/acorn/src/scope.js b/acorn/src/scope.js index 0f06609f2..b5e2e8ddb 100644 --- a/acorn/src/scope.js +++ b/acorn/src/scope.js @@ -1,5 +1,8 @@ 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, + BIND_SIMPLE_CATCH, BIND_FUNCTION, SCOPE_CLASS_FIELD_INIT, SCOPE_CLASS_STATIC_BLOCK +} from "./scopeflags.js" const pp = Parser.prototype @@ -12,8 +15,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 } } @@ -84,7 +85,7 @@ 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 } } @@ -92,6 +93,7 @@ pp.currentVarScope = function() { 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 } } diff --git a/acorn/src/scopeflags.js b/acorn/src/scopeflags.js index 4574e4497..982bff16e 100644 --- a/acorn/src/scopeflags.js +++ b/acorn/src/scopeflags.js @@ -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) { diff --git a/acorn/src/state.js b/acorn/src/state.js index fc5e21e46..ddb4e9299 100644 --- a/acorn/src/state.js +++ b/acorn/src/state.js @@ -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, SCOPE_CLASS_STATIC_BLOCK} from "./scopeflags.js" +import { + SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_SUPER, SCOPE_DIRECT_SUPER, + SCOPE_ARROW, SCOPE_CLASS_STATIC_BLOCK, SCOPE_CLASS_FIELD_INIT +} from "./scopeflags.js" export class Parser { constructor(options, input, startPos) { @@ -99,22 +102,22 @@ 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 } @@ -122,8 +125,12 @@ export class Parser { 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() { diff --git a/acorn/src/statement.js b/acorn/src/statement.js index a8cae83ae..2b37d0e5a 100644 --- a/acorn/src/statement.js +++ b/acorn/src/statement.js @@ -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 @@ -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 }