[class] parse private accessors
This patch adds:
- VariableMode::kPrivateMethod
- VariableMode::kPrivateSetterOnly
- VariableMode::kPrivateGetterOnly
- VariableMode::kPrivateGetterAndSetter
And replace the previous RequiresBrandCheckFlag by inferring
whether the brand check is required from these VariableModes.
It is then possible to check duplicate non-complementary
accessors in the parsers and throw early errors, and allow
complementary accessors to be associated with the same
private name variable.
This patch also adds the following AssignType:
- PRIVATE_METHOD
- PRIVATE_GETTER_ONLY
- PRIVATE_SETTER_ONLY
- PRIVATE_GETTER_AND_SETTER
corresponding to the new VariableModes so that it's possible
to generate specialized code for different type of
private accessor declarations.
Design doc: https://docs.google.com/document/d/10W4begYfs7lmldSqBoQBBt_BKamgT8igqxF9u50RGrI/edit
Bug: v8:8330
Change-Id: I0fb61b1be248630d1eadd74fb16d7d64a421f4c4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1695204
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62988}
diff --git a/src/ast/ast.h b/src/ast/ast.h
index 85c8c32..6622eda 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -1605,12 +1605,15 @@
// Otherwise, the assignment is to a non-property (a global, a local slot, a
// parameter slot, or a destructuring pattern).
enum AssignType {
- NON_PROPERTY, // destructuring
- NAMED_PROPERTY, // obj.key
- KEYED_PROPERTY, // obj[key]
- NAMED_SUPER_PROPERTY, // super.key
- KEYED_SUPER_PROPERTY, // super[key]
- PRIVATE_METHOD // obj.#key: #key is a private method
+ NON_PROPERTY, // destructuring
+ NAMED_PROPERTY, // obj.key
+ KEYED_PROPERTY, // obj[key]
+ NAMED_SUPER_PROPERTY, // super.key
+ KEYED_SUPER_PROPERTY, // super[key]
+ PRIVATE_METHOD, // obj.#key: #key is a private method
+ PRIVATE_GETTER_ONLY, // obj.#key: #key only has a getter defined
+ PRIVATE_SETTER_ONLY, // obj.#key: #key only has a setter defined
+ PRIVATE_GETTER_AND_SETTER // obj.#key: #key has both accessors defined
};
class Property final : public Expression {
@@ -1631,8 +1634,21 @@
VariableProxy* proxy = property->key()->AsVariableProxy();
DCHECK_NOT_NULL(proxy);
Variable* var = proxy->var();
- // Use KEYED_PROPERTY for private fields.
- return var->requires_brand_check() ? PRIVATE_METHOD : KEYED_PROPERTY;
+
+ switch (var->mode()) {
+ case VariableMode::kPrivateMethod:
+ return PRIVATE_METHOD;
+ case VariableMode::kConst:
+ return KEYED_PROPERTY; // Use KEYED_PROPERTY for private fields.
+ case VariableMode::kPrivateGetterOnly:
+ return PRIVATE_GETTER_ONLY;
+ case VariableMode::kPrivateSetterOnly:
+ return PRIVATE_SETTER_ONLY;
+ case VariableMode::kPrivateGetterAndSetter:
+ return PRIVATE_GETTER_AND_SETTER;
+ default:
+ UNREACHABLE();
+ }
}
bool super_access = property->IsSuperAccess();
return (property->key()->IsPropertyName())
diff --git a/src/ast/prettyprinter.cc b/src/ast/prettyprinter.cc
index c0fe3ba..8883a20 100644
--- a/src/ast/prettyprinter.cc
+++ b/src/ast/prettyprinter.cc
@@ -1289,6 +1289,18 @@
PrintIndentedVisit("PRIVATE_METHOD", node->key());
break;
}
+ case PRIVATE_GETTER_ONLY: {
+ PrintIndentedVisit("PRIVATE_GETTER_ONLY", node->key());
+ break;
+ }
+ case PRIVATE_SETTER_ONLY: {
+ PrintIndentedVisit("PRIVATE_SETTER_ONLY", node->key());
+ break;
+ }
+ case PRIVATE_GETTER_AND_SETTER: {
+ PrintIndentedVisit("PRIVATE_GETTER_AND_SETTER", node->key());
+ break;
+ }
case KEYED_PROPERTY:
case KEYED_SUPER_PROPERTY: {
PrintIndentedVisit("KEY", node->key());
diff --git a/src/ast/scopes.cc b/src/ast/scopes.cc
index 237d98e..f8da533 100644
--- a/src/ast/scopes.cc
+++ b/src/ast/scopes.cc
@@ -40,7 +40,6 @@
VariableKind kind,
InitializationFlag initialization_flag,
MaybeAssignedFlag maybe_assigned_flag,
- RequiresBrandCheckFlag requires_brand_check,
bool* was_added) {
// AstRawStrings are unambiguous, i.e., the same string is always represented
// by the same AstRawString*.
@@ -52,9 +51,8 @@
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, requires_brand_check);
+ Variable* variable = new (zone) Variable(
+ scope, name, mode, kind, initialization_flag, maybe_assigned_flag);
p->value = variable;
}
return reinterpret_cast<Variable*>(p->value);
@@ -787,13 +785,11 @@
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check = kNoBrandCheck;
{
location = VariableLocation::CONTEXT;
index = ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode,
- &init_flag, &maybe_assigned_flag,
- &requires_brand_check);
+ &init_flag, &maybe_assigned_flag);
found = index >= 0;
}
@@ -818,9 +814,9 @@
}
bool was_added;
- Variable* var = cache->variables_.Declare(
- zone(), this, name, mode, NORMAL_VARIABLE, init_flag, maybe_assigned_flag,
- requires_brand_check, &was_added);
+ Variable* var =
+ cache->variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
+ init_flag, maybe_assigned_flag, &was_added);
DCHECK(was_added);
var->AllocateTo(location, index);
return var;
@@ -873,6 +869,8 @@
VariableKind kind, bool* was_added,
InitializationFlag init_flag) {
DCHECK(!already_resolved_);
+ // Private methods should be declared with ClassScope::DeclarePrivateName()
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(mode));
// This function handles VariableMode::kVar, VariableMode::kLet, and
// VariableMode::kConst modes. VariableMode::kDynamic variables are
// introduced during variable allocation, and VariableMode::kTemporary
@@ -905,6 +903,8 @@
VariableMode mode, VariableKind kind, InitializationFlag init,
bool* was_added, bool* sloppy_mode_block_scope_function_redefinition,
bool* ok) {
+ // Private methods should be declared with ClassScope::DeclarePrivateName()
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(mode));
DCHECK(IsDeclaredVariableMode(mode));
DCHECK(!already_resolved_);
DCHECK(!GetDeclarationScope()->is_being_lazily_parsed());
@@ -990,7 +990,8 @@
DCHECK(IsDeclaredVariableMode(mode));
DCHECK(!already_resolved_);
DCHECK(GetDeclarationScope()->is_being_lazily_parsed());
-
+ // Private methods should be declared with ClassScope::DeclarePrivateName()
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(mode));
if (mode == VariableMode::kVar && !is_declaration_scope()) {
return GetDeclarationScope()->DeclareVariableName(name, mode, was_added,
kind);
@@ -1044,7 +1045,7 @@
bool was_added;
return cache->variables_.Declare(
zone(), this, name, VariableMode::kDynamicGlobal, kind,
- kCreatedInitialized, kNotAssigned, kNoBrandCheck, &was_added);
+ kCreatedInitialized, kNotAssigned, &was_added);
// TODO(neis): Mark variable as maybe-assigned?
}
@@ -1596,10 +1597,6 @@
if (comma) PrintF(", ");
PrintF("hole initialization elided");
}
- if (var->requires_brand_check()) {
- if (comma) PrintF(", ");
- PrintF("requires brand check");
- }
PrintF("\n");
}
@@ -1774,9 +1771,9 @@
// Declare a new non-local.
DCHECK(IsDynamicVariableMode(mode));
bool was_added;
- Variable* var = variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
- kCreatedInitialized, kNotAssigned,
- kNoBrandCheck, &was_added);
+ Variable* var =
+ variables_.Declare(zone(), this, name, mode, NORMAL_VARIABLE,
+ kCreatedInitialized, kNotAssigned, &was_added);
// Allocate it by giving it a dynamic lookup.
var->AllocateTo(VariableLocation::LOOKUP, -1);
return var;
@@ -2081,11 +2078,14 @@
//
// Temporary variables are always stack-allocated. Catch-bound variables are
// always context-allocated.
- if (var->mode() == VariableMode::kTemporary) return false;
+ VariableMode mode = var->mode();
+ if (mode == VariableMode::kTemporary) return false;
if (is_catch_scope()) return true;
- if ((is_script_scope() || is_eval_scope()) &&
- IsLexicalVariableMode(var->mode())) {
- return true;
+ if (is_script_scope() || is_eval_scope()) {
+ if (IsLexicalVariableMode(mode) ||
+ IsPrivateMethodOrAccessorVariableMode(mode)) {
+ return true;
+ }
}
return var->has_forced_context_allocation() || inner_scope_calls_eval_;
}
@@ -2326,15 +2326,28 @@
(is_function_var_in_context ? 1 : 0);
}
-Variable* ClassScope::DeclarePrivateName(
- const AstRawString* name, RequiresBrandCheckFlag requires_brand_check,
- bool* was_added) {
+bool IsComplementaryAccessorPair(VariableMode a, VariableMode b) {
+ switch (a) {
+ case VariableMode::kPrivateGetterOnly:
+ return b == VariableMode::kPrivateSetterOnly;
+ case VariableMode::kPrivateSetterOnly:
+ return b == VariableMode::kPrivateGetterOnly;
+ default:
+ return false;
+ }
+}
+
+Variable* ClassScope::DeclarePrivateName(const AstRawString* name,
+ VariableMode mode, bool* was_added) {
Variable* result = EnsureRareData()->private_name_map.Declare(
- zone(), this, name, VariableMode::kConst, NORMAL_VARIABLE,
+ zone(), this, name, mode, NORMAL_VARIABLE,
InitializationFlag::kNeedsInitialization,
- MaybeAssignedFlag::kMaybeAssigned, requires_brand_check, was_added);
+ MaybeAssignedFlag::kMaybeAssigned, was_added);
if (*was_added) {
locals_.Add(result);
+ } else if (IsComplementaryAccessorPair(result->mode(), mode)) {
+ *was_added = true;
+ result->set_mode(VariableMode::kPrivateGetterAndSetter);
}
result->ForceContextAllocation();
return result;
@@ -2416,22 +2429,20 @@
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check;
- int index =
- ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode, &init_flag,
- &maybe_assigned_flag, &requires_brand_check);
+ int index = ScopeInfo::ContextSlotIndex(*scope_info_, name_handle, &mode,
+ &init_flag, &maybe_assigned_flag);
if (index < 0) {
return nullptr;
}
- DCHECK_EQ(mode, VariableMode::kConst);
+ DCHECK(IsConstVariableMode(mode));
DCHECK_EQ(init_flag, InitializationFlag::kNeedsInitialization);
DCHECK_EQ(maybe_assigned_flag, MaybeAssignedFlag::kMaybeAssigned);
// Add the found private name to the map to speed up subsequent
// lookups for the same name.
bool was_added;
- Variable* var = DeclarePrivateName(name, requires_brand_check, &was_added);
+ Variable* var = DeclarePrivateName(name, mode, &was_added);
DCHECK(was_added);
var->AllocateTo(VariableLocation::CONTEXT, index);
return var;
diff --git a/src/ast/scopes.h b/src/ast/scopes.h
index 932d5c7..76480b2 100644
--- a/src/ast/scopes.h
+++ b/src/ast/scopes.h
@@ -42,7 +42,6 @@
VariableMode mode, VariableKind kind,
InitializationFlag initialization_flag,
MaybeAssignedFlag maybe_assigned_flag,
- RequiresBrandCheckFlag requires_brand_check,
bool* was_added);
V8_EXPORT_PRIVATE Variable* Lookup(const AstRawString* name);
@@ -558,7 +557,7 @@
MaybeAssignedFlag maybe_assigned_flag, bool* was_added) {
Variable* result =
variables_.Declare(zone, this, name, mode, kind, initialization_flag,
- maybe_assigned_flag, kNoBrandCheck, was_added);
+ maybe_assigned_flag, was_added);
if (*was_added) locals_.Add(result);
return result;
}
@@ -1175,8 +1174,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,
- RequiresBrandCheckFlag requires_brand_check,
+ Variable* DeclarePrivateName(const AstRawString* name, VariableMode mode,
bool* was_added);
void AddUnresolvedPrivateName(VariableProxy* proxy);
diff --git a/src/ast/variables.h b/src/ast/variables.h
index c7b47b1..3358915 100644
--- a/src/ast/variables.h
+++ b/src/ast/variables.h
@@ -21,8 +21,7 @@
public:
Variable(Scope* scope, const AstRawString* name, VariableMode mode,
VariableKind kind, InitializationFlag initialization_flag,
- MaybeAssignedFlag maybe_assigned_flag = kNotAssigned,
- RequiresBrandCheckFlag requires_brand_check = kNoBrandCheck)
+ MaybeAssignedFlag maybe_assigned_flag = kNotAssigned)
: scope_(scope),
name_(name),
local_if_not_shadowed_(nullptr),
@@ -32,7 +31,6 @@
bit_field_(MaybeAssignedFlagField::encode(maybe_assigned_flag) |
InitializationFlagField::encode(initialization_flag) |
VariableModeField::encode(mode) |
- RequiresBrandCheckField::encode(requires_brand_check) |
IsUsedField::encode(false) |
ForceContextAllocationField::encode(false) |
ForceHoleInitializationField::encode(false) |
@@ -58,6 +56,9 @@
Handle<String> name() const { return name_->string(); }
const AstRawString* raw_name() const { return name_; }
VariableMode mode() const { return VariableModeField::decode(bit_field_); }
+ void set_mode(VariableMode mode) {
+ bit_field_ = VariableModeField::update(bit_field_, mode);
+ }
bool has_forced_context_allocation() const {
return ForceContextAllocationField::decode(bit_field_);
}
@@ -85,17 +86,8 @@
set_maybe_assigned();
}
- RequiresBrandCheckFlag get_requires_brand_check_flag() const {
- return RequiresBrandCheckField::decode(bit_field_);
- }
-
bool requires_brand_check() const {
- return get_requires_brand_check_flag() == kRequiresBrandCheck;
- }
-
- void set_requires_brand_check() {
- bit_field_ =
- RequiresBrandCheckField::update(bit_field_, kRequiresBrandCheck);
+ return IsPrivateMethodOrAccessorVariableMode(mode());
}
int initializer_position() { return initializer_position_; }
@@ -125,7 +117,8 @@
// declaration time. Only returns valid results after scope analysis.
bool binding_needs_init() const {
DCHECK_IMPLIES(initialization_flag() == kNeedsInitialization,
- IsLexicalVariableMode(mode()));
+ IsLexicalVariableMode(mode()) ||
+ IsPrivateMethodOrAccessorVariableMode(mode()));
DCHECK_IMPLIES(ForceHoleInitializationField::decode(bit_field_),
initialization_flag() == kNeedsInitialization);
@@ -149,7 +142,8 @@
// be required at runtime.
void ForceHoleInitialization() {
DCHECK_EQ(kNeedsInitialization, initialization_flag());
- DCHECK(IsLexicalVariableMode(mode()));
+ DCHECK(IsLexicalVariableMode(mode()) ||
+ IsPrivateMethodOrAccessorVariableMode(mode()));
bit_field_ = ForceHoleInitializationField::update(bit_field_, true);
}
@@ -243,7 +237,7 @@
bit_field_ = MaybeAssignedFlagField::update(bit_field_, kMaybeAssigned);
}
- using VariableModeField = BitField16<VariableMode, 0, 3>;
+ using VariableModeField = BitField16<VariableMode, 0, 4>;
using VariableKindField =
BitField16<VariableKind, VariableModeField::kNext, 3>;
using LocationField =
@@ -256,8 +250,7 @@
BitField16<bool, InitializationFlagField::kNext, 1>;
using MaybeAssignedFlagField =
BitField16<MaybeAssignedFlag, ForceHoleInitializationField::kNext, 1>;
- using RequiresBrandCheckField =
- BitField16<RequiresBrandCheckFlag, MaybeAssignedFlagField::kNext, 1>;
+
Variable** next() { return &next_; }
friend List;
friend base::ThreadedListTraits<Variable>;
diff --git a/src/common/globals.h b/src/common/globals.h
index 8d1bf5d..eeeaa4f 100644
--- a/src/common/globals.h
+++ b/src/common/globals.h
@@ -1060,6 +1060,25 @@
// has been shadowed by an eval-introduced
// variable
+ // Variables for private methods or accessors whose access require
+ // brand check. Declared only in class scopes by the compiler
+ // and allocated only in class contexts:
+ kPrivateMethod, // Does not coexist with any other variable with the same
+ // name in the same scope.
+
+ kPrivateSetterOnly, // Incompatible with variables with the same name but
+ // any mode other than kPrivateGetterOnly. Transition to
+ // kPrivateGetterAndSetter if a later declaration for the
+ // same name with kPrivateGetterOnly is made.
+
+ kPrivateGetterOnly, // Incompatible with variables with the same name but
+ // any mode other than kPrivateSetterOnly. Transition to
+ // kPrivateGetterAndSetter if a later declaration for the
+ // same name with kPrivateSetterOnly is made.
+
+ kPrivateGetterAndSetter, // Does not coexist with any other variable with the
+ // same name in the same scope.
+
kLastLexicalVariableMode = kConst,
};
@@ -1071,6 +1090,14 @@
return "VAR";
case VariableMode::kLet:
return "LET";
+ case VariableMode::kPrivateGetterOnly:
+ return "PRIVATE_GETTER_ONLY";
+ case VariableMode::kPrivateSetterOnly:
+ return "PRIVATE_SETTER_ONLY";
+ case VariableMode::kPrivateMethod:
+ return "PRIVATE_METHOD";
+ case VariableMode::kPrivateGetterAndSetter:
+ return "PRIVATE_GETTER_AND_SETTER";
case VariableMode::kConst:
return "CONST";
case VariableMode::kDynamic:
@@ -1104,6 +1131,21 @@
return mode <= VariableMode::kVar;
}
+inline bool IsPrivateMethodOrAccessorVariableMode(VariableMode mode) {
+ return mode >= VariableMode::kPrivateMethod &&
+ mode <= VariableMode::kPrivateGetterAndSetter;
+}
+
+inline bool IsSerializableVariableMode(VariableMode mode) {
+ return IsDeclaredVariableMode(mode) ||
+ IsPrivateMethodOrAccessorVariableMode(mode);
+}
+
+inline bool IsConstVariableMode(VariableMode mode) {
+ return mode == VariableMode::kConst ||
+ IsPrivateMethodOrAccessorVariableMode(mode);
+}
+
inline bool IsLexicalVariableMode(VariableMode mode) {
STATIC_ASSERT(static_cast<uint8_t>(VariableMode::kLet) ==
0); // Implies that mode >= VariableMode::kLet.
@@ -1168,8 +1210,6 @@
enum MaybeAssignedFlag : uint8_t { kNotAssigned, kMaybeAssigned };
-enum RequiresBrandCheckFlag : uint8_t { kNoBrandCheck, kRequiresBrandCheck };
-
enum class InterpreterPushArgsMode : unsigned {
kArrayFunction,
kWithFinalSpread,
diff --git a/src/debug/debug-frames.cc b/src/debug/debug-frames.cc
index 4fe062b..78c4c32 100644
--- a/src/debug/debug-frames.cc
+++ b/src/debug/debug-frames.cc
@@ -93,10 +93,8 @@
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check;
return ScopeInfo::ContextSlotIndex(*info, *parameter_name, &mode, &init_flag,
- &maybe_assigned_flag,
- &requires_brand_check) != -1;
+ &maybe_assigned_flag) != -1;
}
RedirectActiveFunctions::RedirectActiveFunctions(SharedFunctionInfo shared,
diff --git a/src/debug/debug-scopes.cc b/src/debug/debug-scopes.cc
index 1091e3a..c9c859c 100644
--- a/src/debug/debug-scopes.cc
+++ b/src/debug/debug-scopes.cc
@@ -884,10 +884,9 @@
VariableMode mode;
InitializationFlag flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check;
- int slot_index = ScopeInfo::ContextSlotIndex(
- context_->scope_info(), *variable_name, &mode, &flag,
- &maybe_assigned_flag, &requires_brand_check);
+ int slot_index =
+ ScopeInfo::ContextSlotIndex(context_->scope_info(), *variable_name, &mode,
+ &flag, &maybe_assigned_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 a0c6fa9..4f691e6 100644
--- a/src/debug/debug-stack-trace-iterator.cc
+++ b/src/debug/debug-stack-trace-iterator.cc
@@ -98,10 +98,9 @@
VariableMode mode;
InitializationFlag flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check;
int slot_index = ScopeInfo::ContextSlotIndex(
context->scope_info(), ReadOnlyRoots(isolate_->heap()).this_string(),
- &mode, &flag, &maybe_assigned_flag, &requires_brand_check);
+ &mode, &flag, &maybe_assigned_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/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
index d3b27b4..3091111 100644
--- a/src/interpreter/bytecode-generator.cc
+++ b/src/interpreter/bytecode-generator.cc
@@ -2005,9 +2005,11 @@
HoleCheckMode::kElided);
break;
}
- default:
+ case ClassLiteral::Property::GETTER:
+ case ClassLiteral::Property::SETTER: {
// TODO(joyee): Private accessors are not yet supported.
- UNREACHABLE();
+ // Make them noops for now.
+ }
}
}
@@ -3017,6 +3019,7 @@
void BytecodeGenerator::BuildHoleCheckForVariableAssignment(Variable* variable,
Token::Value op) {
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(variable->mode()));
if (variable->is_this() && variable->mode() == VariableMode::kConst &&
op == Token::INIT) {
// Perform an initialization check for 'this'. 'this' variable is the
@@ -3201,10 +3204,10 @@
}
// static
BytecodeGenerator::AssignmentLhsData
-BytecodeGenerator::AssignmentLhsData::PrivateMethod(Register object,
- const AstRawString* name) {
- return AssignmentLhsData(PRIVATE_METHOD, nullptr, RegisterList(), object,
- Register(), nullptr, name);
+BytecodeGenerator::AssignmentLhsData::PrivateMethodOrAccessor(
+ AssignType type, Property* property) {
+ return AssignmentLhsData(type, property, RegisterList(), Register(),
+ Register(), nullptr, nullptr);
}
// static
BytecodeGenerator::AssignmentLhsData
@@ -3237,12 +3240,12 @@
Register key = VisitForRegisterValue(property->key());
return AssignmentLhsData::KeyedProperty(object, key);
}
- case PRIVATE_METHOD: {
+ case PRIVATE_METHOD:
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
DCHECK(!property->IsSuperAccess());
- AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
- Register object = VisitForRegisterValue(property->obj());
- const AstRawString* name = property->key()->AsVariableProxy()->raw_name();
- return AssignmentLhsData::PrivateMethod(object, name);
+ return AssignmentLhsData::PrivateMethodOrAccessor(assign_type, property);
}
case NAMED_SUPER_PROPERTY: {
AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
@@ -3799,9 +3802,16 @@
break;
}
case PRIVATE_METHOD: {
- BuildThrowPrivateMethodWriteError(lhs_data.name());
+ BuildThrowPrivateMethodWriteError(
+ lhs_data.expr()->AsProperty()->key()->AsVariableProxy()->raw_name());
break;
}
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
+ // TODO(joyee): implement private accessors.
+ return;
+ }
}
}
@@ -3848,10 +3858,18 @@
break;
}
case PRIVATE_METHOD: {
- BuildThrowPrivateMethodWriteError(lhs_data.name());
+ BuildThrowPrivateMethodWriteError(
+ lhs_data.expr()->AsProperty()->key()->AsVariableProxy()->raw_name());
break;
}
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
+ // TODO(joyee): implement private accessors.
+ return;
+ }
}
+
BinaryOperation* binop = expr->AsCompoundAssignment()->binary_operation();
FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
if (expr->value()->IsSmiLiteral()) {
@@ -4325,6 +4343,12 @@
VisitForAccumulatorValue(property->key());
break;
}
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
+ // TODO(joyee): implement private accessors.
+ return;
+ }
}
}
@@ -4879,6 +4903,12 @@
property->key()->AsVariableProxy()->raw_name());
break;
}
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
+ // TODO(joyee): implement private accessors.
+ return;
+ }
}
// Save result for postfix expressions.
@@ -4950,6 +4980,12 @@
property->key()->AsVariableProxy()->raw_name());
break;
}
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
+ // TODO(joyee): implement private accessors.
+ UNREACHABLE();
+ }
}
// Restore old value for postfix expressions.
diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h
index b754d2c..5cbc566 100644
--- a/src/interpreter/bytecode-generator.h
+++ b/src/interpreter/bytecode-generator.h
@@ -84,8 +84,8 @@
Register object,
const AstRawString* name);
static AssignmentLhsData KeyedProperty(Register object, Register key);
- static AssignmentLhsData PrivateMethod(Register object,
- const AstRawString* name);
+ static AssignmentLhsData PrivateMethodOrAccessor(AssignType type,
+ Property* property);
static AssignmentLhsData NamedSuperProperty(
RegisterList super_property_args);
static AssignmentLhsData KeyedSuperProperty(
@@ -93,7 +93,10 @@
AssignType assign_type() const { return assign_type_; }
Expression* expr() const {
- DCHECK_EQ(assign_type_, NON_PROPERTY);
+ DCHECK(assign_type_ == NON_PROPERTY || assign_type_ == PRIVATE_METHOD ||
+ assign_type_ == PRIVATE_GETTER_ONLY ||
+ assign_type_ == PRIVATE_SETTER_ONLY ||
+ assign_type_ == PRIVATE_GETTER_AND_SETTER);
return expr_;
}
Expression* object_expr() const {
@@ -101,8 +104,7 @@
return object_expr_;
}
Register object() const {
- DCHECK(assign_type_ == NAMED_PROPERTY || assign_type_ == KEYED_PROPERTY ||
- assign_type_ == PRIVATE_METHOD);
+ DCHECK(assign_type_ == NAMED_PROPERTY || assign_type_ == KEYED_PROPERTY);
return object_;
}
Register key() const {
@@ -110,7 +112,7 @@
return key_;
}
const AstRawString* name() const {
- DCHECK(assign_type_ == NAMED_PROPERTY || assign_type_ == PRIVATE_METHOD);
+ DCHECK(assign_type_ == NAMED_PROPERTY);
return name_;
}
RegisterList super_property_args() const {
diff --git a/src/objects/contexts.cc b/src/objects/contexts.cc
index 861e06d..74fb447 100644
--- a/src/objects/contexts.cc
+++ b/src/objects/contexts.cc
@@ -44,7 +44,7 @@
DCHECK(context.IsScriptContext());
int slot_index = ScopeInfo::ContextSlotIndex(
context.scope_info(), name, &result->mode, &result->init_flag,
- &result->maybe_assigned_flag, &result->requires_brand_check);
+ &result->maybe_assigned_flag);
if (slot_index >= 0) {
result->context_index = i;
@@ -161,8 +161,8 @@
}
static PropertyAttributes GetAttributesForMode(VariableMode mode) {
- DCHECK(IsDeclaredVariableMode(mode));
- return mode == VariableMode::kConst ? READ_ONLY : NONE;
+ DCHECK(IsSerializableVariableMode(mode));
+ return IsConstVariableMode(mode) ? READ_ONLY : NONE;
}
// static
@@ -287,10 +287,8 @@
VariableMode mode;
InitializationFlag flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check;
int slot_index = ScopeInfo::ContextSlotIndex(scope_info, *name, &mode,
- &flag, &maybe_assigned_flag,
- &requires_brand_check);
+ &flag, &maybe_assigned_flag);
DCHECK(slot_index < 0 || slot_index >= MIN_CONTEXT_SLOTS);
if (slot_index >= 0) {
if (FLAG_trace_contexts) {
diff --git a/src/objects/contexts.h b/src/objects/contexts.h
index da400ee..1a0f885 100644
--- a/src/objects/contexts.h
+++ b/src/objects/contexts.h
@@ -361,7 +361,6 @@
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
- RequiresBrandCheckFlag requires_brand_check;
};
inline int used() const;
diff --git a/src/objects/scope-info.cc b/src/objects/scope-info.cc
index eca8bc1..95d97f9 100644
--- a/src/objects/scope-info.cc
+++ b/src/objects/scope-info.cc
@@ -218,8 +218,6 @@
uint32_t info =
VariableModeField::encode(var->mode()) |
InitFlagField::encode(var->initialization_flag()) |
- RequiresBrandCheckField::encode(
- var->get_requires_brand_check_flag()) |
MaybeAssignedFlagField::encode(var->maybe_assigned()) |
ParameterNumberField::encode(ParameterNumberField::kMax);
scope_info.set(context_local_base + local_index, *var->name(), mode);
@@ -236,8 +234,6 @@
VariableModeField::encode(var->mode()) |
InitFlagField::encode(var->initialization_flag()) |
MaybeAssignedFlagField::encode(var->maybe_assigned()) |
- RequiresBrandCheckField::encode(
- var->get_requires_brand_check_flag()) |
ParameterNumberField::encode(ParameterNumberField::kMax);
scope_info.set(module_var_entry + kModuleVariablePropertiesOffset,
Smi::FromInt(properties));
@@ -276,8 +272,6 @@
VariableModeField::encode(var->mode()) |
InitFlagField::encode(var->initialization_flag()) |
MaybeAssignedFlagField::encode(var->maybe_assigned()) |
- RequiresBrandCheckField::encode(
- var->get_requires_brand_check_flag()) |
ParameterNumberField::encode(ParameterNumberField::kMax);
scope_info.set(context_local_base + local_index, *var->name(), mode);
scope_info.set(context_local_info_base + local_index,
@@ -451,7 +445,6 @@
VariableModeField::encode(VariableMode::kConst) |
InitFlagField::encode(kCreatedInitialized) |
MaybeAssignedFlagField::encode(kNotAssigned) |
- RequiresBrandCheckField::encode(kNoBrandCheck) |
ParameterNumberField::encode(ParameterNumberField::kMax);
scope_info->set(index++, Smi::FromInt(value));
}
@@ -708,14 +701,6 @@
return MaybeAssignedFlagField::decode(value);
}
-RequiresBrandCheckFlag ScopeInfo::RequiresBrandCheck(int var) const {
- DCHECK_LE(0, var);
- DCHECK_LT(var, ContextLocalCount());
- int info_index = ContextLocalInfosIndex() + var;
- int value = Smi::ToInt(get(info_index));
- return RequiresBrandCheckField::decode(value);
-}
-
// static
bool ScopeInfo::VariableIsSynthetic(String name) {
// There's currently no flag stored on the ScopeInfo to indicate that a
@@ -755,8 +740,7 @@
int ScopeInfo::ContextSlotIndex(ScopeInfo scope_info, String name,
VariableMode* mode,
InitializationFlag* init_flag,
- MaybeAssignedFlag* maybe_assigned_flag,
- RequiresBrandCheckFlag* requires_brand_check) {
+ MaybeAssignedFlag* maybe_assigned_flag) {
DisallowHeapAllocation no_gc;
DCHECK(name.IsInternalizedString());
DCHECK_NOT_NULL(mode);
@@ -773,7 +757,6 @@
*mode = scope_info.ContextLocalMode(var);
*init_flag = scope_info.ContextLocalInitFlag(var);
*maybe_assigned_flag = scope_info.ContextLocalMaybeAssignedFlag(var);
- *requires_brand_check = scope_info.RequiresBrandCheck(var);
int result = Context::MIN_CONTEXT_SLOTS + var;
DCHECK_LT(result, scope_info.ContextLength());
diff --git a/src/objects/scope-info.h b/src/objects/scope-info.h
index 746322e..7631676 100644
--- a/src/objects/scope-info.h
+++ b/src/objects/scope-info.h
@@ -130,9 +130,6 @@
// Return the initialization flag of the given context local.
MaybeAssignedFlag ContextLocalMaybeAssignedFlag(int var) const;
- // Return whether access to the variable requries a brand check.
- RequiresBrandCheckFlag RequiresBrandCheck(int var) const;
-
// Return true if this local was introduced by the compiler, and should not be
// exposed to the user in a debugger.
static bool VariableIsSynthetic(String name);
@@ -144,8 +141,7 @@
// mode for that variable.
static int ContextSlotIndex(ScopeInfo scope_info, String name,
VariableMode* mode, InitializationFlag* init_flag,
- MaybeAssignedFlag* maybe_assigned_flag,
- RequiresBrandCheckFlag* requires_brand_check);
+ MaybeAssignedFlag* maybe_assigned_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,13 +312,13 @@
static const int kPositionInfoEntries = 2;
// Properties of variables.
- using VariableModeField = BitField<VariableMode, 0, 3>;
- using InitFlagField = BitField<InitializationFlag, 3, 1>;
- using MaybeAssignedFlagField = BitField<MaybeAssignedFlag, 4, 1>;
- using RequiresBrandCheckField =
- BitField<RequiresBrandCheckFlag, MaybeAssignedFlagField::kNext, 1>;
+ using VariableModeField = BitField<VariableMode, 0, 4>;
+ using InitFlagField =
+ BitField<InitializationFlag, VariableModeField::kNext, 1>;
+ using MaybeAssignedFlagField =
+ BitField<MaybeAssignedFlag, InitFlagField::kNext, 1>;
using ParameterNumberField =
- BitField<uint32_t, RequiresBrandCheckField::kNext, 16>;
+ BitField<uint32_t, MaybeAssignedFlagField::kNext, 16>;
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 737be1f..14bc428 100644
--- a/src/parsing/parser-base.h
+++ b/src/parsing/parser-base.h
@@ -625,9 +625,17 @@
}
}
- RequiresBrandCheckFlag RequiresBrandCheck(ClassLiteralProperty::Kind kind) {
- return kind == ClassLiteralProperty::Kind::FIELD ? kNoBrandCheck
- : kRequiresBrandCheck;
+ VariableMode GetVariableMode(ClassLiteralProperty::Kind kind) {
+ switch (kind) {
+ case ClassLiteralProperty::Kind::FIELD:
+ return VariableMode::kConst;
+ case ClassLiteralProperty::Kind::METHOD:
+ return VariableMode::kPrivateMethod;
+ case ClassLiteralProperty::Kind::GETTER:
+ return VariableMode::kPrivateGetterOnly;
+ case ClassLiteralProperty::Kind::SETTER:
+ return VariableMode::kPrivateSetterOnly;
+ }
}
const AstRawString* ClassFieldVariableName(AstValueFactory* ast_value_factory,
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc
index a0f2a80..43d3b87 100644
--- a/src/parsing/parser.cc
+++ b/src/parsing/parser.cc
@@ -2782,15 +2782,15 @@
return proxy->var();
}
-Variable* Parser::CreatePrivateNameVariable(
- ClassScope* scope, RequiresBrandCheckFlag requires_brand_check,
- const AstRawString* name) {
+Variable* Parser::CreatePrivateNameVariable(ClassScope* scope,
+ VariableMode mode,
+ const AstRawString* name) {
DCHECK_NOT_NULL(name);
int begin = position();
int end = end_position();
bool was_added = false;
- Variable* var =
- scope->DeclarePrivateName(name, requires_brand_check, &was_added);
+ DCHECK(IsConstVariableMode(mode));
+ Variable* var = scope->DeclarePrivateName(name, mode, &was_added);
if (!was_added) {
Scanner::Location loc(begin, end);
ReportMessageAt(loc, MessageTemplate::kVarRedeclaration, var->raw_name());
@@ -2825,14 +2825,8 @@
ClassLiteralProperty* property,
ClassLiteralProperty::Kind kind,
bool is_static, ClassInfo* class_info) {
- DCHECK_IMPLIES(kind == ClassLiteralProperty::Kind::METHOD,
+ DCHECK_IMPLIES(kind != ClassLiteralProperty::Kind::FIELD,
allow_harmony_private_methods());
- // TODO(joyee): We do not support private accessors yet (which allow
- // declaring the same private name twice). Make them noops.
- if (kind != ClassLiteralProperty::Kind::FIELD &&
- kind != ClassLiteralProperty::Kind::METHOD) {
- return;
- }
if (kind == ClassLiteralProperty::Kind::FIELD) {
if (is_static) {
@@ -2843,7 +2837,7 @@
}
Variable* private_name_var =
- CreatePrivateNameVariable(scope, RequiresBrandCheck(kind), property_name);
+ CreatePrivateNameVariable(scope, GetVariableMode(kind), 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 e20c153..2a22e19 100644
--- a/src/parsing/parser.h
+++ b/src/parsing/parser.h
@@ -297,9 +297,8 @@
VariableKind kind, int beg_pos, int end_pos,
ZonePtrList<const AstRawString>* names);
Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name);
- Variable* CreatePrivateNameVariable(
- ClassScope* scope, RequiresBrandCheckFlag requires_brand_check,
- const AstRawString* name);
+ Variable* CreatePrivateNameVariable(ClassScope* scope, VariableMode mode,
+ const AstRawString* name);
FunctionLiteral* CreateInitializerFunction(
const char* name, DeclarationScope* scope,
ZonePtrList<ClassLiteral::Property>* fields);
diff --git a/src/parsing/preparse-data.cc b/src/parsing/preparse-data.cc
index d66cbc4..fbb5658 100644
--- a/src/parsing/preparse-data.cc
+++ b/src/parsing/preparse-data.cc
@@ -265,7 +265,7 @@
}
if (!scope->is_hidden()) {
for (Variable* var : *scope->locals()) {
- if (IsDeclaredVariableMode(var->mode())) return true;
+ if (IsSerializableVariableMode(var->mode())) return true;
}
}
for (Scope* inner = scope->inner_scope(); inner != nullptr;
@@ -368,7 +368,7 @@
}
for (Variable* var : *scope->locals()) {
- if (IsDeclaredVariableMode(var->mode())) SaveDataForVariable(var);
+ if (IsSerializableVariableMode(var->mode())) SaveDataForVariable(var);
}
SaveDataForInnerScopes(scope);
@@ -611,7 +611,7 @@
}
for (Variable* var : *scope->locals()) {
- if (IsDeclaredVariableMode(var->mode())) RestoreDataForVariable(var);
+ if (IsSerializableVariableMode(var->mode())) RestoreDataForVariable(var);
}
RestoreDataForInnerScopes(scope);
diff --git a/src/parsing/preparser.h b/src/parsing/preparser.h
index e49f2a8..8ea625c 100644
--- a/src/parsing/preparser.h
+++ b/src/parsing/preparser.h
@@ -1101,16 +1101,18 @@
// Don't bother actually binding the proxy.
}
- Variable* DeclarePrivateVariableName(
- const AstRawString* name, ClassScope* scope,
- RequiresBrandCheckFlag requires_brand_check, bool* was_added) {
- return scope->DeclarePrivateName(name, requires_brand_check, was_added);
+ Variable* DeclarePrivateVariableName(const AstRawString* name,
+ ClassScope* scope, VariableMode mode,
+ bool* was_added) {
+ DCHECK(IsConstVariableMode(mode));
+ return scope->DeclarePrivateName(name, mode, was_added);
}
Variable* DeclareVariableName(const AstRawString* name, VariableMode mode,
Scope* scope, bool* was_added,
int position = kNoSourcePosition,
VariableKind kind = NORMAL_VARIABLE) {
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(mode));
Variable* var = scope->DeclareVariableName(name, mode, was_added, kind);
if (var == nullptr) {
ReportUnidentifiableError();
@@ -1250,16 +1252,10 @@
ClassScope* scope, const PreParserIdentifier& property_name,
const PreParserExpression& property, ClassLiteralProperty::Kind kind,
bool is_static, ClassInfo* class_info) {
- // TODO(joyee): We do not support private accessors yet (which allow
- // declaring the same private name twice). Make them noops.
- if (kind != ClassLiteralProperty::Kind::FIELD &&
- kind != ClassLiteralProperty::Kind::METHOD) {
- return;
- }
bool was_added;
DeclarePrivateVariableName(property_name.string_, scope,
- RequiresBrandCheck(kind), &was_added);
+ GetVariableMode(kind), &was_added);
if (!was_added) {
Scanner::Location loc(property.position(), property.position() + 1);
ReportMessageAt(loc, MessageTemplate::kVarRedeclaration,
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc
index ba69b6d..4630ab0 100644
--- a/test/cctest/test-parsing.cc
+++ b/test/cctest/test-parsing.cc
@@ -5542,7 +5542,10 @@
"async #['a']() { }",
"async *#['a]() { }",
- // TODO(joyee): check duplicate accessors
+ "get #a() {} get #a() {}",
+ "get #a() {} get #['a']() {}",
+ "set #a(val) {} set #a(val) {}",
+ "set #a(val) {} set #['a'](val) {}",
"#a\n#",
"#a() c",
@@ -5572,8 +5575,60 @@
private_methods, arraysize(private_methods));
}
+// Test that private members parse in class bodies nested in object literals
+TEST(PrivateMembersNestedInObjectLiteralsNoErrors) {
+ // clang-format off
+ const char* context_data[][2] = {{"({", "})"},
+ {"'use strict'; ({", "});"},
+ {nullptr, nullptr}};
+ const char* class_body_data[] = {
+ "a: class { #a = 1 }",
+ "a: class { #a = () => {} }",
+ "a: class { #a }",
+ "a: class { #a() { } }",
+ "a: class { get #a() { } }",
+ "a: class { set #a(foo) { } }",
+ "a: class { *#a() { } }",
+ "a: class { async #a() { } }",
+ "a: class { async *#a() { } }",
+ nullptr
+ };
+ // clang-format on
+
+ static const ParserFlag private_methods[] = {kAllowHarmonyPrivateMethods};
+ RunParserSyncTest(context_data, class_body_data, kSuccess, nullptr, 0,
+ private_methods, arraysize(private_methods));
+}
+
+// Test that private members parse in class bodies nested in classes
+TEST(PrivateMembersInNestedClassNoErrors) {
+ // clang-format off
+ const char* context_data[][2] = {{"(class {", "});"},
+ {"(class extends Base {", "});"},
+ {"class C {", "}"},
+ {"class C extends Base {", "}"},
+ {nullptr, nullptr}};
+ const char* class_body_data[] = {
+ "a = class { #a = 1 }",
+ "a = class { #a = () => {} }",
+ "a = class { #a }",
+ "a = class { #a() { } }",
+ "a = class { get #a() { } }",
+ "a = class { set #a(foo) { } }",
+ "a = class { *#a() { } }",
+ "a = class { async #a() { } }",
+ "a = class { async *#a() { } }",
+ nullptr
+ };
+ // clang-format on
+
+ static const ParserFlag private_methods[] = {kAllowHarmonyPrivateMethods};
+ RunParserSyncTest(context_data, class_body_data, kSuccess, nullptr, 0,
+ private_methods, arraysize(private_methods));
+}
+
// Test that private members do not parse outside class bodies
-TEST(PrivateMembersInNonClassNoErrors) {
+TEST(PrivateMembersInNonClassErrors) {
// clang-format off
const char* context_data[][2] = {{"", ""},
{"({", "})"},
@@ -5605,6 +5660,122 @@
private_methods, arraysize(private_methods));
}
+// Test that nested private members parse
+TEST(PrivateMembersNestedNoErrors) {
+ // clang-format off
+ const char* context_data[][2] = {{"(class { get #a() { ", "} });"},
+ {
+ "(class { set #a(val) {} get #a() { ",
+ "} });"
+ },
+ {"(class { set #a(val) {", "} });"},
+ {"(class { #a() { ", "} });"},
+ {nullptr, nullptr}};
+ const char* class_body_data[] = {
+ "class C { #a() {} }",
+ "class C { get #a() {} }",
+ "class C { get #a() {} set #a(val) {} }",
+ "class C { set #a(val) {} }",
+ nullptr
+ };
+ // clang-format on
+
+ static const ParserFlag private_methods[] = {kAllowHarmonyPrivateMethods};
+ RunParserSyncTest(context_data, class_body_data, kSuccess, nullptr, 0,
+ private_methods, arraysize(private_methods));
+}
+
+// Test that acessing undeclared private members result in early errors
+TEST(PrivateMembersEarlyErrors) {
+ // clang-format off
+ const char* context_data[][2] = {{"(class {", "});"},
+ {"(class extends Base {", "});"},
+ {"class C {", "}"},
+ {"class C extends Base {", "}"},
+ {nullptr, nullptr}};
+ const char* class_body_data[] = {
+ "set #b(val) { this.#a = val; }",
+ "get #b() { return this.#a; }",
+ "foo() { return this.#a; }",
+ "foo() { this.#a = 1; }",
+ 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 that acessing wrong kind private members do not error early.
+// Instead these should be runtime errors.
+TEST(PrivateMembersWrongAccessNoEarlyErrors) {
+ // clang-format off
+ const char* context_data[][2] = {{"(class {", "});"},
+ {"(class extends Base {", "});"},
+ {"class C {", "}"},
+ {"class C extends Base {", "}"},
+ {nullptr, nullptr}};
+ const char* class_body_data[] = {
+ // Private setter only
+ "set #b(val) {} fn() { return this.#b; }",
+ "set #b(val) {} fn() { this.#b++; }",
+ // Nested private setter only
+ R"(get #b() {}
+ fn() {
+ return new class { set #b(val) {} fn() { this.#b++; } };
+ })",
+ R"(get #b() {}
+ fn() {
+ return new class { set #b(val) {} fn() { return this.#b; } };
+ })",
+
+ // Private getter only
+ "get #b() { } fn() { this.#b = 1; }",
+ "get #b() { } fn() { this.#b++; }",
+ "get #b() { } fn(obj) { ({ y: this.#b } = obj); }",
+ // Nested private getter only
+ R"(set #b(val) {}
+ fn() {
+ return new class { get #b() {} fn() { this.#b++; } };
+ })",
+ R"(set #b(val) {}
+ fn() {
+ return new class { get #b() {} fn() { this.#b = 1; } };
+ })",
+ R"(set #b(val) {}
+ fn() {
+ return new class { get #b() {} fn() { ({ y: this.#b } = obj); } };
+ })",
+
+ // Writing to private methods
+ "#b() { } fn() { this.#b = 1; }",
+ "#b() { } fn() { this.#b++; }",
+ "#b() {} fn(obj) { ({ y: this.#b } = obj); }",
+ // Writing to nested private methods
+ R"(#b() {}
+ fn() {
+ return new class { get #b() {} fn() { this.#b++; } };
+ })",
+ R"(#b() {}
+ fn() {
+ return new class { get #b() {} fn() { this.#b = 1; } };
+ })",
+ R"(#b() {}
+ fn() {
+ return new class { get #b() {} fn() { ({ y: this.#b } = obj); } };
+ })",
+ nullptr
+ };
+ // clang-format on
+
+ static const ParserFlag private_methods[] = {kAllowHarmonyPrivateMethods};
+ RunParserSyncTest(context_data, class_body_data, kSuccess, nullptr, 0,
+ private_methods, arraysize(private_methods));
+}
+
TEST(PrivateClassFieldsNoErrors) {
// clang-format off
// Tests proposed class fields syntax.
diff --git a/test/mjsunit/harmony/private-accessors.js b/test/mjsunit/harmony/private-accessors.js
new file mode 100644
index 0000000..719cf23
--- /dev/null
+++ b/test/mjsunit/harmony/private-accessors.js
@@ -0,0 +1,36 @@
+// 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";
+
+// Complementary private accessors.
+{
+ class C {
+ get #a() { }
+ set #a(val) { }
+ }
+
+ new C;
+}
+
+// Nested private accessors.
+{
+ class C {
+ a() { this.#a; }
+ get #a() {
+ class D { get #a() { } }
+ return new D;
+ }
+ }
+ new C().a();
+}
+
+// Duplicate private accessors.
+// https://tc39.es/proposal-private-methods/#sec-static-semantics-early-errors
+{
+ assertThrows('class C { get #a() {} get #a() {} }', SyntaxError);
+ assertThrows('class C { set #a(val) {} set #a(val) {} }', SyntaxError);
+}