[class] Implement class static blocks

Stage 3 proposal: https://github.com/tc39/proposal-class-static-block

Bug: v8:11375
Change-Id: I579adab4679cce0190b9d8bd814a7cd297ebfa15
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2699449
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72847}
This commit is contained in:
Shu-yu Guo 2021-02-17 15:40:14 -08:00 committed by Commit Bot
parent 93ea0a22c7
commit 44ee4a9fca
19 changed files with 505 additions and 137 deletions

View File

@ -40,8 +40,8 @@ void AstFunctionLiteralIdReindexer::VisitClassLiteral(ClassLiteral* expr) {
Visit(expr->extends());
}
Visit(expr->constructor());
if (expr->static_fields_initializer() != nullptr) {
Visit(expr->static_fields_initializer());
if (expr->static_initializer() != nullptr) {
Visit(expr->static_initializer());
}
if (expr->instance_members_initializer_function() != nullptr) {
Visit(expr->instance_members_initializer_function());

View File

@ -469,8 +469,8 @@ void AstTraversalVisitor<Subclass>::VisitClassLiteral(ClassLiteral* expr) {
RECURSE_EXPRESSION(Visit(expr->extends()));
}
RECURSE_EXPRESSION(Visit(expr->constructor()));
if (expr->static_fields_initializer() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->static_fields_initializer()));
if (expr->static_initializer() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->static_initializer()));
}
if (expr->instance_members_initializer_function() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->instance_members_initializer_function()));
@ -505,6 +505,29 @@ void AstTraversalVisitor<Subclass>::VisitInitializeClassMembersStatement(
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitInitializeClassStaticElementsStatement(
InitializeClassStaticElementsStatement* stmt) {
PROCESS_NODE(stmt);
ZonePtrList<ClassLiteral::StaticElement>* elements = stmt->elements();
for (int i = 0; i < elements->length(); ++i) {
ClassLiteral::StaticElement* element = elements->at(i);
switch (element->kind()) {
case ClassLiteral::StaticElement::PROPERTY: {
ClassLiteral::Property* prop = element->property();
if (!prop->key()->IsLiteral()) {
RECURSE(Visit(prop->key()));
}
RECURSE(Visit(prop->value()));
break;
}
case ClassLiteral::StaticElement::STATIC_BLOCK:
RECURSE(Visit(element->static_block()));
break;
}
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSpread(Spread* expr) {
PROCESS_EXPRESSION(expr);

View File

@ -54,21 +54,22 @@ namespace internal {
V(Block) \
V(SwitchStatement)
#define STATEMENT_NODE_LIST(V) \
ITERATION_NODE_LIST(V) \
BREAKABLE_NODE_LIST(V) \
V(ExpressionStatement) \
V(EmptyStatement) \
V(SloppyBlockFunctionStatement) \
V(IfStatement) \
V(ContinueStatement) \
V(BreakStatement) \
V(ReturnStatement) \
V(WithStatement) \
V(TryCatchStatement) \
V(TryFinallyStatement) \
V(DebuggerStatement) \
V(InitializeClassMembersStatement)
#define STATEMENT_NODE_LIST(V) \
ITERATION_NODE_LIST(V) \
BREAKABLE_NODE_LIST(V) \
V(ExpressionStatement) \
V(EmptyStatement) \
V(SloppyBlockFunctionStatement) \
V(IfStatement) \
V(ContinueStatement) \
V(BreakStatement) \
V(ReturnStatement) \
V(WithStatement) \
V(TryCatchStatement) \
V(TryFinallyStatement) \
V(DebuggerStatement) \
V(InitializeClassMembersStatement) \
V(InitializeClassStaticElementsStatement)
#define LITERAL_NODE_LIST(V) \
V(RegExpLiteral) \
@ -2366,6 +2367,40 @@ class ClassLiteralProperty final : public LiteralProperty {
Variable* private_or_computed_name_var_;
};
class ClassLiteralStaticElement final : public ZoneObject {
public:
enum Kind : uint8_t { PROPERTY, STATIC_BLOCK };
Kind kind() const { return kind_; }
ClassLiteralProperty* property() const {
DCHECK(kind() == PROPERTY);
return property_;
}
Block* static_block() const {
DCHECK(kind() == STATIC_BLOCK);
return static_block_;
}
private:
friend class AstNodeFactory;
friend Zone;
explicit ClassLiteralStaticElement(ClassLiteralProperty* property)
: kind_(PROPERTY), property_(property) {}
explicit ClassLiteralStaticElement(Block* static_block)
: kind_(STATIC_BLOCK), static_block_(static_block) {}
Kind kind_;
union {
ClassLiteralProperty* property_;
Block* static_block_;
};
};
class InitializeClassMembersStatement final : public Statement {
public:
using Property = ClassLiteralProperty;
@ -2382,9 +2417,28 @@ class InitializeClassMembersStatement final : public Statement {
ZonePtrList<Property>* fields_;
};
class InitializeClassStaticElementsStatement final : public Statement {
public:
using StaticElement = ClassLiteralStaticElement;
ZonePtrList<StaticElement>* elements() const { return elements_; }
private:
friend class AstNodeFactory;
friend Zone;
InitializeClassStaticElementsStatement(ZonePtrList<StaticElement>* elements,
int pos)
: Statement(pos, kInitializeClassStaticElementsStatement),
elements_(elements) {}
ZonePtrList<StaticElement>* elements_;
};
class ClassLiteral final : public Expression {
public:
using Property = ClassLiteralProperty;
using StaticElement = ClassLiteralStaticElement;
ClassScope* scope() const { return scope_; }
Expression* extends() const { return extends_; }
@ -2410,9 +2464,7 @@ class ClassLiteral final : public Expression {
return is_anonymous_expression();
}
FunctionLiteral* static_fields_initializer() const {
return static_fields_initializer_;
}
FunctionLiteral* static_initializer() const { return static_initializer_; }
FunctionLiteral* instance_members_initializer_function() const {
return instance_members_initializer_function_;
@ -2430,7 +2482,7 @@ class ClassLiteral final : public Expression {
FunctionLiteral* constructor,
ZonePtrList<Property>* public_members,
ZonePtrList<Property>* private_members,
FunctionLiteral* static_fields_initializer,
FunctionLiteral* static_initializer,
FunctionLiteral* instance_members_initializer_function,
int start_position, int end_position,
bool has_name_static_property, bool has_static_computed_names,
@ -2443,7 +2495,7 @@ class ClassLiteral final : public Expression {
constructor_(constructor),
public_members_(public_members),
private_members_(private_members),
static_fields_initializer_(static_fields_initializer),
static_initializer_(static_initializer),
instance_members_initializer_function_(
instance_members_initializer_function),
home_object_(home_object),
@ -2460,7 +2512,7 @@ class ClassLiteral final : public Expression {
FunctionLiteral* constructor_;
ZonePtrList<Property>* public_members_;
ZonePtrList<Property>* private_members_;
FunctionLiteral* static_fields_initializer_;
FunctionLiteral* static_initializer_;
FunctionLiteral* instance_members_initializer_function_;
using HasNameStaticProperty = Expression::NextBitField<bool, 1>;
using HasStaticComputedNames = HasNameStaticProperty::Next<bool, 1>;
@ -3174,11 +3226,21 @@ class AstNodeFactory final {
is_computed_name, is_private);
}
ClassLiteral::StaticElement* NewClassLiteralStaticElement(
ClassLiteral::Property* property) {
return zone_->New<ClassLiteral::StaticElement>(property);
}
ClassLiteral::StaticElement* NewClassLiteralStaticElement(
Block* static_block) {
return zone_->New<ClassLiteral::StaticElement>(static_block);
}
ClassLiteral* NewClassLiteral(
ClassScope* scope, Expression* extends, FunctionLiteral* constructor,
ZonePtrList<ClassLiteral::Property>* public_members,
ZonePtrList<ClassLiteral::Property>* private_members,
FunctionLiteral* static_fields_initializer,
FunctionLiteral* static_initializer,
FunctionLiteral* instance_members_initializer_function,
int start_position, int end_position, bool has_name_static_property,
bool has_static_computed_names, bool is_anonymous,
@ -3186,7 +3248,7 @@ class AstNodeFactory final {
Variable* static_home_object) {
return zone_->New<ClassLiteral>(
scope, extends, constructor, public_members, private_members,
static_fields_initializer, instance_members_initializer_function,
static_initializer, instance_members_initializer_function,
start_position, end_position, has_name_static_property,
has_static_computed_names, is_anonymous, has_private_methods,
home_object, static_home_object);
@ -3242,6 +3304,12 @@ class AstNodeFactory final {
return zone_->New<InitializeClassMembersStatement>(args, pos);
}
InitializeClassStaticElementsStatement*
NewInitializeClassStaticElementsStatement(
ZonePtrList<ClassLiteral::StaticElement>* args, int pos) {
return zone_->New<InitializeClassStaticElementsStatement>(args, pos);
}
Zone* zone() const { return zone_; }
private:

View File

@ -235,6 +235,18 @@ void CallPrinter::VisitInitializeClassMembersStatement(
}
}
void CallPrinter::VisitInitializeClassStaticElementsStatement(
InitializeClassStaticElementsStatement* node) {
for (int i = 0; i < node->elements()->length(); i++) {
ClassLiteral::StaticElement* element = node->elements()->at(i);
if (element->kind() == ClassLiteral::StaticElement::PROPERTY) {
Find(element->property()->value());
} else {
Find(element->static_block());
}
}
}
void CallPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {}
@ -1086,9 +1098,8 @@ void AstPrinter::VisitClassLiteral(ClassLiteral* node) {
PrintLiteralWithModeIndented("BRAND", brand, brand->raw_name());
}
}
if (node->static_fields_initializer() != nullptr) {
PrintIndentedVisit("STATIC FIELDS INITIALIZER",
node->static_fields_initializer());
if (node->static_initializer() != nullptr) {
PrintIndentedVisit("STATIC INITIALIZER", node->static_initializer());
}
if (node->instance_members_initializer_function() != nullptr) {
PrintIndentedVisit("INSTANCE MEMBERS INITIALIZER",
@ -1104,34 +1115,58 @@ void AstPrinter::VisitInitializeClassMembersStatement(
PrintClassProperties(node->fields());
}
void AstPrinter::VisitInitializeClassStaticElementsStatement(
InitializeClassStaticElementsStatement* node) {
IndentedScope indent(this, "INITIALIZE CLASS STATIC ELEMENTS",
node->position());
PrintClassStaticElements(node->elements());
}
void AstPrinter::PrintClassProperty(ClassLiteral::Property* property) {
const char* prop_kind = nullptr;
switch (property->kind()) {
case ClassLiteral::Property::METHOD:
prop_kind = "METHOD";
break;
case ClassLiteral::Property::GETTER:
prop_kind = "GETTER";
break;
case ClassLiteral::Property::SETTER:
prop_kind = "SETTER";
break;
case ClassLiteral::Property::FIELD:
prop_kind = "FIELD";
break;
}
EmbeddedVector<char, 128> buf;
SNPrintF(buf, "PROPERTY%s%s - %s", property->is_static() ? " - STATIC" : "",
property->is_private() ? " - PRIVATE" : " - PUBLIC", prop_kind);
IndentedScope prop(this, buf.begin());
PrintIndentedVisit("KEY", property->key());
PrintIndentedVisit("VALUE", property->value());
}
void AstPrinter::PrintClassProperties(
const ZonePtrList<ClassLiteral::Property>* properties) {
for (int i = 0; i < properties->length(); i++) {
ClassLiteral::Property* property = properties->at(i);
const char* prop_kind = nullptr;
switch (property->kind()) {
case ClassLiteral::Property::METHOD:
prop_kind = "METHOD";
break;
case ClassLiteral::Property::GETTER:
prop_kind = "GETTER";
break;
case ClassLiteral::Property::SETTER:
prop_kind = "SETTER";
break;
case ClassLiteral::Property::FIELD:
prop_kind = "FIELD";
break;
}
EmbeddedVector<char, 128> buf;
SNPrintF(buf, "PROPERTY%s%s - %s", property->is_static() ? " - STATIC" : "",
property->is_private() ? " - PRIVATE" : " - PUBLIC", prop_kind);
IndentedScope prop(this, buf.begin());
PrintIndentedVisit("KEY", properties->at(i)->key());
PrintIndentedVisit("VALUE", properties->at(i)->value());
PrintClassProperty(properties->at(i));
}
}
void AstPrinter::PrintClassStaticElements(
const ZonePtrList<ClassLiteral::StaticElement>* static_elements) {
for (int i = 0; i < static_elements->length(); i++) {
ClassLiteral::StaticElement* element = static_elements->at(i);
switch (element->kind()) {
case ClassLiteral::StaticElement::PROPERTY:
PrintClassProperty(element->property());
break;
case ClassLiteral::StaticElement::STATIC_BLOCK:
PrintIndentedVisit("STATIC BLOCK", element->static_block());
break;
}
}
}
void AstPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {
IndentedScope indent(this, "NATIVE FUNC LITERAL", node->position());

View File

@ -133,8 +133,11 @@ class AstPrinter final : public AstVisitor<AstPrinter> {
const char* prefix = "");
void PrintObjectProperties(
const ZonePtrList<ObjectLiteral::Property>* properties);
void PrintClassProperty(ClassLiteral::Property* property);
void PrintClassProperties(
const ZonePtrList<ClassLiteral::Property>* properties);
void PrintClassStaticElements(
const ZonePtrList<ClassLiteral::StaticElement>* static_elements);
void inc_indent() { indent_++; }
void dec_indent() { indent_--; }

View File

@ -27,8 +27,9 @@ namespace internal {
T(ApplyNonFunction, \
"Function.prototype.apply was called on %, which is a % and not a " \
"function") \
T(ArgumentsDisallowedInInitializer, \
"'arguments' is not allowed in class field initializer") \
T(ArgumentsDisallowedInInitializerAndStaticBlock, \
"'arguments' is not allowed in class field initializer or static " \
"initialization block") \
T(ArrayBufferTooShort, \
"Derived ArrayBuffer constructor created a buffer which was too small") \
T(ArrayBufferSpeciesThis, \

View File

@ -249,7 +249,8 @@ DEFINE_IMPLICATION(harmony_weak_refs_with_cleanup_some, harmony_weak_refs)
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
V(harmony_weak_refs_with_cleanup_some, \
"harmony weak references with FinalizationRegistry.prototype.cleanupSome") \
V(harmony_import_assertions, "harmony import assertions")
V(harmony_import_assertions, "harmony import assertions") \
V(harmony_class_static_blocks, "harmony static initializer blocks")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \

View File

@ -4317,6 +4317,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_top_level_await)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_logical_assignment)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_assertions)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_private_brand_checks)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_static_blocks)
#ifdef V8_INTL_SUPPORT
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_displaynames_date_types)

View File

@ -2441,7 +2441,7 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
.LoadAccumulatorWithRegister(class_constructor);
}
if (expr->static_fields_initializer() != nullptr) {
if (expr->static_initializer() != nullptr) {
// TODO(gsathya): This can be optimized away to be a part of the
// class boilerplate in the future. The name argument can be
// passed to the DefineClass runtime function and have it set
@ -2461,8 +2461,7 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
}
RegisterList args = register_allocator()->NewRegisterList(1);
Register initializer =
VisitForRegisterValue(expr->static_fields_initializer());
Register initializer = VisitForRegisterValue(expr->static_initializer());
builder()
->MoveRegister(class_constructor, args[0])
@ -2488,46 +2487,64 @@ void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr, Register name) {
}
}
void BytecodeGenerator::VisitInitializeClassMembersStatement(
InitializeClassMembersStatement* stmt) {
void BytecodeGenerator::BuildClassProperty(ClassLiteral::Property* property) {
RegisterAllocationScope register_scope(this);
RegisterList args = register_allocator()->NewRegisterList(3);
Register constructor = args[0], key = args[1], value = args[2];
builder()->MoveRegister(builder()->Receiver(), constructor);
// Private methods are not initialized in BuildClassProperty.
DCHECK_IMPLIES(property->is_private(),
property->kind() == ClassLiteral::Property::FIELD);
if (property->is_computed_name()) {
DCHECK_EQ(property->kind(), ClassLiteral::Property::FIELD);
DCHECK(!property->is_private());
Variable* var = property->computed_name_var();
DCHECK_NOT_NULL(var);
// The computed name is already evaluated and stored in a variable at class
// definition time.
BuildVariableLoad(var, HoleCheckMode::kElided);
builder()->StoreAccumulatorInRegister(key);
} 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);
builder()->StoreAccumulatorInRegister(key);
} else {
BuildLoadPropertyKey(property, key);
}
builder()->SetExpressionAsStatementPosition(property->value());
VisitForRegisterValue(property->value(), value);
Runtime::FunctionId function_id =
property->kind() == ClassLiteral::Property::FIELD &&
!property->is_private()
? Runtime::kCreateDataProperty
: Runtime::kAddPrivateField;
builder()->CallRuntime(function_id, args);
}
void BytecodeGenerator::VisitInitializeClassMembersStatement(
InitializeClassMembersStatement* stmt) {
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);
BuildClassProperty(stmt->fields()->at(i));
}
}
if (property->is_computed_name()) {
DCHECK_EQ(property->kind(), ClassLiteral::Property::FIELD);
DCHECK(!property->is_private());
Variable* var = property->computed_name_var();
DCHECK_NOT_NULL(var);
// The computed name is already evaluated and stored in a
// variable at class definition time.
BuildVariableLoad(var, HoleCheckMode::kElided);
builder()->StoreAccumulatorInRegister(key);
} 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);
builder()->StoreAccumulatorInRegister(key);
} else {
BuildLoadPropertyKey(property, key);
void BytecodeGenerator::VisitInitializeClassStaticElementsStatement(
InitializeClassStaticElementsStatement* stmt) {
for (int i = 0; i < stmt->elements()->length(); i++) {
ClassLiteral::StaticElement* element = stmt->elements()->at(i);
switch (element->kind()) {
case ClassLiteral::StaticElement::PROPERTY:
BuildClassProperty(element->property());
break;
case ClassLiteral::StaticElement::STATIC_BLOCK:
VisitBlock(element->static_block());
break;
}
builder()->SetExpressionAsStatementPosition(property->value());
VisitForRegisterValue(property->value(), value);
Runtime::FunctionId function_id =
property->kind() == ClassLiteral::Property::FIELD &&
!property->is_private()
? Runtime::kCreateDataProperty
: Runtime::kAddPrivateField;
builder()->CallRuntime(function_id, args);
}
}
@ -2876,7 +2893,7 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
// the class, meaning we can't wait until the
// StoreDataPropertyInLiteral call later to set the name.
if (property->value()->IsClassLiteral() &&
property->value()->AsClassLiteral()->static_fields_initializer() !=
property->value()->AsClassLiteral()->static_initializer() !=
nullptr) {
value = register_allocator()->NewRegister();
VisitClassLiteral(property->value()->AsClassLiteral(), key);

View File

@ -319,6 +319,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
Register value);
void BuildPrivateMethods(ClassLiteral* expr, bool is_static,
Register home_object);
void BuildClassProperty(ClassLiteral::Property* property);
void BuildClassLiteral(ClassLiteral* expr, Register name);
void VisitClassLiteral(ClassLiteral* expr, Register name);
void VisitNewTargetVariable(Variable* variable);

View File

@ -58,10 +58,10 @@ enum FunctionKind : uint8_t {
kConciseMethod,
kStaticConciseMethod,
kClassMembersInitializerFunction,
kStaticClassMembersInitializerFunction,
kClassStaticInitializerFunction,
// END concise methods 2
kLastFunctionKind = kStaticClassMembersInitializerFunction,
kLastFunctionKind = kClassStaticInitializerFunction,
};
constexpr int kFunctionKindBitSize = 5;
@ -104,7 +104,7 @@ inline bool IsConciseMethod(FunctionKind kind) {
return base::IsInRange(kind, FunctionKind::kAsyncConciseMethod,
FunctionKind::kStaticAsyncConciseGeneratorMethod) ||
base::IsInRange(kind, FunctionKind::kConciseGeneratorMethod,
FunctionKind::kStaticClassMembersInitializerFunction);
FunctionKind::kClassStaticInitializerFunction);
}
inline bool IsStrictFunctionWithoutPrototype(FunctionKind kind) {
@ -113,7 +113,7 @@ inline bool IsStrictFunctionWithoutPrototype(FunctionKind kind) {
base::IsInRange(kind, FunctionKind::kAsyncConciseMethod,
FunctionKind::kStaticAsyncConciseGeneratorMethod) ||
base::IsInRange(kind, FunctionKind::kConciseGeneratorMethod,
FunctionKind::kStaticClassMembersInitializerFunction);
FunctionKind::kClassStaticInitializerFunction);
}
inline bool IsGetterFunction(FunctionKind kind) {
@ -153,7 +153,7 @@ inline bool IsClassConstructor(FunctionKind kind) {
inline bool IsClassMembersInitializerFunction(FunctionKind kind) {
return base::IsInRange(kind, FunctionKind::kClassMembersInitializerFunction,
FunctionKind::kStaticClassMembersInitializerFunction);
FunctionKind::kClassStaticInitializerFunction);
}
inline bool IsConstructable(FunctionKind kind) {
@ -169,7 +169,7 @@ inline bool IsStatic(FunctionKind kind) {
case FunctionKind::kStaticConciseGeneratorMethod:
case FunctionKind::kStaticAsyncConciseMethod:
case FunctionKind::kStaticAsyncConciseGeneratorMethod:
case FunctionKind::kStaticClassMembersInitializerFunction:
case FunctionKind::kClassStaticInitializerFunction:
return true;
default:
return false;
@ -213,8 +213,8 @@ inline const char* FunctionKind2String(FunctionKind kind) {
return "AsyncModule";
case FunctionKind::kClassMembersInitializerFunction:
return "ClassMembersInitializerFunction";
case FunctionKind::kStaticClassMembersInitializerFunction:
return "StaticClassMembersInitializerFunction";
case FunctionKind::kClassStaticInitializerFunction:
return "ClassStaticInitializerFunction";
case FunctionKind::kDefaultBaseConstructor:
return "DefaultBaseConstructor";
case FunctionKind::kDefaultDerivedConstructor:

View File

@ -214,6 +214,7 @@ class ParserBase {
using BreakableStatementT = typename Types::BreakableStatement;
using ClassLiteralPropertyT = typename Types::ClassLiteralProperty;
using ClassPropertyListT = typename Types::ClassPropertyList;
using ClassStaticElementListT = typename Types::ClassStaticElementList;
using ExpressionT = typename Types::Expression;
using ExpressionListT = typename Types::ExpressionList;
using FormalParametersT = typename Types::FormalParameters;
@ -589,38 +590,40 @@ class ParserBase {
: extends(parser->impl()->NullExpression()),
public_members(parser->impl()->NewClassPropertyList(4)),
private_members(parser->impl()->NewClassPropertyList(4)),
static_fields(parser->impl()->NewClassPropertyList(4)),
static_elements(parser->impl()->NewClassStaticElementList(4)),
instance_fields(parser->impl()->NewClassPropertyList(4)),
constructor(parser->impl()->NullExpression()),
has_seen_constructor(false),
has_name_static_property(false),
has_static_computed_names(false),
has_static_class_fields(false),
has_static_elements(false),
has_static_private_methods(false),
has_static_blocks(false),
has_instance_members(false),
requires_brand(false),
is_anonymous(false),
has_private_methods(false),
static_fields_scope(nullptr),
static_elements_scope(nullptr),
instance_members_scope(nullptr),
computed_field_count(0) {}
ExpressionT extends;
ClassPropertyListT public_members;
ClassPropertyListT private_members;
ClassPropertyListT static_fields;
ClassStaticElementListT static_elements;
ClassPropertyListT instance_fields;
FunctionLiteralT constructor;
bool has_seen_constructor;
bool has_name_static_property;
bool has_static_computed_names;
bool has_static_class_fields;
bool has_static_elements;
bool has_static_private_methods;
bool has_static_blocks;
bool has_instance_members;
bool requires_brand;
bool is_anonymous;
bool has_private_methods;
DeclarationScope* static_fields_scope;
DeclarationScope* static_elements_scope;
DeclarationScope* instance_members_scope;
int computed_field_count;
Variable* home_object_variable = nullptr;
@ -1057,6 +1060,10 @@ class ParserBase {
bool is_resumable() const {
return IsResumableFunction(function_state_->kind());
}
bool is_class_static_block() const {
return function_state_->kind() ==
FunctionKind::kClassStaticInitializerFunction;
}
bool is_await_allowed() const {
return is_async_function() || (flags().allow_harmony_top_level_await() &&
IsModule(function_state_->kind()));
@ -1176,6 +1183,7 @@ class ParserBase {
bool* has_seen_constructor);
ExpressionT ParseMemberInitializer(ClassInfo* class_info, int beg_pos,
bool is_static);
BlockT ParseClassStaticBlock(ClassInfo* class_info);
ObjectLiteralPropertyT ParseObjectPropertyDefinition(
ParsePropertyInfo* prop_info, bool* has_seen_proto);
void ParseArguments(
@ -1295,6 +1303,8 @@ class ParserBase {
StatementT ParseStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
AllowLabelledFunctionStatement allow_function);
BlockT ParseBlock(ZonePtrList<const AstRawString>* labels,
Scope* block_scope);
BlockT ParseBlock(ZonePtrList<const AstRawString>* labels);
// Parse a SubStatement in strict mode, or with an extra block scope in
@ -1633,14 +1643,16 @@ ParserBase<Impl>::ParseAndClassifyIdentifier(Token::Value next) {
IdentifierT name = impl()->GetIdentifier();
if (V8_UNLIKELY(impl()->IsArguments(name) &&
scope()->ShouldBanArguments())) {
ReportMessage(MessageTemplate::kArgumentsDisallowedInInitializer);
ReportMessage(
MessageTemplate::kArgumentsDisallowedInInitializerAndStaticBlock);
return impl()->EmptyIdentifierString();
}
return name;
}
if (!Token::IsValidIdentifier(next, language_mode(), is_generator(),
flags().is_module() || is_async_function())) {
flags().is_module() || is_async_function() ||
is_class_static_block())) {
ReportUnexpectedToken(next);
return impl()->EmptyIdentifierString();
}
@ -2433,10 +2445,10 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
ClassInfo* class_info, int beg_pos, bool is_static) {
FunctionParsingScope body_parsing_scope(impl());
DeclarationScope* initializer_scope =
is_static ? class_info->static_fields_scope
is_static ? class_info->static_elements_scope
: class_info->instance_members_scope;
FunctionKind function_kind =
is_static ? FunctionKind::kStaticClassMembersInitializerFunction
is_static ? FunctionKind::kClassStaticInitializerFunction
: FunctionKind::kClassMembersInitializerFunction;
if (initializer_scope == nullptr) {
@ -2459,8 +2471,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
initializer_scope->set_end_position(end_position());
if (is_static) {
class_info->static_fields_scope = initializer_scope;
class_info->has_static_class_fields = true;
class_info->static_elements_scope = initializer_scope;
class_info->has_static_elements = true;
} else {
class_info->instance_members_scope = initializer_scope;
class_info->has_instance_members = true;
@ -2469,6 +2481,32 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
return initializer;
}
template <typename Impl>
typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseClassStaticBlock(
ClassInfo* class_info) {
Consume(Token::STATIC);
DeclarationScope* initializer_scope = class_info->static_elements_scope;
if (initializer_scope == nullptr) {
initializer_scope =
NewFunctionScope(FunctionKind::kClassStaticInitializerFunction);
initializer_scope->set_start_position(position());
initializer_scope->SetLanguageMode(LanguageMode::kStrict);
class_info->static_elements_scope = initializer_scope;
}
FunctionState initializer_state(&function_state_, &scope_, initializer_scope);
AcceptINScope accept_in(this, true);
// Each static block has its own var and lexical scope, so make a new var
// block scope instead of using the synthetic members initializer function
// scope.
BlockT static_block = ParseBlock(nullptr, NewVarblockScope());
initializer_scope->set_end_position(end_position());
class_info->has_static_elements = true;
return static_block;
}
template <typename Impl>
typename ParserBase<Impl>::ObjectLiteralPropertyT
ParserBase<Impl>::ParseObjectPropertyDefinition(ParsePropertyInfo* prop_info,
@ -4623,12 +4661,22 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
const bool has_extends = !impl()->IsNull(class_info.extends);
while (peek() != Token::RBRACE) {
if (Check(Token::SEMICOLON)) continue;
// Either we're parsing a `static { }` initialization block or a property.
if (FLAG_harmony_class_static_blocks && peek() == Token::STATIC &&
PeekAhead() == Token::LBRACE) {
BlockT static_block = ParseClassStaticBlock(&class_info);
impl()->AddClassStaticBlock(static_block, &class_info);
continue;
}
FuncNameInferrerState fni_state(&fni_);
// If we haven't seen the constructor yet, it potentially is the next
// property.
bool is_constructor = !class_info.has_seen_constructor;
ParsePropertyInfo prop_info(this);
prop_info.position = PropertyPosition::kClassLiteral;
ClassLiteralPropertyT property =
ParseClassPropertyDefinition(&class_info, &prop_info, has_extends);
@ -5196,7 +5244,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
template <typename Impl>
typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
ZonePtrList<const AstRawString>* labels) {
ZonePtrList<const AstRawString>* labels, Scope* block_scope) {
// Block ::
// '{' StatementList '}'
@ -5207,7 +5255,7 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
CheckStackOverflow();
{
BlockState block_state(zone(), &scope_);
BlockState block_state(&scope_, block_scope);
scope()->set_start_position(peek_position());
Target target(this, body, labels, nullptr, Target::TARGET_FOR_NAMED_ONLY);
@ -5233,6 +5281,12 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
return body;
}
template <typename Impl>
typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
ZonePtrList<const AstRawString>* labels) {
return ParseBlock(labels, NewScope(BLOCK_SCOPE));
}
template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseScopedStatement(
ZonePtrList<const AstRawString>* labels) {

View File

@ -3026,7 +3026,8 @@ void Parser::DeclarePublicClassField(ClassScope* scope,
bool is_static, bool is_computed_name,
ClassInfo* class_info) {
if (is_static) {
class_info->static_fields->Add(property, zone());
class_info->static_elements->Add(
factory()->NewClassLiteralStaticElement(property), zone());
} else {
class_info->instance_fields->Add(property, zone());
}
@ -3049,7 +3050,8 @@ void Parser::DeclarePrivateClassMember(ClassScope* scope,
bool is_static, ClassInfo* class_info) {
if (kind == ClassLiteralProperty::Kind::FIELD) {
if (is_static) {
class_info->static_fields->Add(property, zone());
class_info->static_elements->Add(
factory()->NewClassLiteralStaticElement(property), zone());
} else {
class_info->instance_fields->Add(property, zone());
}
@ -3089,15 +3091,18 @@ void Parser::DeclarePublicClassMethod(const AstRawString* class_name,
class_info->public_members->Add(property, zone());
}
void Parser::AddClassStaticBlock(Block* block, ClassInfo* class_info) {
DCHECK(class_info->has_static_elements);
class_info->static_elements->Add(
factory()->NewClassLiteralStaticElement(block), zone());
}
FunctionLiteral* Parser::CreateInitializerFunction(
const char* name, DeclarationScope* scope,
ZonePtrList<ClassLiteral::Property>* fields) {
const char* name, DeclarationScope* scope, Statement* initializer_stmt) {
DCHECK(IsClassMembersInitializerFunction(scope->function_kind()));
// function() { .. class fields initializer .. }
ScopedPtrList<Statement> statements(pointer_buffer());
InitializeClassMembersStatement* stmt =
factory()->NewInitializeClassMembersStatement(fields, kNoSourcePosition);
statements.Add(stmt);
statements.Add(initializer_stmt);
FunctionLiteral* result = factory()->NewFunctionLiteral(
ast_value_factory()->GetOneByteString(name), scope, statements, 0, 0, 0,
FunctionLiteral::kNoDuplicateParameters,
@ -3138,18 +3143,20 @@ Expression* Parser::RewriteClassLiteral(ClassScope* block_scope,
block_scope->class_variable()->set_initializer_position(end_pos);
}
FunctionLiteral* static_fields_initializer = nullptr;
if (class_info->has_static_class_fields) {
static_fields_initializer = CreateInitializerFunction(
"<static_fields_initializer>", class_info->static_fields_scope,
class_info->static_fields);
FunctionLiteral* static_initializer = nullptr;
if (class_info->has_static_elements) {
static_initializer = CreateInitializerFunction(
"<static_initializer>", class_info->static_elements_scope,
factory()->NewInitializeClassStaticElementsStatement(
class_info->static_elements, kNoSourcePosition));
}
FunctionLiteral* instance_members_initializer_function = nullptr;
if (class_info->has_instance_members) {
instance_members_initializer_function = CreateInitializerFunction(
"<instance_members_initializer>", class_info->instance_members_scope,
class_info->instance_fields);
factory()->NewInitializeClassMembersStatement(
class_info->instance_fields, kNoSourcePosition));
class_info->constructor->set_requires_instance_members_initializer(true);
class_info->constructor->add_expected_properties(
class_info->instance_fields->length());
@ -3164,8 +3171,8 @@ Expression* Parser::RewriteClassLiteral(ClassScope* block_scope,
ClassLiteral* class_literal = factory()->NewClassLiteral(
block_scope, class_info->extends, class_info->constructor,
class_info->public_members, class_info->private_members,
static_fields_initializer, instance_members_initializer_function, pos,
end_pos, class_info->has_name_static_property,
static_initializer, instance_members_initializer_function, pos, end_pos,
class_info->has_name_static_property,
class_info->has_static_computed_names, class_info->is_anonymous,
class_info->has_private_methods, class_info->home_object_variable,
class_info->static_home_object_variable);

View File

@ -103,7 +103,9 @@ struct ParserTypes<Parser> {
using Block = v8::internal::Block*;
using BreakableStatement = v8::internal::BreakableStatement*;
using ClassLiteralProperty = ClassLiteral::Property*;
using ClassLiteralStaticElement = ClassLiteral::StaticElement*;
using ClassPropertyList = ZonePtrList<ClassLiteral::Property>*;
using ClassStaticElementList = ZonePtrList<ClassLiteral::StaticElement>*;
using Expression = v8::internal::Expression*;
using ExpressionList = ScopedPtrList<v8::internal::Expression>;
using FormalParameters = ParserFormalParameters;
@ -313,9 +315,9 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Variable* CreatePrivateNameVariable(ClassScope* scope, VariableMode mode,
IsStaticFlag is_static_flag,
const AstRawString* name);
FunctionLiteral* CreateInitializerFunction(
const char* name, DeclarationScope* scope,
ZonePtrList<ClassLiteral::Property>* fields);
FunctionLiteral* CreateInitializerFunction(const char* name,
DeclarationScope* scope,
Statement* initializer_stmt);
bool IdentifierEquals(const AstRawString* identifier,
const AstRawString* other) {
@ -347,6 +349,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
const AstRawString* property_name, bool is_static,
bool is_computed_name, bool is_private,
ClassInfo* class_info);
void AddClassStaticBlock(Block* block, ClassInfo* class_info);
Expression* RewriteClassLiteral(ClassScope* block_scope,
const AstRawString* name,
ClassInfo* class_info, int pos, int end_pos);
@ -842,6 +845,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
int size) const {
return zone()->New<ZonePtrList<ClassLiteral::Property>>(size, zone());
}
V8_INLINE ZonePtrList<ClassLiteral::StaticElement>* NewClassStaticElementList(
int size) const {
return zone()->New<ZonePtrList<ClassLiteral::StaticElement>>(size, zone());
}
V8_INLINE ZonePtrList<Statement>* NewStatementList(int size) const {
return zone()->New<ZonePtrList<Statement>>(size, zone());
}

View File

@ -876,6 +876,7 @@ struct ParserTypes<PreParser> {
// Return types for traversing functions.
using ClassLiteralProperty = PreParserExpression;
using ClassLiteralStaticElement = PreParserExpression;
using Expression = PreParserExpression;
using FunctionLiteral = PreParserExpression;
using ObjectLiteralProperty = PreParserExpression;
@ -885,6 +886,7 @@ struct ParserTypes<PreParser> {
using FormalParameters = PreParserFormalParameters;
using Identifier = PreParserIdentifier;
using ClassPropertyList = PreParserPropertyList;
using ClassStaticElementList = PreParserPropertyList;
using StatementList = PreParserScopedStatementList;
using Block = PreParserBlock;
using BreakableStatement = PreParserStatement;
@ -1239,6 +1241,11 @@ class PreParser : public ParserBase<PreParser> {
}
}
V8_INLINE void AddClassStaticBlock(PreParserBlock block,
ClassInfo* class_info) {
DCHECK(class_info->has_static_elements);
}
V8_INLINE PreParserExpression
RewriteClassLiteral(ClassScope* scope, const PreParserIdentifier& name,
ClassInfo* class_info, int pos, int end_pos) {
@ -1260,7 +1267,7 @@ class PreParser : public ParserBase<PreParser> {
FunctionState function_state(&function_state_, &scope_, function_scope);
GetNextFunctionLiteralId();
}
if (class_info->has_static_class_fields) {
if (class_info->has_static_elements) {
GetNextFunctionLiteralId();
}
if (class_info->has_instance_members) {
@ -1601,6 +1608,10 @@ class PreParser : public ParserBase<PreParser> {
return PreParserPropertyList();
}
V8_INLINE PreParserPropertyList NewClassStaticElementList(int size) const {
return PreParserPropertyList();
}
V8_INLINE PreParserStatementList NewStatementList(int size) const {
return PreParserStatementList();
}

View File

@ -360,6 +360,11 @@ void Processor::VisitInitializeClassMembersStatement(
replacement_ = node;
}
void Processor::VisitInitializeClassStaticElementsStatement(
InitializeClassStaticElementsStatement* node) {
replacement_ = node;
}
// Expressions are never visited.
#define DEF_VISIT(type) \
void Processor::Visit##type(type* expr) { UNREACHABLE(); }

View File

@ -2,5 +2,5 @@
static x = foo();
^
ReferenceError: foo is not defined
at Function.<static_fields_initializer> (*%(basename)s:8:14)
at *%(basename)s:1:1
at Function.<static_initializer> (*%(basename)s:8:14)
at *%(basename)s:1:1

View File

@ -0,0 +1,134 @@
// Copyright 2021 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-class-static-blocks
{
// Basic functionality
let log = [];
class C {
static { log.push("block1"); }
static { log.push("block2"); }
}
assertArrayEquals(["block1", "block2"], log);
}
{
// Static blocks run in textual order interleaved with field initializers.
let log = [];
class C {
static { log.push("block1"); }
static public_static_method() {}
static public_field = log.push("public_field");
static { log.push("block2"); }
static #private_field = log.push("private_field");
static { log.push("block3"); }
}
assertArrayEquals(["block1",
"public_field",
"block2",
"private_field",
"block3"], log);
}
{
// Static blocks have access to private fields.
let exfil;
class C {
#foo;
constructor(x) { this.#foo = x; }
static {
exfil = function(o) { return o.#foo; };
}
}
assertEquals(exfil(new C(42)), 42);
}
{
// 'this' is the constructor.
let log = [];
class C {
static x = 42;
static {
log.push(this.x);
}
}
assertArrayEquals([42], log);
}
{
// super.property accesses work as expected.
let log = [];
class B {
static foo = 42;
static get field_getter() { return "field_getter"; }
static set field_setter(x) { log.push(x); };
static method() { return "bar"; }
}
class C extends B {
static {
log.push(super.foo);
log.push(super.field_getter);
super.field_setter = "C";
log.push(super.method());
}
}
assertArrayEquals([42, "field_getter", "C", "bar"], log);
}
{
// Each static block is its own var and let scope.
let log = [];
let f;
class C {
static {
var x = "x1";
let y = "y1";
log.push(x);
log.push(y);
}
static {
var x = "x2";
let y = "y2";
f = () => [x, y];
}
static {
assertThrows(() => x, ReferenceError);
assertThrows(() => y, ReferenceError);
}
}
assertArrayEquals(["x1", "y1"], log);
assertArrayEquals(["x2", "y2"], f());
}
{
// new.target is undefined.
let log = [];
class C {
static {
log.push(new.target);
}
}
assertArrayEquals([undefined], log);
}
function assertDoesntParse(expr, context_start, context_end) {
assertThrows(() => {
eval(`${context_start} class C { static { ${expr} } } ${context_end}`);
}, SyntaxError);
}
for (let [s, e] of [['', ''],
['function* g() {', '}'],
['async function af() {', '}'],
['async function* ag() {', '}']]) {
assertDoesntParse('arguments;', s, e);
assertDoesntParse('arguments[0] = 42;', s, e);
assertDoesntParse('super();', s, e);
assertDoesntParse('yield 42;', s, e);
assertDoesntParse('await 42;', s, e);
// 'await' is disallowed as an identifier.
assertDoesntParse('let await;', s, e);
assertDoesntParse('await;', s, e);
}

View File

@ -43,13 +43,13 @@ function testClassConstruction() {
// ReferenceError: FAIL is not defined
// at thrower
// at <static_fields_initializer>
// at <static_initializer>
// at testClassConstruction
// at testTrace
testTrace(
"during class construction",
testClassConstruction,
["thrower", "<static_fields_initializer>"],
["thrower", "<static_initializer>"],
["anonymous"]
);