Commit b9191bd3 authored by Joyee Cheung's avatar Joyee Cheung Committed by Commit Bot

[class] implement private method declarations

This patch implements the declarations of private methods, the access
of private methods would be left to a future patch.
When a private methods declaration is encountered, we now:

- Create a brand symbol during class evaluation and store it in the
  context.
- Create the closures for the private methods
- Load the brand from the context and store it in the instance in the
  constructor.

Design: https://docs.google.com/document/d/1T-Ql6HOIH2U_8YjWkwK2rTfywwb7b3Qe8d3jkz72KwA/edit#

Bug: v8:8330
Change-Id: I2d695cbdc8a7367ddc7620d627b318f779d36150
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1568708
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61387}
parent db7f61d6
......@@ -201,6 +201,7 @@ class AstBigInt {
F(bigint, "bigint") \
F(boolean, "boolean") \
F(computed, "<computed>") \
F(dot_brand, ".brand") \
F(constructor, "constructor") \
F(default, "default") \
F(done, "done") \
......
......@@ -282,6 +282,17 @@ std::unique_ptr<char[]> FunctionLiteral::GetDebugName() const {
return result;
}
bool FunctionLiteral::requires_brand_initialization() const {
Scope* outer = scope_->outer_scope();
// If there are no variables declared in the outer scope other than
// the class name variable, the outer class scope may be elided when
// the function is deserialized after preparsing.
if (!outer->is_class_scope()) return false;
return outer->AsClassScope()->brand() != nullptr;
}
ObjectLiteralProperty::ObjectLiteralProperty(Expression* key, Expression* value,
Kind kind, bool is_computed_name)
: LiteralProperty(key, value, is_computed_name),
......
......@@ -2342,6 +2342,8 @@ class FunctionLiteral final : public Expression {
return RequiresInstanceMembersInitializer::decode(bit_field_);
}
bool requires_brand_initialization() const;
ProducedPreparseData* produced_preparse_data() const {
return produced_preparse_data_;
}
......@@ -2435,12 +2437,10 @@ class ClassLiteralProperty final : public LiteralProperty {
}
void set_private_name_var(Variable* var) {
DCHECK_EQ(FIELD, kind());
DCHECK(is_private());
private_or_computed_name_var_ = var;
}
Variable* private_name_var() const {
DCHECK_EQ(FIELD, kind());
DCHECK(is_private());
return private_or_computed_name_var_;
}
......@@ -2476,7 +2476,7 @@ class ClassLiteral final : public Expression {
public:
typedef ClassLiteralProperty Property;
Scope* scope() const { return scope_; }
ClassScope* scope() const { return scope_; }
Variable* class_variable() const { return class_variable_; }
Expression* extends() const { return extends_; }
FunctionLiteral* constructor() const { return constructor_; }
......@@ -2508,7 +2508,7 @@ class ClassLiteral final : public Expression {
private:
friend class AstNodeFactory;
ClassLiteral(Scope* scope, Variable* class_variable, Expression* extends,
ClassLiteral(ClassScope* scope, Variable* class_variable, Expression* extends,
FunctionLiteral* constructor, ZonePtrList<Property>* properties,
FunctionLiteral* static_fields_initializer,
FunctionLiteral* instance_members_initializer_function,
......@@ -2531,7 +2531,7 @@ class ClassLiteral final : public Expression {
}
int end_position_;
Scope* scope_;
ClassScope* scope_;
Variable* class_variable_;
Expression* extends_;
FunctionLiteral* constructor_;
......@@ -3233,7 +3233,7 @@ class AstNodeFactory final {
}
ClassLiteral* NewClassLiteral(
Scope* scope, Variable* variable, Expression* extends,
ClassScope* scope, Variable* variable, Expression* extends,
FunctionLiteral* constructor,
ZonePtrList<ClassLiteral::Property>* properties,
FunctionLiteral* static_fields_initializer,
......
......@@ -1054,12 +1054,17 @@ void AstPrinter::VisitClassLiteral(ClassLiteral* node) {
if (node->extends() != nullptr) {
PrintIndentedVisit("EXTENDS", node->extends());
}
Scope* outer = node->constructor()->scope()->outer_scope();
if (outer->is_class_scope()) {
Variable* brand = outer->AsClassScope()->brand();
PrintLiteralWithModeIndented("BRAND", brand, brand->raw_name());
}
if (node->static_fields_initializer() != nullptr) {
PrintIndentedVisit("STATIC FIELDS INITIALIZER",
node->static_fields_initializer());
}
if (node->instance_members_initializer_function() != nullptr) {
PrintIndentedVisit("INSTANCE ELEMENTS INITIALIZER",
PrintIndentedVisit("INSTANCE MEMBERS INITIALIZER",
node->instance_members_initializer_function());
}
PrintClassProperties(node->properties());
......@@ -1067,7 +1072,7 @@ void AstPrinter::VisitClassLiteral(ClassLiteral* node) {
void AstPrinter::VisitInitializeClassMembersStatement(
InitializeClassMembersStatement* node) {
IndentedScope indent(this, "INITIALIZE CLASS ELEMENTS", node->position());
IndentedScope indent(this, "INITIALIZE CLASS MEMBERS", node->position());
PrintClassProperties(node->fields());
}
......
......@@ -145,9 +145,16 @@ ClassScope::ClassScope(Zone* zone, Scope* outer_scope)
set_language_mode(LanguageMode::kStrict);
}
ClassScope::ClassScope(Zone* zone, Handle<ScopeInfo> scope_info)
ClassScope::ClassScope(Zone* zone, AstValueFactory* ast_value_factory,
Handle<ScopeInfo> scope_info)
: Scope(zone, CLASS_SCOPE, scope_info) {
set_language_mode(LanguageMode::kStrict);
if (scope_info->HasClassBrand()) {
Variable* brand =
LookupInScopeInfo(ast_value_factory->dot_brand_string(), this);
DCHECK_NOT_NULL(brand);
EnsureRareData()->brand = brand;
}
}
Scope::Scope(Zone* zone, ScopeType scope_type, Handle<ScopeInfo> scope_info)
......@@ -333,7 +340,8 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
outer_scope = new (zone)
DeclarationScope(zone, EVAL_SCOPE, handle(scope_info, isolate));
} else if (scope_info->scope_type() == CLASS_SCOPE) {
outer_scope = new (zone) ClassScope(zone, handle(scope_info, isolate));
outer_scope = new (zone)
ClassScope(zone, ast_value_factory, handle(scope_info, isolate));
} else if (scope_info->scope_type() == BLOCK_SCOPE) {
if (scope_info->is_declaration_scope()) {
outer_scope = new (zone)
......@@ -1710,6 +1718,11 @@ void Scope::Print(int n) {
if (class_scope->rare_data_ != nullptr) {
PrintMap(n1, "// private name vars:\n",
&(class_scope->rare_data_->private_name_map), true, function);
Variable* brand = class_scope->brand();
if (brand != nullptr) {
Indent(n1, "// brand var:\n");
PrintVar(n1, brand);
}
}
}
......@@ -2512,5 +2525,21 @@ VariableProxy* ClassScope::ResolvePrivateNamesPartially() {
return nullptr;
}
Variable* ClassScope::DeclareBrandVariable(AstValueFactory* ast_value_factory,
int class_token_pos) {
DCHECK_IMPLIES(rare_data_ != nullptr, rare_data_->brand == nullptr);
bool was_added;
Variable* brand = Declare(zone(), ast_value_factory->dot_brand_string(),
VariableMode::kConst, NORMAL_VARIABLE,
InitializationFlag::kNeedsInitialization,
MaybeAssignedFlag::kMaybeAssigned, &was_added);
DCHECK(was_added);
brand->ForceContextAllocation();
brand->set_is_used();
EnsureRareData()->brand = brand;
brand->set_initializer_position(class_token_pos);
return brand;
}
} // namespace internal
} // namespace v8
......@@ -1169,7 +1169,8 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
public:
ClassScope(Zone* zone, Scope* outer_scope);
// Deserialization.
ClassScope(Zone* zone, Handle<ScopeInfo> scope_info);
ClassScope(Zone* zone, AstValueFactory* ast_value_factory,
Handle<ScopeInfo> scope_info);
// Declare a private name in the private name map and add it to the
// local variables of this scope.
......@@ -1205,6 +1206,11 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
// and the current tail.
void MigrateUnresolvedPrivateNameTail(AstNodeFactory* ast_node_factory,
UnresolvedList::Iterator tail);
Variable* DeclareBrandVariable(AstValueFactory* ast_value_factory,
int class_token_pos);
Variable* brand() {
return rare_data_ == nullptr ? nullptr : rare_data_->brand;
}
private:
friend class Scope;
......@@ -1222,6 +1228,7 @@ class V8_EXPORT_PRIVATE ClassScope : public Scope {
explicit RareData(Zone* zone) : private_name_map(zone) {}
UnresolvedList unresolved_private_names;
VariableMap private_name_map;
Variable* brand = nullptr;
};
V8_INLINE RareData* EnsureRareData() {
......
......@@ -147,6 +147,7 @@
V(_, disjunction_string, "disjunction") \
V(_, display_name_string, "displayName") \
V(_, done_string, "done") \
V(_, dot_brand_string, ".brand") \
V(_, dot_catch_string, ".catch") \
V(_, dot_default_string, ".default") \
V(_, dot_for_string, ".for") \
......
......@@ -1156,10 +1156,15 @@ void BytecodeGenerator::GenerateBytecodeBody() {
builder()->StackCheck(info()->literal()->start_position());
// The derived constructor case is handled in VisitCallSuper.
if (IsBaseConstructor(function_kind()) &&
info()->literal()->requires_instance_members_initializer()) {
BuildInstanceMemberInitialization(Register::function_closure(),
builder()->Receiver());
if (IsBaseConstructor(function_kind())) {
if (info()->literal()->requires_brand_initialization()) {
BuildPrivateBrandInitialization(builder()->Receiver());
}
if (info()->literal()->requires_instance_members_initializer()) {
BuildInstanceMemberInitialization(Register::function_closure(),
builder()->Receiver());
}
}
// Visit statements in the function body.
......@@ -1929,6 +1934,39 @@ bool BytecodeGenerator::ShouldOptimizeAsOneShot() const {
info()->literal()->is_oneshot_iife();
}
void BytecodeGenerator::BuildPrivateClassMemberNameAssignment(
ClassLiteral::Property* property) {
DCHECK(property->is_private());
switch (property->kind()) {
case ClassLiteral::Property::FIELD: {
// Create the private name symbols for fields during class
// evaluation and store them on the context. These will be
// used as keys later during instance or static initialization.
RegisterAllocationScope private_name_register_scope(this);
Register private_name = register_allocator()->NewRegister();
VisitForRegisterValue(property->key(), private_name);
builder()
->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
.StoreAccumulatorInRegister(private_name)
.CallRuntime(Runtime::kCreatePrivateNameSymbol, private_name);
DCHECK_NOT_NULL(property->private_name_var());
BuildVariableAssignment(property->private_name_var(), Token::INIT,
HoleCheckMode::kElided);
break;
}
case ClassLiteral::Property::METHOD: {
// Create the closures for private methods.
VisitForAccumulatorValue(property->value());
BuildVariableAssignment(property->private_name_var(), Token::INIT,
HoleCheckMode::kElided);
break;
}
default:
// TODO(joyee): Private accessors are not yet supported.
UNREACHABLE();
}
}
void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
size_t class_boilerplate_entry =
builder()->AllocateDeferredConstantPoolEntry();
......@@ -1993,19 +2031,15 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
}
}
if (property->is_private()) {
BuildPrivateClassMemberNameAssignment(property);
// The private fields are initialized in the initializer function and
// the private brand for the private methods are initialized in the
// constructor instead.
continue;
}
if (property->kind() == ClassLiteral::Property::FIELD) {
if (property->is_private()) {
RegisterAllocationScope private_name_register_scope(this);
Register private_name = register_allocator()->NewRegister();
VisitForRegisterValue(property->key(), private_name);
builder()
->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
.StoreAccumulatorInRegister(private_name)
.CallRuntime(Runtime::kCreatePrivateNameSymbol, private_name);
DCHECK_NOT_NULL(property->private_name_var());
BuildVariableAssignment(property->private_name_var(), Token::INIT,
HoleCheckMode::kElided);
}
// We don't compute field's value here, but instead do it in the
// initializer function.
continue;
......@@ -2029,6 +2063,23 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
HoleCheckMode::kElided);
}
// Create the class brand symbol and store it on the context
// during class evaluation. This will be stored in the
// receiver later in the constructor.
if (expr->scope()->brand() != nullptr) {
Register brand = register_allocator()->NewRegister();
const AstRawString* class_name =
expr->class_variable() != nullptr
? expr->class_variable()->raw_name()
: ast_string_constants()->empty_string();
builder()
->LoadLiteral(class_name)
.StoreAccumulatorInRegister(brand)
.CallRuntime(Runtime::kCreatePrivateNameSymbol, brand);
BuildVariableAssignment(expr->scope()->brand(), Token::INIT,
HoleCheckMode::kElided);
}
if (expr->instance_members_initializer_function() != nullptr) {
Register initializer =
VisitForRegisterValue(expr->instance_members_initializer_function());
......@@ -2110,6 +2161,10 @@ void BytecodeGenerator::VisitInitializeClassMembersStatement(
for (int i = 0; i < stmt->fields()->length(); i++) {
ClassLiteral::Property* property = stmt->fields()->at(i);
// Private methods are not initialized in the
// InitializeClassMembersStatement.
DCHECK_IMPLIES(property->is_private(),
property->kind() == ClassLiteral::Property::FIELD);
if (property->is_computed_name()) {
DCHECK_EQ(property->kind(), ClassLiteral::Property::FIELD);
......@@ -2120,8 +2175,7 @@ void BytecodeGenerator::VisitInitializeClassMembersStatement(
// variable at class definition time.
BuildVariableLoad(var, HoleCheckMode::kElided);
builder()->StoreAccumulatorInRegister(key);
} else if (property->kind() == ClassLiteral::Property::FIELD &&
property->is_private()) {
} else if (property->is_private()) {
Variable* private_name_var = property->private_name_var();
DCHECK_NOT_NULL(private_name_var);
BuildVariableLoad(private_name_var, HoleCheckMode::kElided);
......@@ -2143,6 +2197,17 @@ void BytecodeGenerator::VisitInitializeClassMembersStatement(
}
}
void BytecodeGenerator::BuildPrivateBrandInitialization(Register receiver) {
RegisterList brand_args = register_allocator()->NewRegisterList(2);
Variable* brand = info()->scope()->outer_scope()->AsClassScope()->brand();
DCHECK_NOT_NULL(brand);
BuildVariableLoad(brand, HoleCheckMode::kElided);
builder()
->StoreAccumulatorInRegister(brand_args[1])
.MoveRegister(receiver, brand_args[0])
.CallRuntime(Runtime::kAddPrivateBrand, brand_args);
}
void BytecodeGenerator::BuildInstanceMemberInitialization(Register constructor,
Register instance) {
RegisterList args = register_allocator()->NewRegisterList(1);
......@@ -4475,6 +4540,13 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kRequired);
}
Register instance = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(instance);
if (info()->literal()->requires_brand_initialization()) {
BuildPrivateBrandInitialization(instance);
}
// The derived constructor has the correct bit set always, so we
// don't emit code to load and call the initializer if not
// required.
......@@ -4487,11 +4559,10 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
// if required.
if (info()->literal()->requires_instance_members_initializer() ||
!IsDerivedConstructor(info()->literal()->kind())) {
Register instance = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(instance);
BuildInstanceMemberInitialization(this_function, instance);
builder()->LoadAccumulatorWithRegister(instance);
}
builder()->LoadAccumulatorWithRegister(instance);
}
void BytecodeGenerator::VisitCallNew(CallNew* expr) {
......
......@@ -291,10 +291,12 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest);
void VisitCallSuper(Call* call);
void BuildPrivateClassMemberNameAssignment(ClassLiteral::Property* property);
void BuildClassLiteral(ClassLiteral* expr, Register name);
void VisitClassLiteral(ClassLiteral* expr, Register name);
void VisitNewTargetVariable(Variable* variable);
void VisitThisFunctionVariable(Variable* variable);
void BuildPrivateBrandInitialization(Register receiver);
void BuildInstanceMemberInitialization(Register constructor,
Register instance);
void BuildGeneratorObjectVariableInitialization();
......
......@@ -2247,6 +2247,7 @@ void ScopeInfo::ScopeInfoPrint(std::ostream& os) { // NOLINT
if (HasReceiver()) {
os << "\n - receiver: " << ReceiverVariableField::decode(flags);
}
if (HasClassBrand()) os << "\n - has class brand";
if (HasNewTarget()) os << "\n - needs new target";
if (HasFunctionName()) {
os << "\n - function name(" << FunctionVariableField::decode(flags)
......
......@@ -526,6 +526,11 @@ Handle<ClassBoilerplate> ClassBoilerplate::BuildClassBoilerplate(
for (int i = 0; i < expr->properties()->length(); i++) {
ClassLiteral::Property* property = expr->properties()->at(i);
// Private members are not processed using the class boilerplate.
if (property->is_private()) {
continue;
}
ClassBoilerplate::ValueKind value_kind;
switch (property->kind()) {
case ClassLiteral::Property::METHOD:
......
......@@ -134,6 +134,9 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
function_name_info = NONE;
}
const bool has_brand = scope->is_class_scope()
? scope->AsClassScope()->brand() != nullptr
: false;
const bool has_function_name = function_name_info != NONE;
const bool has_position_info = NeedsPositionInfo(scope->scope_type());
const bool has_receiver = receiver_info == STACK || receiver_info == CONTEXT;
......@@ -181,6 +184,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
LanguageModeField::encode(scope->language_mode()) |
DeclarationScopeField::encode(scope->is_declaration_scope()) |
ReceiverVariableField::encode(receiver_info) |
HasClassBrandField::encode(has_brand) |
HasNewTargetField::encode(has_new_target) |
FunctionVariableField::encode(function_name_info) |
HasInferredFunctionNameField::encode(has_inferred_function_name) |
......@@ -354,9 +358,9 @@ Handle<ScopeInfo> ScopeInfo::CreateForWithScope(
ScopeTypeField::encode(WITH_SCOPE) | CallsSloppyEvalField::encode(false) |
LanguageModeField::encode(LanguageMode::kSloppy) |
DeclarationScopeField::encode(false) |
ReceiverVariableField::encode(NONE) | HasNewTargetField::encode(false) |
FunctionVariableField::encode(NONE) | IsAsmModuleField::encode(false) |
HasSimpleParametersField::encode(true) |
ReceiverVariableField::encode(NONE) | HasClassBrandField::encode(false) |
HasNewTargetField::encode(false) | FunctionVariableField::encode(NONE) |
IsAsmModuleField::encode(false) | HasSimpleParametersField::encode(true) |
FunctionKindField::encode(kNormalFunction) |
HasOuterScopeInfoField::encode(has_outer_scope_info) |
IsDebugEvaluateScopeField::encode(false);
......@@ -416,7 +420,7 @@ Handle<ScopeInfo> ScopeInfo::CreateForBootstrapping(Isolate* isolate,
LanguageModeField::encode(LanguageMode::kSloppy) |
DeclarationScopeField::encode(true) |
ReceiverVariableField::encode(is_empty_function ? UNUSED : CONTEXT) |
HasNewTargetField::encode(false) |
HasClassBrandField::encode(false) | HasNewTargetField::encode(false) |
FunctionVariableField::encode(is_empty_function ? UNUSED : NONE) |
HasInferredFunctionNameField::encode(has_inferred_function_name) |
IsAsmModuleField::encode(false) | HasSimpleParametersField::encode(true) |
......@@ -536,6 +540,10 @@ bool ScopeInfo::HasAllocatedReceiver() const {
return allocation == STACK || allocation == CONTEXT;
}
bool ScopeInfo::HasClassBrand() const {
return HasClassBrandField::decode(Flags());
}
bool ScopeInfo::HasNewTarget() const {
return HasNewTargetField::decode(Flags());
}
......
......@@ -69,6 +69,9 @@ class ScopeInfo : public FixedArray {
// or context-allocated?
bool HasAllocatedReceiver() const;
// Does this scope has class brand (for private methods)?
bool HasClassBrand() const;
// Does this scope declare a "new.target" binding?
bool HasNewTarget() const;
......@@ -228,8 +231,10 @@ class ScopeInfo : public FixedArray {
class ReceiverVariableField
: public BitField<VariableAllocationInfo, DeclarationScopeField::kNext,
2> {};
class HasNewTargetField
class HasClassBrandField
: public BitField<bool, ReceiverVariableField::kNext, 1> {};
class HasNewTargetField
: public BitField<bool, HasClassBrandField::kNext, 1> {};
class FunctionVariableField
: public BitField<VariableAllocationInfo, HasNewTargetField::kNext, 2> {};
// TODO(cbruni): Combine with function variable field when only storing the
......
......@@ -527,6 +527,7 @@ class ParserBase {
has_static_computed_names(false),
has_static_class_fields(false),
has_instance_members(false),
requires_brand(false),
is_anonymous(false),
static_fields_scope(nullptr),
instance_members_scope(nullptr),
......@@ -543,6 +544,7 @@ class ParserBase {
bool has_static_computed_names;
bool has_static_class_fields;
bool has_instance_members;
bool requires_brand;
bool is_anonymous;
DeclarationScope* static_fields_scope;
DeclarationScope* instance_members_scope;
......@@ -4249,19 +4251,32 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
}
is_constructor &= class_info.has_seen_constructor;
if (V8_UNLIKELY(property_kind == ClassLiteralProperty::FIELD)) {
bool is_field = property_kind == ClassLiteralProperty::FIELD;
if (V8_UNLIKELY(prop_info.is_private)) {
DCHECK(!is_constructor);
class_info.requires_brand |= !is_field;
impl()->DeclarePrivateClassMember(class_scope, prop_info.name, property,
property_kind, prop_info.is_static,
&class_info);
impl()->InferFunctionName();
continue;
}
if (V8_UNLIKELY(is_field)) {
DCHECK(!prop_info.is_private);
if (prop_info.is_computed_name) {
DCHECK(!prop_info.is_private);
class_info.computed_field_count++;
}
impl()->DeclareClassField(class_scope, property, prop_info.name,
prop_info.is_static, prop_info.is_computed_name,
prop_info.is_private, &class_info);
} else {
impl()->DeclareClassProperty(class_scope, name, property, is_constructor,
&class_info);
impl()->DeclarePublicClassField(class_scope, property,
prop_info.is_static,
prop_info.is_computed_name, &class_info);
impl()->InferFunctionName();
continue;
}
impl()->DeclarePublicClassMethod(name, property, is_constructor,
&class_info);
impl()->InferFunctionName();
}
......@@ -4278,6 +4293,10 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
return impl()->FailureExpression();
}
if (class_info.requires_brand) {
class_scope->DeclareBrandVariable(ast_value_factory(), kNoSourcePosition);
}
return impl()->RewriteClassLiteral(class_scope, name, &class_info,
class_token_pos, end_pos);
}
......
......@@ -2800,18 +2800,16 @@ Variable* Parser::CreatePrivateNameVariable(ClassScope* scope,
return proxy->var();
}
void Parser::DeclareClassField(ClassScope* scope,
ClassLiteralProperty* property,
const AstRawString* property_name,
bool is_static, bool is_computed_name,
bool is_private, ClassInfo* class_info) {
void Parser::DeclarePublicClassField(ClassScope* scope,
ClassLiteralProperty* property,
bool is_static, bool is_computed_name,
ClassInfo* class_info) {
if (is_static) {
class_info->static_fields->Add(property, zone());
} else {
class_info->instance_fields->Add(property, zone());
}
DCHECK_IMPLIES(is_computed_name, !is_private);
if (is_computed_name) {
// We create a synthetic variable name here so that scope
// analysis doesn't dedupe the vars.
......@@ -2820,27 +2818,49 @@ void Parser::DeclareClassField(ClassScope* scope,
ast_value_factory(), class_info->computed_field_count));
property->set_computed_name_var(computed_name_var);
class_info->properties->Add(property, zone());
} else if (is_private) {
Variable* private_name_var =
CreatePrivateNameVariable(scope, property_name);
int pos = property->value()->position();
if (pos == kNoSourcePosition) {
pos = property->key()->position();
}
}
void Parser::DeclarePrivateClassMember(ClassScope* scope,
const AstRawString* property_name,
ClassLiteralProperty* property,
ClassLiteralProperty::Kind kind,
bool is_static, ClassInfo* class_info) {
DCHECK_IMPLIES(kind == ClassLiteralProperty::Kind::METHOD,
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) {
class_info->static_fields->Add(property, zone());
} else {
class_info->instance_fields->Add(property, zone());
}
private_name_var->set_initializer_position(pos);
property->set_private_name_var(private_name_var);
class_info->properties->Add(property, zone());
}
Variable* private_name_var = CreatePrivateNameVariable(scope, property_name);
int pos = property->value()->position();
if (pos == kNoSourcePosition) {
pos = property->key()->position();
}
private_name_var->set_initializer_position(pos);
property->set_private_name_var(private_name_var);
class_info->properties->Add(property, zone());
}
// This method declares a property of the given class. It updates the
// following fields of class_info, as appropriate:
// - constructor
// - properties
void Parser::DeclareClassProperty(ClassScope* scope,
const AstRawString* class_name,
ClassLiteralProperty* property,
bool is_constructor, ClassInfo* class_info) {
void Parser::DeclarePublicClassMethod(const AstRawString* class_name,
ClassLiteralProperty* property,
bool is_constructor,
ClassInfo* class_info) {
if (is_constructor) {
DCHECK(!class_info->constructor);
class_info->constructor = property->value()->AsFunctionLiteral();
......@@ -2861,9 +2881,9 @@ FunctionLiteral* Parser::CreateInitializerFunction(
FunctionKind::kClassMembersInitializerFunction);
// function() { .. class fields initializer .. }
ScopedPtrList<Statement> statements(pointer_buffer());
InitializeClassMembersStatement* static_fields =
InitializeClassMembersStatement* stmt =
factory()->NewInitializeClassMembersStatement(fields, kNoSourcePosition);
statements.Add(static_fields);
statements.Add(stmt);
return factory()->NewFunctionLiteral(
ast_value_factory()->GetOneByteString(name), scope, statements, 0, 0, 0,
FunctionLiteral::kNoDuplicateParameters,
......
......@@ -313,6 +313,19 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
int class_token_pos, int end_pos);
void DeclareClassVariable(const AstRawString* name, ClassInfo* class_info,
int class_token_pos);
void DeclareClassBrandVariable(ClassScope* scope, ClassInfo* class_info,
int class_token_pos);
void DeclarePrivateClassMember(ClassScope* scope,
const AstRawString* property_name,
ClassLiteralProperty* property,
ClassLiteralProperty::Kind kind,
bool is_static, ClassInfo* class_info);
void DeclarePublicClassMethod(const AstRawString* class_name,
ClassLiteralProperty* property,
bool is_constructor, ClassInfo* class_info);
void DeclarePublicClassField(ClassScope* scope,
ClassLiteralProperty* property, bool is_static,
bool is_computed_name, ClassInfo* class_info);
void DeclareClassProperty(ClassScope* scope, const AstRawString* class_name,
ClassLiteralProperty* property, bool is_constructor,
ClassInfo* class_info);
......
......@@ -1230,32 +1230,39 @@ class PreParser : public ParserBase<PreParser> {
&was_added);
}
}
V8_INLINE void DeclareClassProperty(ClassScope* scope,
const PreParserIdentifier& class_name,
const PreParserExpression& property,
bool is_constructor,
ClassInfo* class_info) {}
V8_INLINE void DeclareClassField(ClassScope* scope,
const PreParserExpression& property,
const PreParserIdentifier& property_name,
bool is_static, bool is_computed_name,
bool is_private, ClassInfo* class_info) {
DCHECK_IMPLIES(is_computed_name, !is_private);
V8_INLINE void DeclarePublicClassMethod(const PreParserIdentifier& class_name,
const PreParserExpression& property,
bool is_constructor,
ClassInfo* class_info) {}
V8_INLINE void DeclarePublicClassField(ClassScope* scope,
const PreParserExpression& property,
bool is_static, bool is_computed_name,
ClassInfo* class_info) {
if (is_computed_name) {
bool was_added;
DeclareVariableName(
ClassFieldVariableName(ast_value_factory(),
class_info->computed_field_count),
VariableMode::kConst, scope, &was_added);
} else if (is_private) {
bool was_added;
DeclarePrivateVariableName(property_name.string_, scope, &was_added);
if (!was_added) {
Scanner::Location loc(property.position(), property.position() + 1);
ReportMessageAt(loc, MessageTemplate::kVarRedeclaration,
property_name.string_);
}
}
}
V8_INLINE void DeclarePrivateClassMember(
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, &was_added);
if (!was_added) {
Scanner::Location loc(property.position(), property.position() + 1);
ReportMessageAt(loc, MessageTemplate::kVarRedeclaration,
property_name.string_);
}
}
......
......@@ -1113,6 +1113,31 @@ RUNTIME_FUNCTION(Runtime_GetOwnPropertyDescriptor) {
return *desc.ToPropertyDescriptorObject(isolate);
}
RUNTIME_FUNCTION(Runtime_AddPrivateBrand) {
HandleScope scope(isolate);
DCHECK_EQ(args.length(), 2);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
CONVERT_ARG_HANDLE_CHECKED(Symbol, brand, 1);
DCHECK(brand->is_private_name());
LookupIterator it = LookupIterator::PropertyOrElement(
isolate, receiver, brand, LookupIterator::OWN);
if (it.IsFound()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kVarRedeclaration, brand));
}
PropertyAttributes attributes =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
// TODO(joyee): we could use this slot to store something useful. For now,
// store the brand itself.
CHECK(Object::AddDataProperty(&it, brand, attributes, Just(kDontThrow),
StoreOrigin::kMaybeKeyed)
.FromJust());
return *receiver;
}
RUNTIME_FUNCTION(Runtime_AddPrivateField) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
......
......@@ -106,6 +106,7 @@ bool Runtime::NeedsExactContext(FunctionId id) {
// try-catch in async function.
return false;
case Runtime::kAddPrivateField:
case Runtime::kAddPrivateBrand:
case Runtime::kCopyDataProperties:
case Runtime::kCreateDataProperty:
case Runtime::kCreatePrivateNameSymbol:
......
......@@ -282,6 +282,7 @@ namespace internal {
#define FOR_EACH_INTRINSIC_OBJECT(F, I) \
F(AddDictionaryProperty, 3, 1) \
F(AddPrivateField, 3, 1) \
F(AddPrivateBrand, 2, 1) \
F(AllocateHeapNumber, 0, 1) \
F(ClassOf, 1, 1) \
F(CollectTypeProfile, 3, 1) \
......
#
# Autogenerated by generate-bytecode-expectations.
#
---
wrap: yes
private methods: yes
---
snippet: "
{
class A {
#a() { return 1; }
}
new A;
}
"
frame size: 7
parameter count: 1
bytecode array length: 62
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),
/* 78 S> */ B(Ldar), R(0),
/* 78 E> */ B(Construct), R(0), R(0), U8(0), U8(0),
B(LdaUndefined),
/* 87 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 D {
#d() {}
}
class E extends D {
#e() {}
}
new D;
new E;
}
"
frame size: 9
parameter count: 1
bytecode array length: 121
bytecodes: [
/* 30 E> */ B(StackCheck),
B(CreateBlockContext), U8(0),
B(PushContext), R(4),
B(LdaTheHole),
B(Star), R(8),
B(CreateClosure), U8(2), U8(0), U8(2),
B(Star), R(5),
B(LdaConstant), U8(1),
B(Star), R(6),
B(CreateClosure), U8(3), U8(1), U8(2),
B(StaCurrentContextSlot), U8(4),
B(Mov), R(5), R(7),
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
B(Star), R(6),
B(Mov), R(7), R(3),
B(LdaConstant), U8(4),
B(Star), R(7),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(7), U8(1),
B(StaCurrentContextSlot), U8(5),
B(PopContext), R(4),
B(Mov), R(3), R(0),
/* 38 E> */ B(CreateBlockContext), U8(5),
B(PushContext), R(4),
/* 83 E> */ B(CreateClosure), U8(7), U8(2), U8(2),
B(Star), R(5),
B(LdaConstant), U8(6),
B(Star), R(6),
B(CreateClosure), U8(8), U8(3), U8(2),
B(StaCurrentContextSlot), U8(4),
B(Mov), R(5), R(7),
B(Mov), R(3), R(8),
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
B(Star), R(6),
B(Mov), R(7), R(2),
B(LdaConstant), U8(9),
B(Star), R(7),
B(CallRuntime), U16(Runtime::kCreatePrivateNameSymbol), R(7), U8(1),
B(StaCurrentContextSlot), U8(5),
B(PopContext), R(4),
B(Mov), R(2), R(1),
/* 106 S> */ B(Ldar), R(3),
/* 106 E> */ B(Construct), R(3), R(0), U8(0), U8(0),
/* 115 S> */ B(Ldar), R(2),
/* 115 E> */ B(Construct), R(2), R(0), U8(0), U8(2),
B(LdaUndefined),
/* 124 S> */ B(Return),
]
constant pool: [
SCOPE_INFO_TYPE,
FIXED_ARRAY_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["D"],
SCOPE_INFO_TYPE,
FIXED_ARRAY_TYPE,
SHARED_FUNCTION_INFO_TYPE,
SHARED_FUNCTION_INFO_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["E"],
]
handlers: [
]
......@@ -2757,6 +2757,38 @@ TEST(PrivateClassFields) {
LoadGolden("PrivateClassFields.golden")));
}
TEST(PrivateMethods) {
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"
" #a() { return 1; }\n"
" }\n"
"\n"
" new A;\n"
"}\n",
"{\n"
" class D {\n"
" #d() {}\n"
" }\n"
"\n"
" class E extends D {\n"
" #e() {}\n"
" }\n"
"\n"
" new D;\n"
" new E;\n"
"}\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("PrivateMethods.golden")));
i::FLAG_harmony_private_methods = old_methods_flag;
}
TEST(StaticClassFields) {
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
......
// 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";
{
class C {
#a() {}
}
new C;
}
{
class C {
#a() {
class B {
#a() { }
}
new B;
}
}
new C;
}
{
class A {
#a() {
class C extends A {
#c() { }
}
new C;
}
}
new A;
}
{
const C = class {
#a() { }
}
new C;
}
{
const C = class {
#a() {
const B = class {
#a() { }
}
new B;
}
}
new C;
}
{
class A {
constructor(arg) {
return arg;
}
}
class C extends A {
#x() { }
constructor(arg) {
super(arg);
}
}
// Add the brand twice on the same object.
let c1 = new C({});
assertThrows(() => new C(c1), TypeError);
}
{
// TODO(v8:9177): test extending a class expression that does not have
// a private method.
class D extends class {
#c() {}
} {
#d() {}
}
class E extends D {
#e() {}
}
new D;
new E;
}
......@@ -306,49 +306,49 @@ KNOWN_MAPS = {
("read_only_space", 0x026e9): (98, "EnumCacheMap"),
("read_only_space", 0x02789): (115, "ArrayBoilerplateDescriptionMap"),
("read_only_space", 0x02ad9): (101, "InterceptorInfoMap"),
("read_only_space", 0x05141): (89, "AccessCheckInfoMap"),
("read_only_space", 0x05191): (90, "AccessorInfoMap"),
("read_only_space", 0x051e1): (91, "AccessorPairMap"),
("read_only_space", 0x05231): (92, "AliasedArgumentsEntryMap"),
("read_only_space", 0x05281): (93, "AllocationMementoMap"),
("read_only_space", 0x052d1): (94, "AsmWasmDataMap"),
("read_only_space", 0x05321): (95, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05371): (96, "ClassPositionsMap"),
("read_only_space", 0x053c1): (97, "DebugInfoMap"),
("read_only_space", 0x05411): (99, "FunctionTemplateInfoMap"),
("read_only_space", 0x05461): (100, "FunctionTemplateRareDataMap"),
("read_only_space", 0x054b1): (102, "InterpreterDataMap"),
("read_only_space", 0x05501): (103, "ModuleInfoEntryMap"),
("read_only_space", 0x05551): (104, "ModuleMap"),
("read_only_space", 0x055a1): (105, "ObjectTemplateInfoMap"),
("read_only_space", 0x055f1): (106, "PromiseCapabilityMap"),
("read_only_space", 0x05641): (107, "PromiseReactionMap"),
("read_only_space", 0x05691): (108, "PrototypeInfoMap"),
("read_only_space", 0x056e1): (109, "ScriptMap"),
("read_only_space", 0x05731): (110, "SourcePositionTableWithFrameCacheMap"),
("read_only_space", 0x05781): (111, "StackFrameInfoMap"),
("read_only_space", 0x057d1): (112, "StackTraceFrameMap"),
("read_only_space", 0x05821): (113, "Tuple2Map"),
("read_only_space", 0x05871): (114, "Tuple3Map"),
("read_only_space", 0x058c1): (116, "WasmCapiFunctionDataMap"),
("read_only_space", 0x05911): (117, "WasmDebugInfoMap"),
("read_only_space", 0x05961): (118, "WasmExceptionTagMap"),
("read_only_space", 0x059b1): (119, "WasmExportedFunctionDataMap"),
("read_only_space", 0x05a01): (120, "CallableTaskMap"),
("read_only_space", 0x05a51): (121, "CallbackTaskMap"),
("read_only_space", 0x05aa1): (122, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x05af1): (123, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05b41): (124, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x05b91): (125, "FinalizationGroupCleanupJobTaskMap"),
("read_only_space", 0x05be1): (126, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05c31): (126, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05c81): (161, "LoadHandler1Map"),
("read_only_space", 0x05cd1): (161, "LoadHandler2Map"),
("read_only_space", 0x05d21): (161, "LoadHandler3Map"),
("read_only_space", 0x05d71): (169, "StoreHandler0Map"),
("read_only_space", 0x05dc1): (169, "StoreHandler1Map"),
("read_only_space", 0x05e11): (169, "StoreHandler2Map"),
("read_only_space", 0x05e61): (169, "StoreHandler3Map"),
("read_only_space", 0x05159): (89, "AccessCheckInfoMap"),
("read_only_space", 0x051a9): (90, "AccessorInfoMap"),
("read_only_space", 0x051f9): (91, "AccessorPairMap"),
("read_only_space", 0x05249): (92, "AliasedArgumentsEntryMap"),
("read_only_space", 0x05299): (93, "AllocationMementoMap"),
("read_only_space", 0x052e9): (94, "AsmWasmDataMap"),
("read_only_space", 0x05339): (95, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05389): (96, "ClassPositionsMap"),
("read_only_space", 0x053d9): (97, "DebugInfoMap"),
("read_only_space", 0x05429): (99, "FunctionTemplateInfoMap"),
("read_only_space", 0x05479): (100, "FunctionTemplateRareDataMap"),
("read_only_space", 0x054c9): (102, "InterpreterDataMap"),
("read_only_space", 0x05519): (103, "ModuleInfoEntryMap"),
("read_only_space", 0x05569): (104, "ModuleMap"),
("read_only_space", 0x055b9): (105, "ObjectTemplateInfoMap"),
("read_only_space", 0x05609): (106, "PromiseCapabilityMap"),
("read_only_space", 0x05659): (107, "PromiseReactionMap"),
("read_only_space", 0x056a9): (108, "PrototypeInfoMap"),
("read_only_space", 0x056f9): (109, "ScriptMap"),
("read_only_space", 0x05749): (110, "SourcePositionTableWithFrameCacheMap"),
("read_only_space", 0x05799): (111, "StackFrameInfoMap"),
("read_only_space", 0x057e9): (112, "StackTraceFrameMap"),
("read_only_space", 0x05839): (113, "Tuple2Map"),
("read_only_space", 0x05889): (114, "Tuple3Map"),
("read_only_space", 0x058d9): (116, "WasmCapiFunctionDataMap"),
("read_only_space", 0x05929): (117, "WasmDebugInfoMap"),
("read_only_space", 0x05979): (118, "WasmExceptionTagMap"),
("read_only_space", 0x059c9): (119, "WasmExportedFunctionDataMap"),
("read_only_space", 0x05a19): (120, "CallableTaskMap"),
("read_only_space", 0x05a69): (121, "CallbackTaskMap"),
("read_only_space", 0x05ab9): (122, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x05b09): (123, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05b59): (124, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x05ba9): (125, "FinalizationGroupCleanupJobTaskMap"),
("read_only_space", 0x05bf9): (126, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05c49): (126, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05c99): (161, "LoadHandler1Map"),
("read_only_space", 0x05ce9): (161, "LoadHandler2Map"),
("read_only_space", 0x05d39): (161, "LoadHandler3Map"),
("read_only_space", 0x05d89): (169, "StoreHandler0Map"),
("read_only_space", 0x05dd9): (169, "StoreHandler1Map"),
("read_only_space", 0x05e29): (169, "StoreHandler2Map"),
("read_only_space", 0x05e79): (169, "StoreHandler3Map"),
("map_space", 0x00141): (1057, "ExternalMap"),
("map_space", 0x00191): (1073, "JSMessageObjectMap"),
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment