[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:
parent
93ea0a22c7
commit
44ee4a9fca
@ -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());
|
||||
|
@ -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);
|
||||
|
114
src/ast/ast.h
114
src/ast/ast.h
@ -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:
|
||||
|
@ -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());
|
||||
|
@ -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_--; }
|
||||
|
@ -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, \
|
||||
|
@ -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) \
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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(); }
|
||||
|
@ -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
|
||||
|
134
test/mjsunit/harmony/class-static-blocks.js
Normal file
134
test/mjsunit/harmony/class-static-blocks.js
Normal 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);
|
||||
}
|
@ -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"]
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user