Fix class member initializer reparsing logic

Intertwined static / public member initializers can mix up ids, so unmix them.

Bug: 458914193
Change-Id: If0708b56750a92e03eaa5530cfbff295c2acf630
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7203465
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Auto-Submit: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/main@{#103958}
diff --git a/src/ast/scopes.cc b/src/ast/scopes.cc
index c0438fe..b476498 100644
--- a/src/ast/scopes.cc
+++ b/src/ast/scopes.cc
@@ -1448,7 +1448,7 @@
   // Functions which force eager compilation and class member initializer
   // functions are not lazily compilable.
   return !force_eager_compilation_ &&
-         !IsClassMembersInitializerFunction(function_kind());
+         !IsClassInitializerFunction(function_kind());
 }
 
 int Scope::ContextChainLength(Scope* scope) const {
diff --git a/src/ast/scopes.h b/src/ast/scopes.h
index 4b0a708..2698b64 100644
--- a/src/ast/scopes.h
+++ b/src/ast/scopes.h
@@ -996,7 +996,7 @@
 #endif  // V8_ENABLE_WEBASSEMBLY
 
   bool should_ban_arguments() const {
-    return IsClassMembersInitializerFunction(function_kind());
+    return IsClassInitializerFunction(function_kind());
   }
 
   void set_module_has_toplevel_await() {
diff --git a/src/objects/call-site-info.cc b/src/objects/call-site-info.cc
index eb9137c..f0a390c 100644
--- a/src/objects/call-site-info.cc
+++ b/src/objects/call-site-info.cc
@@ -467,7 +467,7 @@
   DirectHandle<JSFunction> function(Cast<JSFunction>(info->function()),
                                     isolate);
   // Class members initializer function is not a method.
-  if (IsClassMembersInitializerFunction(function->shared()->kind())) {
+  if (IsClassInitializerFunction(function->shared()->kind())) {
     return isolate->factory()->null_value();
   }
 
diff --git a/src/objects/function-kind.h b/src/objects/function-kind.h
index 9a1a64f..148109a 100644
--- a/src/objects/function-kind.h
+++ b/src/objects/function-kind.h
@@ -58,11 +58,13 @@
   kConciseMethod,
   kStaticConciseMethod,
   kClassMembersInitializerFunction,
+  kClassMembersInitializerFunctionPrecededByStatic,
   kClassStaticInitializerFunction,
+  kClassStaticInitializerFunctionPrecededByMember,
   // END concise methods 2
   kInvalid,
 
-  kLastFunctionKind = kClassStaticInitializerFunction,
+  kLastFunctionKind = kClassStaticInitializerFunctionPrecededByMember,
 };
 
 constexpr int kFunctionKindBitSize = 5;
@@ -105,8 +107,9 @@
 inline bool IsConciseMethod(FunctionKind kind) {
   return base::IsInRange(kind, FunctionKind::kAsyncConciseMethod,
                          FunctionKind::kStaticAsyncConciseGeneratorMethod) ||
-         base::IsInRange(kind, FunctionKind::kConciseGeneratorMethod,
-                         FunctionKind::kClassStaticInitializerFunction);
+         base::IsInRange(
+             kind, FunctionKind::kConciseGeneratorMethod,
+             FunctionKind::kClassStaticInitializerFunctionPrecededByMember);
 }
 
 inline bool IsStrictFunctionWithoutPrototype(FunctionKind kind) {
@@ -114,8 +117,9 @@
                          FunctionKind::kAsyncArrowFunction) ||
          base::IsInRange(kind, FunctionKind::kAsyncConciseMethod,
                          FunctionKind::kStaticAsyncConciseGeneratorMethod) ||
-         base::IsInRange(kind, FunctionKind::kConciseGeneratorMethod,
-                         FunctionKind::kClassStaticInitializerFunction);
+         base::IsInRange(
+             kind, FunctionKind::kConciseGeneratorMethod,
+             FunctionKind::kClassStaticInitializerFunctionPrecededByMember);
 }
 
 inline bool IsGetterFunction(FunctionKind kind) {
@@ -153,9 +157,22 @@
                          FunctionKind::kDerivedConstructor);
 }
 
-inline bool IsClassMembersInitializerFunction(FunctionKind kind) {
-  return base::IsInRange(kind, FunctionKind::kClassMembersInitializerFunction,
-                         FunctionKind::kClassStaticInitializerFunction);
+inline bool IsClassInitializerFunction(FunctionKind kind) {
+  return base::IsInRange(
+      kind, FunctionKind::kClassMembersInitializerFunction,
+      FunctionKind::kClassStaticInitializerFunctionPrecededByMember);
+}
+
+inline bool IsClassInstanceInitializerFunction(FunctionKind kind) {
+  return base::IsInRange(
+      kind, FunctionKind::kClassMembersInitializerFunction,
+      FunctionKind::kClassMembersInitializerFunctionPrecededByStatic);
+}
+
+inline bool IsClassStaticInitializerFunction(FunctionKind kind) {
+  return base::IsInRange(
+      kind, FunctionKind::kClassStaticInitializerFunction,
+      FunctionKind::kClassStaticInitializerFunctionPrecededByMember);
 }
 
 inline bool IsConstructable(FunctionKind kind) {
@@ -172,6 +189,7 @@
     case FunctionKind::kStaticAsyncConciseMethod:
     case FunctionKind::kStaticAsyncConciseGeneratorMethod:
     case FunctionKind::kClassStaticInitializerFunction:
+    case FunctionKind::kClassStaticInitializerFunctionPrecededByMember:
       return true;
     default:
       return false;
@@ -217,6 +235,10 @@
       return "ClassMembersInitializerFunction";
     case FunctionKind::kClassStaticInitializerFunction:
       return "ClassStaticInitializerFunction";
+    case FunctionKind::kClassMembersInitializerFunctionPrecededByStatic:
+      return "ClassMembersInitializerFunctionPrecededByStatic";
+    case FunctionKind::kClassStaticInitializerFunctionPrecededByMember:
+      return "ClassStaticInitializerFunctionPrecededByMember";
     case FunctionKind::kDefaultBaseConstructor:
       return "DefaultBaseConstructor";
     case FunctionKind::kDefaultDerivedConstructor:
diff --git a/src/objects/shared-function-info.cc b/src/objects/shared-function-info.cc
index e8877a4..748cde9 100644
--- a/src/objects/shared-function-info.cc
+++ b/src/objects/shared-function-info.cc
@@ -344,8 +344,8 @@
   }
 #endif  // V8_ENABLE_WEBASSEMBLY
   FunctionKind function_kind = shared->kind();
-  if (IsClassMembersInitializerFunction(function_kind)) {
-    return function_kind == FunctionKind::kClassMembersInitializerFunction
+  if (IsClassInitializerFunction(function_kind)) {
+    return IsClassInstanceInitializerFunction(function_kind)
                ? isolate->factory()->instance_members_initializer_string()
                : isolate->factory()->static_initializer_string();
   }
diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h
index dddf315..bf412c9 100644
--- a/src/parsing/parser-base.h
+++ b/src/parsing/parser-base.h
@@ -310,7 +310,10 @@
 
   void SkipInfos(int delta) { info_id_ += delta; }
 
-  void ResetInfoId() { info_id_ = 0; }
+  void ResetInfoId(int id = 0) {
+    DCHECK_LE(0, id);
+    info_id_ = id;
+  }
 
   // The Zone where the parsing outputs are stored.
   Zone* main_zone() const { return ast_value_factory()->single_parse_zone(); }
@@ -628,8 +631,11 @@
     DeclarationScope* EnsureStaticElementsScope(ParserBase* parser, int beg_pos,
                                                 int info_id) {
       if (!has_static_elements()) {
-        static_elements_scope = parser->NewFunctionScope(
-            FunctionKind::kClassStaticInitializerFunction);
+        FunctionKind kind =
+            has_instance_members()
+                ? FunctionKind::kClassStaticInitializerFunctionPrecededByMember
+                : FunctionKind::kClassStaticInitializerFunction;
+        static_elements_scope = parser->NewFunctionScope(kind);
         static_elements_scope->SetLanguageMode(LanguageMode::kStrict);
         static_elements_scope->set_start_position(beg_pos);
         static_elements_function_id = info_id;
@@ -643,8 +649,11 @@
     DeclarationScope* EnsureInstanceMembersScope(ParserBase* parser,
                                                  int beg_pos, int info_id) {
       if (!has_instance_members()) {
-        instance_members_scope = parser->NewFunctionScope(
-            FunctionKind::kClassMembersInitializerFunction);
+        FunctionKind kind =
+            has_static_elements()
+                ? FunctionKind::kClassMembersInitializerFunctionPrecededByStatic
+                : FunctionKind::kClassMembersInitializerFunction;
+        instance_members_scope = parser->NewFunctionScope(kind);
         instance_members_scope->SetLanguageMode(LanguageMode::kStrict);
         instance_members_scope->set_start_position(beg_pos);
         instance_members_function_id = info_id;
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc
index 1ee48a3..d6cd244 100644
--- a/src/parsing/parser.cc
+++ b/src/parsing/parser.cc
@@ -1063,7 +1063,7 @@
 
   FunctionKind function_kind = flags().function_kind();
   FunctionLiteral* result;
-  if (V8_UNLIKELY(IsClassMembersInitializerFunction(function_kind))) {
+  if (V8_UNLIKELY(IsClassInitializerFunction(function_kind))) {
     // Reparsing of class member initializer functions has to be handled
     // specially because they require reparsing of the whole class body,
     // function start/end positions correspond to the class literal body
@@ -1118,9 +1118,7 @@
   DCHECK(ast_value_factory());
   fni_.PushEnclosingName(raw_name);
 
-  ResetInfoId();
-  DCHECK_LT(0, function_literal_id);
-  SkipInfos(function_literal_id - 1);
+  ResetInfoId(function_literal_id - 1);
 
   ScopedModification<Mode> mode_scope(&mode_, PARSE_EAGERLY);
 
@@ -1256,10 +1254,6 @@
   DCHECK_NOT_NULL(nearest_decl_scope);
   FunctionState function_state(&function_state_, &scope_, nearest_decl_scope);
 
-  // We will reindex the function literals later.
-  ResetInfoId();
-  SkipInfos(initializer_id - 1);
-
   // We preparse the class members that are not fields with initializers
   // in order to collect the function literal ids.
   ScopedModification<Mode> mode_scope(&mode_, PARSE_LAZILY);
@@ -1304,9 +1298,43 @@
     scope()->MarkReparsingForClassInitializer();
 #endif
 
+    // Class members for instance and static fields can be intertwined. For
+    // example, in `class { a; static {}; b; static {} }` (where `a` and `b` are
+    // instance fields, and `static {}` are static initialization blocks), the
+    // initial parse might assign function literal IDs as follows:
+    //   - Instance members initializer (for `a` and `b`): ID 1
+    //   - Static members initializer (for `static {}` blocks): ID 2
+    //
+    // When we reparse a specific initializer (e.g., the static members
+    // initializer, ID 2), we must ensure that the scope for the "other" kind of
+    // initializer (e.g., the instance members initializer, ID 1) is
+    // pre-allocated if it lexically precedes the current one.
+    //
+    // If the instance scope (ID 1) is not pre-allocated:
+    // 1. The parser processes the first `static {}` block. It allocates the
+    //    static scope with the correct ID (ID 2), as that is the next available
+    //    ID after skipping.
+    // 2. The parser continues and encounters `b`. Since no instance scope
+    //    exists yet, it lazily allocates one.
+    // 3. This new instance scope takes the *next* available ID, which is ID 3.
+    //    However, ID 3 might not exist (overrunning the range) or might belong
+    //    to a subsequent function, causing a shift/mismatch.
+    //
+    // Pre-allocating the preceding scope (ID 1) ensures it exists before the
+    // parser encounters `b`, preventing the incorrect allocation of a new ID.
+
+    if (initializer_kind ==
+        FunctionKind::kClassMembersInitializerFunctionPrecededByStatic) {
+      class_info.EnsureStaticElementsScope(this, kNoSourcePosition, -1);
+    } else if (initializer_kind ==
+               FunctionKind::kClassStaticInitializerFunctionPrecededByMember) {
+      class_info.EnsureInstanceMembersScope(this, kNoSourcePosition, -1);
+    }
+    ResetInfoId(initializer_id - 1);
+
     ParseClassLiteralBody(class_info, class_name, class_token_pos, Token::kEos);
 
-    if (initializer_kind == FunctionKind::kClassMembersInitializerFunction) {
+    if (IsClassInstanceInitializerFunction(initializer_kind)) {
       DCHECK_EQ(class_info.instance_members_function_id, initializer_id);
       initializer = CreateInstanceMembersInitializer(class_name, &class_info);
     } else {
@@ -1318,7 +1346,7 @@
 
   if (has_error()) return nullptr;
 
-  DCHECK(IsClassMembersInitializerFunction(initializer_kind));
+  DCHECK(IsClassInitializerFunction(initializer_kind));
 
   no_expression_scope.ValidateExpression();
 
@@ -3348,7 +3376,7 @@
 FunctionLiteral* Parser::CreateInitializerFunction(
     const AstRawString* class_name, DeclarationScope* scope,
     int function_literal_id, Statement* initializer_stmt) {
-  DCHECK(IsClassMembersInitializerFunction(scope->function_kind()));
+  DCHECK(IsClassInitializerFunction(scope->function_kind()));
   // function() { .. class fields initializer .. }
   ScopedPtrList<Statement> statements(pointer_buffer());
   statements.Add(initializer_stmt);
diff --git a/src/tracing/code-data-source.cc b/src/tracing/code-data-source.cc
index 7d4f0dd..177a8f8 100644
--- a/src/tracing/code-data-source.cc
+++ b/src/tracing/code-data-source.cc
@@ -108,8 +108,10 @@
     case FunctionKind::kStaticConciseMethod:
       return InternedV8JsFunction::KIND_STATIC_CONCISE_METHOD;
     case FunctionKind::kClassMembersInitializerFunction:
+    case FunctionKind::kClassMembersInitializerFunctionPrecededByStatic:
       return InternedV8JsFunction::KIND_CLASS_MEMBERS_INITIALIZER_FUNCTION;
     case FunctionKind::kClassStaticInitializerFunction:
+    case FunctionKind::kClassStaticInitializerFunctionPrecededByMember:
       return InternedV8JsFunction::KIND_CLASS_STATIC_INITIALIZER_FUNCTION;
     case FunctionKind::kInvalid:
       return InternedV8JsFunction::KIND_INVALID;
diff --git a/test/unittests/objects/object-unittest.cc b/test/unittests/objects/object-unittest.cc
index e3c7d92..18032ea 100644
--- a/test/unittests/objects/object-unittest.cc
+++ b/test/unittests/objects/object-unittest.cc
@@ -575,6 +575,9 @@
     case FunctionKind::kAsyncConciseGeneratorMethod:
     case FunctionKind::kStaticAsyncConciseGeneratorMethod:
     case FunctionKind::kClassMembersInitializerFunction:
+    case FunctionKind::kClassMembersInitializerFunctionPrecededByStatic:
+    case FunctionKind::kClassStaticInitializerFunction:
+    case FunctionKind::kClassStaticInitializerFunctionPrecededByMember:
       return true;
     default:
       return false;
@@ -661,6 +664,9 @@
     case FunctionKind::kConciseMethod:
     case FunctionKind::kStaticConciseMethod:
     case FunctionKind::kClassMembersInitializerFunction:
+    case FunctionKind::kClassMembersInitializerFunctionPrecededByStatic:
+    case FunctionKind::kClassStaticInitializerFunction:
+    case FunctionKind::kClassStaticInitializerFunctionPrecededByMember:
       return false;
     default:
       return true;