[class] parse static private methods and accessors

This patch uses a bit in the Variable bit fields to distinguish
static private names from instance private names, so that we
can check the conflicts of private accessors that are complementary
but with different staticness in the parser, and use this
information later when generating code for checking static brands
for private method access.

Design doc: https://docs.google.com/document/d/1rgGRw5RdzaRrM-GrIMhsn-DLULtADV2dmIdh_iIZxlc/edit

Bug: v8:8330
Change-Id: I8d70600e594e3d07f77ea519751b7ca2e0de87b5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1781010
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/master@{#63677}
diff --git a/src/ast/scopes.cc b/src/ast/scopes.cc
index b6b00c9..cf18a4e 100644
--- a/src/ast/scopes.cc
+++ b/src/ast/scopes.cc
@@ -40,7 +40,7 @@
                                VariableKind kind,
                                InitializationFlag initialization_flag,
                                MaybeAssignedFlag maybe_assigned_flag,
-                               bool* was_added) {
+                               IsStaticFlag is_static_flag, bool* was_added) {
   // AstRawStrings are unambiguous, i.e., the same string is always represented
   // by the same AstRawString*.
   // FIXME(marja): fix the type of Lookup.
@@ -51,8 +51,9 @@
   if (*was_added) {
     // The variable has not been declared yet -> insert it.
     DCHECK_EQ(name, p->key);
-    Variable* variable = new (zone) Variable(
-        scope, name, mode, kind, initialization_flag, maybe_assigned_flag);
+    Variable* variable =
+        new (zone) Variable(scope, name, mode, kind, initialization_flag,
+                            maybe_assigned_flag, is_static_flag);
     p->value = variable;
   }
   return reinterpret_cast<Variable*>(p->value);
@@ -797,11 +798,13 @@
   VariableMode mode;
   InitializationFlag init_flag;
   MaybeAssignedFlag maybe_assigned_flag;
+  IsStaticFlag is_static_flag;
 
   {
     location = VariableLocation::CONTEXT;
     index = ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode,
-                                        &init_flag, &maybe_assigned_flag);
+                                        &init_flag, &maybe_assigned_flag,
+                                        &is_static_flag);
     found = index >= 0;
   }
 
@@ -826,9 +829,9 @@
   }
 
   bool was_added;
-  Variable* var =
-      cache->variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
-                                init_flag, maybe_assigned_flag, &was_added);
+  Variable* var = cache->variables_.Declare(
+      zone(), this, name, mode, NORMAL_VARIABLE, init_flag, maybe_assigned_flag,
+      IsStaticFlag::kNotStatic, &was_added);
   DCHECK(was_added);
   var->AllocateTo(location, index);
   return var;
@@ -1057,7 +1060,7 @@
   bool was_added;
   return cache->variables_.Declare(
       zone(), this, name, VariableMode::kDynamicGlobal, kind,
-      kCreatedInitialized, kNotAssigned, &was_added);
+      kCreatedInitialized, kNotAssigned, IsStaticFlag::kNotStatic, &was_added);
   // TODO(neis): Mark variable as maybe-assigned?
 }
 
@@ -1784,9 +1787,9 @@
   // Declare a new non-local.
   DCHECK(IsDynamicVariableMode(mode));
   bool was_added;
-  Variable* var =
-      variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
-                         kCreatedInitialized, kNotAssigned, &was_added);
+  Variable* var = variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
+                                     kCreatedInitialized, kNotAssigned,
+                                     IsStaticFlag::kNotStatic, &was_added);
   // Allocate it by giving it a dynamic lookup.
   var->AllocateTo(VariableLocation::LOOKUP, -1);
   return var;
@@ -2407,14 +2410,17 @@
 }
 
 Variable* ClassScope::DeclarePrivateName(const AstRawString* name,
-                                         VariableMode mode, bool* was_added) {
+                                         VariableMode mode,
+                                         IsStaticFlag is_static_flag,
+                                         bool* was_added) {
   Variable* result = EnsureRareData()->private_name_map.Declare(
       zone(), this, name, mode, NORMAL_VARIABLE,
       InitializationFlag::kNeedsInitialization,
-      MaybeAssignedFlag::kMaybeAssigned, was_added);
+      MaybeAssignedFlag::kMaybeAssigned, is_static_flag, was_added);
   if (*was_added) {
     locals_.Add(result);
-  } else if (IsComplementaryAccessorPair(result->mode(), mode)) {
+  } else if (IsComplementaryAccessorPair(result->mode(), mode) &&
+             result->is_static_flag() == is_static_flag) {
     *was_added = true;
     result->set_mode(VariableMode::kPrivateGetterAndSetter);
   }
@@ -2493,8 +2499,10 @@
   VariableMode mode;
   InitializationFlag init_flag;
   MaybeAssignedFlag maybe_assigned_flag;
-  int index = ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode,
-                                          &init_flag, &maybe_assigned_flag);
+  IsStaticFlag is_static_flag;
+  int index =
+      ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode, &init_flag,
+                                  &maybe_assigned_flag, &is_static_flag);
   if (index < 0) {
     return nullptr;
   }
@@ -2506,7 +2514,7 @@
   // Add the found private name to the map to speed up subsequent
   // lookups for the same name.
   bool was_added;
-  Variable* var = DeclarePrivateName(name, mode, &was_added);
+  Variable* var = DeclarePrivateName(name, mode, is_static_flag, &was_added);
   DCHECK(was_added);
   var->AllocateTo(VariableLocation::CONTEXT, index);
   return var;
@@ -2615,6 +2623,7 @@
 }
 
 Variable* ClassScope::DeclareBrandVariable(AstValueFactory* ast_value_factory,
+                                           IsStaticFlag is_static_flag,
                                            int class_token_pos) {
   DCHECK_IMPLIES(GetRareData() != nullptr, GetRareData()->brand == nullptr);
   bool was_added;
@@ -2623,6 +2632,7 @@
                             InitializationFlag::kNeedsInitialization,
                             MaybeAssignedFlag::kMaybeAssigned, &was_added);
   DCHECK(was_added);
+  brand->set_is_static_flag(is_static_flag);
   brand->ForceContextAllocation();
   brand->set_is_used();
   EnsureRareData()->brand = brand;
diff --git a/src/ast/scopes.h b/src/ast/scopes.h
index ce098f0..3d1841a 100644
--- a/src/ast/scopes.h
+++ b/src/ast/scopes.h
@@ -44,7 +44,7 @@
                     VariableMode mode, VariableKind kind,
                     InitializationFlag initialization_flag,
                     MaybeAssignedFlag maybe_assigned_flag,
-                    bool* was_added);
+                    IsStaticFlag is_static_flag, bool* was_added);
 
   V8_EXPORT_PRIVATE Variable* Lookup(const AstRawString* name);
   void Remove(Variable* var);
@@ -556,9 +556,10 @@
   Variable* Declare(Zone* zone, const AstRawString* name, VariableMode mode,
                     VariableKind kind, InitializationFlag initialization_flag,
                     MaybeAssignedFlag maybe_assigned_flag, bool* was_added) {
-    Variable* result =
-        variables_.Declare(zone, this, name, mode, kind, initialization_flag,
-                           maybe_assigned_flag, was_added);
+    // Static variables can only be declared using ClassScope methods.
+    Variable* result = variables_.Declare(
+        zone, this, name, mode, kind, initialization_flag, maybe_assigned_flag,
+        IsStaticFlag::kNotStatic, was_added);
     if (*was_added) locals_.Add(result);
     return result;
   }
@@ -1256,7 +1257,7 @@
   // Declare a private name in the private name map and add it to the
   // local variables of this scope.
   Variable* DeclarePrivateName(const AstRawString* name, VariableMode mode,
-                               bool* was_added);
+                               IsStaticFlag is_static_flag, bool* was_added);
 
   // Try resolving all unresolved private names found in the current scope.
   // Called from DeclarationScope::AllocateVariables() when reparsing a
@@ -1287,6 +1288,7 @@
   void MigrateUnresolvedPrivateNameTail(AstNodeFactory* ast_node_factory,
                                         UnresolvedList::Iterator tail);
   Variable* DeclareBrandVariable(AstValueFactory* ast_value_factory,
+                                 IsStaticFlag is_static_flag,
                                  int class_token_pos);
   Variable* brand() {
     return GetRareData() == nullptr ? nullptr : GetRareData()->brand;
diff --git a/src/ast/variables.h b/src/ast/variables.h
index 1ff6f9f..05d73dd 100644
--- a/src/ast/variables.h
+++ b/src/ast/variables.h
@@ -21,7 +21,8 @@
  public:
   Variable(Scope* scope, const AstRawString* name, VariableMode mode,
            VariableKind kind, InitializationFlag initialization_flag,
-           MaybeAssignedFlag maybe_assigned_flag = kNotAssigned)
+           MaybeAssignedFlag maybe_assigned_flag = kNotAssigned,
+           IsStaticFlag is_static_flag = IsStaticFlag::kNotStatic)
       : scope_(scope),
         name_(name),
         local_if_not_shadowed_(nullptr),
@@ -35,10 +36,13 @@
                    ForceContextAllocationField::encode(false) |
                    ForceHoleInitializationField::encode(false) |
                    LocationField::encode(VariableLocation::UNALLOCATED) |
-                   VariableKindField::encode(kind)) {
+                   VariableKindField::encode(kind) |
+                   IsStaticFlagField::encode(is_static_flag)) {
     // Var declared variables never need initialization.
     DCHECK(!(mode == VariableMode::kVar &&
              initialization_flag == kNeedsInitialization));
+    DCHECK_IMPLIES(is_static_flag == IsStaticFlag::kStatic,
+                   IsConstVariableMode(mode));
   }
 
   explicit Variable(Variable* other);
@@ -59,6 +63,14 @@
   void set_mode(VariableMode mode) {
     bit_field_ = VariableModeField::update(bit_field_, mode);
   }
+  void set_is_static_flag(IsStaticFlag is_static_flag) {
+    bit_field_ = IsStaticFlagField::update(bit_field_, is_static_flag);
+  }
+  IsStaticFlag is_static_flag() const {
+    return IsStaticFlagField::decode(bit_field_);
+  }
+  bool is_static() const { return is_static_flag() == IsStaticFlag::kStatic; }
+
   bool has_forced_context_allocation() const {
     return ForceContextAllocationField::decode(bit_field_);
   }
@@ -249,6 +261,7 @@
   using ForceHoleInitializationField = InitializationFlagField::Next<bool, 1>;
   using MaybeAssignedFlagField =
       ForceHoleInitializationField::Next<MaybeAssignedFlag, 1>;
+  using IsStaticFlagField = MaybeAssignedFlagField::Next<IsStaticFlag, 1>;
 
   Variable** next() { return &next_; }
   friend List;
diff --git a/src/common/globals.h b/src/common/globals.h
index a0584b9..4d5a239 100644
--- a/src/common/globals.h
+++ b/src/common/globals.h
@@ -1216,6 +1216,10 @@
 // immediately initialized upon creation (kCreatedInitialized).
 enum InitializationFlag : uint8_t { kNeedsInitialization, kCreatedInitialized };
 
+// Static variables can only be used with the class in the closest
+// class scope as receivers.
+enum class IsStaticFlag : uint8_t { kNotStatic, kStatic };
+
 enum MaybeAssignedFlag : uint8_t { kNotAssigned, kMaybeAssigned };
 
 enum class InterpreterPushArgsMode : unsigned {
diff --git a/src/debug/debug-frames.cc b/src/debug/debug-frames.cc
index 78c4c32..49351f9 100644
--- a/src/debug/debug-frames.cc
+++ b/src/debug/debug-frames.cc
@@ -93,8 +93,10 @@
   VariableMode mode;
   InitializationFlag init_flag;
   MaybeAssignedFlag maybe_assigned_flag;
+  IsStaticFlag is_static_flag;
   return ScopeInfo::ContextSlotIndex(*info, *parameter_name, &mode, &init_flag,
-                                     &maybe_assigned_flag) != -1;
+                                     &maybe_assigned_flag,
+                                     &is_static_flag) != -1;
 }
 
 RedirectActiveFunctions::RedirectActiveFunctions(SharedFunctionInfo shared,
diff --git a/src/debug/debug-scopes.cc b/src/debug/debug-scopes.cc
index a560a47..512b85f 100644
--- a/src/debug/debug-scopes.cc
+++ b/src/debug/debug-scopes.cc
@@ -1006,9 +1006,10 @@
   VariableMode mode;
   InitializationFlag flag;
   MaybeAssignedFlag maybe_assigned_flag;
+  IsStaticFlag is_static_flag;
   int slot_index =
       ScopeInfo::ContextSlotIndex(context_->scope_info(), *variable_name, &mode,
-                                  &flag, &maybe_assigned_flag);
+                                  &flag, &maybe_assigned_flag, &is_static_flag);
   if (slot_index < 0) return false;
 
   context_->set(slot_index, *new_value);
diff --git a/src/debug/debug-stack-trace-iterator.cc b/src/debug/debug-stack-trace-iterator.cc
index bbe34c4..12f492d 100644
--- a/src/debug/debug-stack-trace-iterator.cc
+++ b/src/debug/debug-stack-trace-iterator.cc
@@ -97,9 +97,10 @@
     VariableMode mode;
     InitializationFlag flag;
     MaybeAssignedFlag maybe_assigned_flag;
+    IsStaticFlag is_static_flag;
     int slot_index = ScopeInfo::ContextSlotIndex(
         context->scope_info(), ReadOnlyRoots(isolate_->heap()).this_string(),
-        &mode, &flag, &maybe_assigned_flag);
+        &mode, &flag, &maybe_assigned_flag, &is_static_flag);
     if (slot_index < 0) return v8::MaybeLocal<v8::Value>();
     Handle<Object> value = handle(context->get(slot_index), isolate_);
     if (value->IsTheHole(isolate_)) return v8::MaybeLocal<v8::Value>();
diff --git a/src/objects/contexts.cc b/src/objects/contexts.cc
index d50cfe9..aee664e 100644
--- a/src/objects/contexts.cc
+++ b/src/objects/contexts.cc
@@ -39,12 +39,14 @@
 bool ScriptContextTable::Lookup(Isolate* isolate, ScriptContextTable table,
                                 String name, LookupResult* result) {
   DisallowHeapAllocation no_gc;
+  // Static variables cannot be in script contexts.
+  IsStaticFlag is_static_flag;
   for (int i = 0; i < table.used(); i++) {
     Context context = table.get_context(i);
     DCHECK(context.IsScriptContext());
     int slot_index = ScopeInfo::ContextSlotIndex(
         context.scope_info(), name, &result->mode, &result->init_flag,
-        &result->maybe_assigned_flag);
+        &result->maybe_assigned_flag, &is_static_flag);
 
     if (slot_index >= 0) {
       result->context_index = i;
@@ -286,8 +288,10 @@
       VariableMode mode;
       InitializationFlag flag;
       MaybeAssignedFlag maybe_assigned_flag;
-      int slot_index = ScopeInfo::ContextSlotIndex(scope_info, *name, &mode,
-                                                   &flag, &maybe_assigned_flag);
+      IsStaticFlag is_static_flag;
+      int slot_index =
+          ScopeInfo::ContextSlotIndex(scope_info, *name, &mode, &flag,
+                                      &maybe_assigned_flag, &is_static_flag);
       DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
       if (slot_index >= 0) {
         if (FLAG_trace_contexts) {
diff --git a/src/objects/scope-info.cc b/src/objects/scope-info.cc
index c0a38f5..0f4dc26 100644
--- a/src/objects/scope-info.cc
+++ b/src/objects/scope-info.cc
@@ -223,7 +223,8 @@
               VariableModeField::encode(var->mode()) |
               InitFlagField::encode(var->initialization_flag()) |
               MaybeAssignedFlagField::encode(var->maybe_assigned()) |
-              ParameterNumberField::encode(ParameterNumberField::kMax);
+              ParameterNumberField::encode(ParameterNumberField::kMax) |
+              IsStaticFlagField::encode(var->is_static_flag());
           scope_info.set(context_local_base + local_index, *var->name(), mode);
           scope_info.set(context_local_info_base + local_index,
                          Smi::FromInt(info));
@@ -238,7 +239,8 @@
               VariableModeField::encode(var->mode()) |
               InitFlagField::encode(var->initialization_flag()) |
               MaybeAssignedFlagField::encode(var->maybe_assigned()) |
-              ParameterNumberField::encode(ParameterNumberField::kMax);
+              ParameterNumberField::encode(ParameterNumberField::kMax) |
+              IsStaticFlagField::encode(var->is_static_flag());
           scope_info.set(module_var_entry + kModuleVariablePropertiesOffset,
                          Smi::FromInt(properties));
           module_var_entry += kModuleVariableEntryLength;
@@ -276,7 +278,8 @@
               VariableModeField::encode(var->mode()) |
               InitFlagField::encode(var->initialization_flag()) |
               MaybeAssignedFlagField::encode(var->maybe_assigned()) |
-              ParameterNumberField::encode(ParameterNumberField::kMax);
+              ParameterNumberField::encode(ParameterNumberField::kMax) |
+              IsStaticFlagField::encode(var->is_static_flag());
           scope_info.set(context_local_base + local_index, *var->name(), mode);
           scope_info.set(context_local_info_base + local_index,
                          Smi::FromInt(info));
@@ -456,7 +459,8 @@
         VariableModeField::encode(VariableMode::kConst) |
         InitFlagField::encode(kCreatedInitialized) |
         MaybeAssignedFlagField::encode(kNotAssigned) |
-        ParameterNumberField::encode(ParameterNumberField::kMax);
+        ParameterNumberField::encode(ParameterNumberField::kMax) |
+        IsStaticFlagField::encode(IsStaticFlag::kNotStatic);
     scope_info->set(index++, Smi::FromInt(value));
   }
 
@@ -686,6 +690,14 @@
   return VariableModeField::decode(value);
 }
 
+IsStaticFlag ScopeInfo::ContextLocalIsStaticFlag(int var) const {
+  DCHECK_LE(0, var);
+  DCHECK_LT(var, ContextLocalCount());
+  int info_index = ContextLocalInfosIndex() + var;
+  int value = Smi::ToInt(get(info_index));
+  return IsStaticFlagField::decode(value);
+}
+
 InitializationFlag ScopeInfo::ContextLocalInitFlag(int var) const {
   DCHECK_LE(0, var);
   DCHECK_LT(var, ContextLocalCount());
@@ -756,7 +768,8 @@
 int ScopeInfo::ContextSlotIndex(ScopeInfo scope_info, String name,
                                 VariableMode* mode,
                                 InitializationFlag* init_flag,
-                                MaybeAssignedFlag* maybe_assigned_flag) {
+                                MaybeAssignedFlag* maybe_assigned_flag,
+                                IsStaticFlag* is_static_flag) {
   DisallowHeapAllocation no_gc;
   DCHECK(name.IsInternalizedString());
   DCHECK_NOT_NULL(mode);
@@ -771,6 +784,7 @@
     if (name != scope_info.get(i)) continue;
     int var = i - start;
     *mode = scope_info.ContextLocalMode(var);
+    *is_static_flag = scope_info.ContextLocalIsStaticFlag(var);
     *init_flag = scope_info.ContextLocalInitFlag(var);
     *maybe_assigned_flag = scope_info.ContextLocalMaybeAssignedFlag(var);
     int result = Context::MIN_CONTEXT_SLOTS + var;
diff --git a/src/objects/scope-info.h b/src/objects/scope-info.h
index ef7b239..a8f483a 100644
--- a/src/objects/scope-info.h
+++ b/src/objects/scope-info.h
@@ -121,6 +121,9 @@
   // Return the mode of the given context local.
   VariableMode ContextLocalMode(int var) const;
 
+  // Return whether the given context local variable is static.
+  IsStaticFlag ContextLocalIsStaticFlag(int var) const;
+
   // Return the initialization flag of the given context local.
   InitializationFlag ContextLocalInitFlag(int var) const;
 
@@ -141,7 +144,8 @@
   // mode for that variable.
   static int ContextSlotIndex(ScopeInfo scope_info, String name,
                               VariableMode* mode, InitializationFlag* init_flag,
-                              MaybeAssignedFlag* maybe_assigned_flag);
+                              MaybeAssignedFlag* maybe_assigned_flag,
+                              IsStaticFlag* is_static_flag);
 
   // Lookup metadata of a MODULE-allocated variable.  Return 0 if there is no
   // module variable with the given name (the index value of a MODULE variable
@@ -316,6 +320,7 @@
   using InitFlagField = VariableModeField::Next<InitializationFlag, 1>;
   using MaybeAssignedFlagField = InitFlagField::Next<MaybeAssignedFlag, 1>;
   using ParameterNumberField = MaybeAssignedFlagField::Next<uint32_t, 16>;
+  using IsStaticFlagField = ParameterNumberField::Next<IsStaticFlag, 1>;
 
   friend class ScopeIterator;
   friend std::ostream& operator<<(std::ostream& os,
diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h
index c3c46b4..570202e 100644
--- a/src/parsing/parser-base.h
+++ b/src/parsing/parser-base.h
@@ -4440,7 +4440,9 @@
   }
 
   if (class_info.requires_brand) {
-    class_scope->DeclareBrandVariable(ast_value_factory(), kNoSourcePosition);
+    // TODO(joyee): implement static brand checking
+    class_scope->DeclareBrandVariable(
+        ast_value_factory(), IsStaticFlag::kNotStatic, kNoSourcePosition);
   }
 
   return impl()->RewriteClassLiteral(class_scope, name, &class_info,
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc
index 307ba7f..cc1bf8b 100644
--- a/src/parsing/parser.cc
+++ b/src/parsing/parser.cc
@@ -2782,13 +2782,15 @@
 
 Variable* Parser::CreatePrivateNameVariable(ClassScope* scope,
                                             VariableMode mode,
+                                            IsStaticFlag is_static_flag,
                                             const AstRawString* name) {
   DCHECK_NOT_NULL(name);
   int begin = position();
   int end = end_position();
   bool was_added = false;
   DCHECK(IsConstVariableMode(mode));
-  Variable* var = scope->DeclarePrivateName(name, mode, &was_added);
+  Variable* var =
+      scope->DeclarePrivateName(name, mode, is_static_flag, &was_added);
   if (!was_added) {
     Scanner::Location loc(begin, end);
     ReportMessageAt(loc, MessageTemplate::kVarRedeclaration, var->raw_name());
@@ -2834,8 +2836,10 @@
     }
   }
 
-  Variable* private_name_var =
-      CreatePrivateNameVariable(scope, GetVariableMode(kind), property_name);
+  Variable* private_name_var = CreatePrivateNameVariable(
+      scope, GetVariableMode(kind),
+      is_static ? IsStaticFlag::kStatic : IsStaticFlag::kNotStatic,
+      property_name);
   int pos = property->value()->position();
   if (pos == kNoSourcePosition) {
     pos = property->key()->position();
diff --git a/src/parsing/parser.h b/src/parsing/parser.h
index 57af801..014b698 100644
--- a/src/parsing/parser.h
+++ b/src/parsing/parser.h
@@ -300,6 +300,7 @@
                              ZonePtrList<const AstRawString>* names);
   Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name);
   Variable* CreatePrivateNameVariable(ClassScope* scope, VariableMode mode,
+                                      IsStaticFlag is_static_flag,
                                       const AstRawString* name);
   FunctionLiteral* CreateInitializerFunction(
       const char* name, DeclarationScope* scope,
diff --git a/src/parsing/preparser.h b/src/parsing/preparser.h
index 29aae57..a14b48a 100644
--- a/src/parsing/preparser.h
+++ b/src/parsing/preparser.h
@@ -1108,9 +1108,10 @@
 
   Variable* DeclarePrivateVariableName(const AstRawString* name,
                                        ClassScope* scope, VariableMode mode,
+                                       IsStaticFlag is_static_flag,
                                        bool* was_added) {
     DCHECK(IsConstVariableMode(mode));
-    return scope->DeclarePrivateName(name, mode, was_added);
+    return scope->DeclarePrivateName(name, mode, is_static_flag, was_added);
   }
 
   Variable* DeclareVariableName(const AstRawString* name, VariableMode mode,
@@ -1258,8 +1259,10 @@
       bool is_static, ClassInfo* class_info) {
     bool was_added;
 
-    DeclarePrivateVariableName(property_name.string_, scope,
-                               GetVariableMode(kind), &was_added);
+    DeclarePrivateVariableName(
+        property_name.string_, scope, GetVariableMode(kind),
+        is_static ? IsStaticFlag::kStatic : IsStaticFlag::kNotStatic,
+        &was_added);
     if (!was_added) {
       Scanner::Location loc(property.position(), property.position() + 1);
       ReportMessageAt(loc, MessageTemplate::kVarRedeclaration,
diff --git a/test/cctest/interpreter/bytecode_expectations/StaticPrivateMethodDeclaration.golden b/test/cctest/interpreter/bytecode_expectations/StaticPrivateMethodDeclaration.golden
new file mode 100644
index 0000000..6a3f77f
--- /dev/null
+++ b/test/cctest/interpreter/bytecode_expectations/StaticPrivateMethodDeclaration.golden
@@ -0,0 +1,256 @@
+#
+# Autogenerated by generate-bytecode-expectations.
+#
+
+---
+wrap: yes
+private methods: yes
+
+---
+snippet: "
+  {
+    class A {
+      static #a() { return 1; }
+    }
+  }
+"
+frame size: 7
+parameter count: 1
+bytecode array length: 55
+bytecodes: [
+  /*   30 E> */ B(StackCheck),
+                B(CreateBlockContext), U8(0),
+                B(PushContext), R(2),
+                B(LdaTheHole),
+                B(Star), R(6),
+                B(CreateClosure), U8(2), U8(0), U8(2),
+                B(Star), R(3),
+                B(LdaConstant), U8(1),
+                B(Star), R(4),
+                B(CreateClosure), U8(3), U8(1), U8(2),
+                B(StaCurrentContextSlot), U8(4),
+                B(Mov), R(3), R(5),
+                B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(3),
+                B(Star), R(4),
+                B(Mov), R(5), R(1),
+                B(LdaConstant), U8(4),
+                B(Star), R(5),
+                B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(5), U8(1),
+                B(StaCurrentContextSlot), U8(5),
+                B(PopContext), R(2),
+                B(Mov), R(1), R(0),
+                B(LdaUndefined),
+  /*   84 S> */ B(Return),
+]
+constant pool: [
+  SCOPE_INFO_TYPE,
+  FIXED_ARRAY_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+  ONE_BYTE_INTERNALIZED_STRING_TYPE ["A"],
+]
+handlers: [
+]
+
+---
+snippet: "
+  {
+    class A {
+      static get #a() { return 1; }
+    }
+  }
+"
+frame size: 8
+parameter count: 1
+bytecode array length: 65
+bytecodes: [
+  /*   30 E> */ B(StackCheck),
+                B(CreateBlockContext), U8(0),
+                B(PushContext), R(2),
+                B(LdaTheHole),
+                B(Star), R(6),
+                B(CreateClosure), U8(2), U8(0), U8(2),
+                B(Star), R(3),
+                B(LdaConstant), U8(1),
+                B(Star), R(4),
+                B(Mov), R(3), R(5),
+                B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(3),
+                B(Star), R(4),
+                B(Mov), R(5), R(1),
+                B(LdaConstant), U8(3),
+                B(Star), R(5),
+                B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(5), U8(1),
+                B(StaCurrentContextSlot), U8(5),
+                B(CreateClosure), U8(4), U8(1), U8(2),
+                B(Star), R(6),
+                B(LdaNull),
+                B(Star), R(7),
+                B(CallRuntime), U16(Runtime::kCreatePrivateAccessors), R(6), U8(2),
+                B(StaCurrentContextSlot), U8(4),
+                B(PopContext), R(2),
+                B(Mov), R(1), R(0),
+                B(LdaUndefined),
+  /*   88 S> */ B(Return),
+]
+constant pool: [
+  SCOPE_INFO_TYPE,
+  FIXED_ARRAY_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+  ONE_BYTE_INTERNALIZED_STRING_TYPE ["A"],
+  SHARED_FUNCTION_INFO_TYPE,
+]
+handlers: [
+]
+
+---
+snippet: "
+  {
+    class A {
+      static set #a(val) { }
+    }
+  }
+"
+frame size: 8
+parameter count: 1
+bytecode array length: 65
+bytecodes: [
+  /*   30 E> */ B(StackCheck),
+                B(CreateBlockContext), U8(0),
+                B(PushContext), R(2),
+                B(LdaTheHole),
+                B(Star), R(6),
+                B(CreateClosure), U8(2), U8(0), U8(2),
+                B(Star), R(3),
+                B(LdaConstant), U8(1),
+                B(Star), R(4),
+                B(Mov), R(3), R(5),
+                B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(3),
+                B(Star), R(4),
+                B(Mov), R(5), R(1),
+                B(LdaConstant), U8(3),
+                B(Star), R(5),
+                B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(5), U8(1),
+                B(StaCurrentContextSlot), U8(5),
+                B(LdaNull),
+                B(Star), R(6),
+                B(CreateClosure), U8(4), U8(1), U8(2),
+                B(Star), R(7),
+                B(CallRuntime), U16(Runtime::kCreatePrivateAccessors), R(6), U8(2),
+                B(StaCurrentContextSlot), U8(4),
+                B(PopContext), R(2),
+                B(Mov), R(1), R(0),
+                B(LdaUndefined),
+  /*   81 S> */ B(Return),
+]
+constant pool: [
+  SCOPE_INFO_TYPE,
+  FIXED_ARRAY_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+  ONE_BYTE_INTERNALIZED_STRING_TYPE ["A"],
+  SHARED_FUNCTION_INFO_TYPE,
+]
+handlers: [
+]
+
+---
+snippet: "
+  {
+    class A {
+      static get #a() { return 1; }
+      static set #a(val) { }
+    }
+  }
+"
+frame size: 8
+parameter count: 1
+bytecode array length: 68
+bytecodes: [
+  /*   30 E> */ B(StackCheck),
+                B(CreateBlockContext), U8(0),
+                B(PushContext), R(2),
+                B(LdaTheHole),
+                B(Star), R(6),
+                B(CreateClosure), U8(2), U8(0), U8(2),
+                B(Star), R(3),
+                B(LdaConstant), U8(1),
+                B(Star), R(4),
+                B(Mov), R(3), R(5),
+                B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(3),
+                B(Star), R(4),
+                B(Mov), R(5), R(1),
+                B(LdaConstant), U8(3),
+                B(Star), R(5),
+                B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(5), U8(1),
+                B(StaCurrentContextSlot), U8(5),
+                B(CreateClosure), U8(4), U8(1), U8(2),
+                B(Star), R(6),
+                B(CreateClosure), U8(5), U8(2), U8(2),
+                B(Star), R(7),
+                B(CallRuntime), U16(Runtime::kCreatePrivateAccessors), R(6), U8(2),
+                B(StaCurrentContextSlot), U8(4),
+                B(PopContext), R(2),
+                B(Mov), R(1), R(0),
+                B(LdaUndefined),
+  /*  115 S> */ B(Return),
+]
+constant pool: [
+  SCOPE_INFO_TYPE,
+  FIXED_ARRAY_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+  ONE_BYTE_INTERNALIZED_STRING_TYPE ["A"],
+  SHARED_FUNCTION_INFO_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+]
+handlers: [
+]
+
+---
+snippet: "
+  {
+    class A {
+      static #a() { }
+      #b() { }
+    }
+  }
+"
+frame size: 7
+parameter count: 1
+bytecode array length: 61
+bytecodes: [
+  /*   30 E> */ B(StackCheck),
+                B(CreateBlockContext), U8(0),
+                B(PushContext), R(2),
+                B(LdaTheHole),
+                B(Star), R(6),
+                B(CreateClosure), U8(2), U8(0), U8(2),
+                B(Star), R(3),
+                B(LdaConstant), U8(1),
+                B(Star), R(4),
+                B(CreateClosure), U8(3), U8(1), U8(2),
+                B(StaCurrentContextSlot), U8(4),
+                B(CreateClosure), U8(4), U8(2), U8(2),
+                B(StaCurrentContextSlot), U8(5),
+                B(Mov), R(3), R(5),
+                B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(3),
+                B(Star), R(4),
+                B(Mov), R(5), R(1),
+                B(LdaConstant), U8(5),
+                B(Star), R(5),
+                B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(5), U8(1),
+                B(StaCurrentContextSlot), U8(6),
+                B(PopContext), R(2),
+                B(Mov), R(1), R(0),
+                B(LdaUndefined),
+  /*   87 S> */ B(Return),
+]
+constant pool: [
+  SCOPE_INFO_TYPE,
+  FIXED_ARRAY_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+  SHARED_FUNCTION_INFO_TYPE,
+  ONE_BYTE_INTERNALIZED_STRING_TYPE ["A"],
+]
+handlers: [
+]
+
diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc
index 1583866..0f6c2f7 100644
--- a/test/cctest/interpreter/test-bytecode-generator.cc
+++ b/test/cctest/interpreter/test-bytecode-generator.cc
@@ -2884,6 +2884,50 @@
   i::FLAG_harmony_private_methods = old_methods_flag;
 }
 
+TEST(StaticPrivateMethodDeclaration) {
+  bool old_methods_flag = i::FLAG_harmony_private_methods;
+  i::FLAG_harmony_private_methods = true;
+  InitializedIgnitionHandleScope scope;
+  BytecodeExpectationsPrinter printer(CcTest::isolate());
+
+  const char* snippets[] = {
+      "{\n"
+      "  class A {\n"
+      "    static #a() { return 1; }\n"
+      "  }\n"
+      "}\n",
+
+      "{\n"
+      "  class A {\n"
+      "    static get #a() { return 1; }\n"
+      "  }\n"
+      "}\n",
+
+      "{\n"
+      "  class A {\n"
+      "    static set #a(val) { }\n"
+      "  }\n"
+      "}\n",
+
+      "{\n"
+      "  class A {\n"
+      "    static get #a() { return 1; }\n"
+      "    static set #a(val) { }\n"
+      "  }\n"
+      "}\n",
+
+      "{\n"
+      "  class A {\n"
+      "    static #a() { }\n"
+      "    #b() { }\n"
+      "  }\n"
+      "}\n"};
+
+  CHECK(CompareTexts(BuildActual(printer, snippets),
+                     LoadGolden("StaticPrivateMethodDeclaration.golden")));
+  i::FLAG_harmony_private_methods = old_methods_flag;
+}
+
 TEST(PrivateAccessorDeclaration) {
   bool old_methods_flag = i::FLAG_harmony_private_methods;
   i::FLAG_harmony_private_methods = true;
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc
index 857bd7a..0184041 100644
--- a/test/cctest/test-parsing.cc
+++ b/test/cctest/test-parsing.cc
@@ -5877,6 +5877,70 @@
                     private_methods, arraysize(private_methods));
 }
 
+TEST(PrivateStaticClassMethodsAndAccessorsNoErrors) {
+  // clang-format off
+  // Tests proposed class fields syntax.
+  const char* context_data[][2] = {{"(class {", "});"},
+                                   {"(class extends Base {", "});"},
+                                   {"class C {", "}"},
+                                   {"class C extends Base {", "}"},
+                                   {nullptr, nullptr}};
+  const char* class_body_data[] = {
+    "static #a() { }",
+    "static get #a() { }",
+    "static set #a(val) { }",
+    "static get #a() { } static set #a(val) { }",
+    "static *#a() { }",
+    "static async #a() { }",
+    "static async *#a() { }",
+    nullptr
+  };
+  // clang-format on
+
+  RunParserSyncTest(context_data, class_body_data, kError);
+
+  static const ParserFlag private_methods[] = {kAllowHarmonyPrivateMethods};
+  RunParserSyncTest(context_data, class_body_data, kSuccess, nullptr, 0,
+                    private_methods, arraysize(private_methods));
+}
+
+TEST(PrivateStaticClassMethodsAndAccessorsDuplicateErrors) {
+  // clang-format off
+  // Tests proposed class fields syntax.
+  const char* context_data[][2] = {{"(class {", "});"},
+                                   {"(class extends Base {", "});"},
+                                   {"class C {", "}"},
+                                   {"class C extends Base {", "}"},
+                                   {nullptr, nullptr}};
+  const char* class_body_data[] = {
+    "static get #a() {} static get #a() {}",
+    "static get #a() {} static #a() {}",
+    "static get #a() {} get #a() {}",
+    "static get #a() {} set #a(val) {}",
+    "static get #a() {} #a() {}",
+
+    "static set #a(val) {} static set #a(val) {}",
+    "static set #a(val) {} static #a() {}",
+    "static set #a(val) {} get #a() {}",
+    "static set #a(val) {} set #a(val) {}",
+    "static set #a(val) {} #a() {}",
+
+    "static #a() {} static #a() {}",
+    "static #a() {} #a(val) {}",
+    "static #a() {} set #a(val) {}",
+    "static #a() {} get #a() {}",
+
+    nullptr
+  };
+  // clang-format on
+
+  RunParserSyncTest(context_data, class_body_data, kError);
+
+  static const ParserFlag private_methods[] = {kAllowHarmonyPrivateMethods};
+  RunParserSyncTest(context_data, class_body_data, kError, nullptr, 0,
+                    private_methods, arraysize(private_methods));
+}
+
 TEST(PrivateClassFieldsNoErrors) {
   // clang-format off
   // Tests proposed class fields syntax.
@@ -6216,14 +6280,6 @@
     "#a; static #a",
     "static #a; #a",
 
-    // TODO(joyee): support static private methods
-    "static #a() { }",
-    "static get #a() { }",
-    "static set #a() { }",
-    "static *#a() { }",
-    "static async #a() { }",
-    "static async *#a() { }",
-
     // ASI
     "static #['a'] = 0\n",
     "static #['a'] = 0\n b",
diff --git a/test/mjsunit/harmony/static-private-methods.js b/test/mjsunit/harmony/static-private-methods.js
new file mode 100644
index 0000000..3de14ce
--- /dev/null
+++ b/test/mjsunit/harmony/static-private-methods.js
@@ -0,0 +1,44 @@
+// Copyright 2019 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-private-methods
+
+"use strict";
+
+// Static private methods
+{
+  let store = 1;
+  class C {
+    static #a() { return store; }
+  }
+}
+
+// Complementary static private accessors.
+{
+  let store = 1;
+  class C {
+    static get #a() { return store; }
+    static set #a(val) { store = val; }
+  }
+}
+
+// Duplicate static private accessors and methods.
+{
+  assertThrows('class C { static get #a() {} static get #a() {} }', SyntaxError);
+  assertThrows('class C { static get #a() {} static #a() {} }', SyntaxError);
+  assertThrows('class C { static get #a() {} get #a() {} }', SyntaxError);
+  assertThrows('class C { static get #a() {} set #a(val) {} }', SyntaxError);
+  assertThrows('class C { static get #a() {} #a() {} }', SyntaxError);
+
+  assertThrows('class C { static set #a(val) {} static set #a(val) {} }', SyntaxError);
+  assertThrows('class C { static set #a(val) {} static #a() {} }', SyntaxError);
+  assertThrows('class C { static set #a(val) {} get #a() {} }', SyntaxError);
+  assertThrows('class C { static set #a(val) {} set #a(val) {} }', SyntaxError);
+  assertThrows('class C { static set #a(val) {} #a() {} }', SyntaxError);
+
+  assertThrows('class C { static #a() {} static #a() {} }', SyntaxError);
+  assertThrows('class C { static #a() {} #a(val) {} }', SyntaxError);
+  assertThrows('class C { static #a() {} set #a(val) {} }', SyntaxError);
+  assertThrows('class C { static #a() {} get #a() {} }', SyntaxError);
+}