diff --git a/src/ast/ast.h b/src/ast/ast.h index b4218c5988..644dcb483b 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -2626,6 +2626,21 @@ class FunctionLiteral final : public Expression { int yield_count() { return yield_count_; } void set_yield_count(int yield_count) { yield_count_ = yield_count; } + bool requires_class_field_init() { + return RequiresClassFieldInit::decode(bitfield_); + } + void set_requires_class_field_init(bool requires_class_field_init) { + bitfield_ = + RequiresClassFieldInit::update(bitfield_, requires_class_field_init); + } + bool is_class_field_initializer() { + return IsClassFieldInitializer::decode(bitfield_); + } + void set_is_class_field_initializer(bool is_class_field_initializer) { + bitfield_ = + IsClassFieldInitializer::update(bitfield_, is_class_field_initializer); + } + private: friend class AstNodeFactory; @@ -2655,21 +2670,23 @@ class FunctionLiteral final : public Expression { kHasDuplicateParameters) | IsFunction::encode(is_function) | ShouldEagerCompile::encode(eager_compile_hint == kShouldEagerCompile) | + RequiresClassFieldInit::encode(false) | + IsClassFieldInitializer::encode(false) | FunctionKindBits::encode(kind) | ShouldBeUsedOnceHint::encode(false); DCHECK(IsValidFunctionKind(kind)); } - class FunctionTypeBits : public BitField16 {}; - class Pretenure : public BitField16 {}; - class HasDuplicateParameters : public BitField16 {}; - class IsFunction : public BitField16 {}; - class ShouldEagerCompile : public BitField16 {}; - class ShouldBeUsedOnceHint : public BitField16 {}; - class FunctionKindBits : public BitField16 {}; + class FunctionTypeBits : public BitField {}; + class Pretenure : public BitField {}; + class HasDuplicateParameters : public BitField {}; + class IsFunction : public BitField {}; + class ShouldEagerCompile : public BitField {}; + class ShouldBeUsedOnceHint : public BitField {}; + class RequiresClassFieldInit : public BitField {}; + class IsClassFieldInitializer : public BitField {}; + class FunctionKindBits : public BitField {}; - // Start with 16-bit field, which should get packed together - // with Expression's trailing 16-bit field. - uint16_t bitfield_; + uint32_t bitfield_; BailoutReason dont_optimize_reason_; @@ -2691,7 +2708,7 @@ class FunctionLiteral final : public Expression { // about a class literal's properties from the parser to the code generator. class ClassLiteralProperty final : public LiteralProperty { public: - enum Kind : uint8_t { METHOD, GETTER, SETTER }; + enum Kind : uint8_t { METHOD, GETTER, SETTER, FIELD }; Kind kind() const { return kind_; } @@ -2720,6 +2737,13 @@ class ClassLiteral final : public Expression { int start_position() const { return position(); } int end_position() const { return end_position_; } + VariableProxy* static_initializer_proxy() const { + return static_initializer_proxy_; + } + void set_static_initializer_proxy(VariableProxy* proxy) { + static_initializer_proxy_ = proxy; + } + BailoutId CreateLiteralId() const { return BailoutId(local_id(0)); } BailoutId PrototypeId() { return BailoutId(local_id(1)); } @@ -2754,7 +2778,8 @@ class ClassLiteral final : public Expression { class_variable_proxy_(class_variable_proxy), extends_(extends), constructor_(constructor), - properties_(properties) {} + properties_(properties), + static_initializer_proxy_(nullptr) {} static int parent_num_ids() { return Expression::num_ids(); } int local_id(int n) const { return base_id() + parent_num_ids() + n; } @@ -2766,6 +2791,7 @@ class ClassLiteral final : public Expression { Expression* extends_; FunctionLiteral* constructor_; ZoneList* properties_; + VariableProxy* static_initializer_proxy_; }; diff --git a/src/ast/prettyprinter.cc b/src/ast/prettyprinter.cc index 3fa5aff6a2..874c15991e 100644 --- a/src/ast/prettyprinter.cc +++ b/src/ast/prettyprinter.cc @@ -919,6 +919,9 @@ void AstPrinter::PrintClassProperties( case ClassLiteral::Property::SETTER: prop_kind = "SETTER"; break; + case ClassLiteral::Property::FIELD: + prop_kind = "FIELD"; + break; } EmbeddedVector buf; SNPrintF(buf, "PROPERTY%s - %s", property->is_static() ? " - STATIC" : "", diff --git a/src/ast/scopes.h b/src/ast/scopes.h index baa3cc0722..6987b7ffb1 100644 --- a/src/ast/scopes.h +++ b/src/ast/scopes.h @@ -685,7 +685,14 @@ class DeclarationScope : public Scope { } // Returns the default function arity excluding default or rest parameters. - int default_function_length() const { return arity_; } + // This will be used to set the length of the function, by default. + // Class field initializers use this property to indicate the number of + // fields being initialized. + int arity() const { return arity_; } + + // Normal code should not need to call this. Class field initializers use this + // property to indicate the number of fields being initialized. + void set_arity(int arity) { arity_ = arity; } // Returns the number of formal parameters, excluding a possible rest // parameter. Examples: diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 14fe8b0a5b..c7ee791aa0 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -2816,6 +2816,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(icu_case_mapping) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_async_await) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrictive_generators) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_trailing_commas) +EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_fields) void InstallPublicSymbol(Factory* factory, Handle native_context, const char* name, Handle value) { @@ -3416,6 +3417,7 @@ bool Genesis::InstallExperimentalNatives() { "native harmony-async-await.js", nullptr}; static const char* harmony_restrictive_generators_natives[] = {nullptr}; static const char* harmony_trailing_commas_natives[] = {nullptr}; + static const char* harmony_class_fields_natives[] = {nullptr}; for (int i = ExperimentalNatives::GetDebuggerCount(); i < ExperimentalNatives::GetBuiltinsCount(); i++) { diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc index 5eafd06fa0..a31e9d73ad 100644 --- a/src/compiler/ast-graph-builder.cc +++ b/src/compiler/ast-graph-builder.cc @@ -1652,6 +1652,10 @@ void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) { NewNode(op, receiver, key, value, attr); break; } + case ClassLiteral::Property::FIELD: { + UNREACHABLE(); + break; + } } } diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 6b8784de05..40a3f7dcdf 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -199,7 +199,8 @@ DEFINE_IMPLICATION(es_staging, move_object_start) V(harmony_regexp_property, "harmony unicode regexp property classes") \ V(harmony_for_in, "harmony for-in syntax") \ V(harmony_trailing_commas, \ - "harmony trailing commas in function parameter lists") + "harmony trailing commas in function parameter lists") \ + V(harmony_class_fields, "harmony public fields in class literals") // Features that are complete (but still behind --harmony/es-staging flag). #define HARMONY_STAGED_BASE(V) \ diff --git a/src/full-codegen/arm/full-codegen-arm.cc b/src/full-codegen/arm/full-codegen-arm.cc index 79a0515b38..0c6cde8354 100644 --- a/src/full-codegen/arm/full-codegen-arm.cc +++ b/src/full-codegen/arm/full-codegen-arm.cc @@ -2018,6 +2018,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + case ClassLiteral::Property::FIELD: default: UNREACHABLE(); } diff --git a/src/full-codegen/arm64/full-codegen-arm64.cc b/src/full-codegen/arm64/full-codegen-arm64.cc index d1b02eb31e..8cf4006c33 100644 --- a/src/full-codegen/arm64/full-codegen-arm64.cc +++ b/src/full-codegen/arm64/full-codegen-arm64.cc @@ -1919,6 +1919,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + case ClassLiteral::Property::FIELD: default: UNREACHABLE(); } diff --git a/src/full-codegen/ia32/full-codegen-ia32.cc b/src/full-codegen/ia32/full-codegen-ia32.cc index 105d99c1c3..8fde75830b 100644 --- a/src/full-codegen/ia32/full-codegen-ia32.cc +++ b/src/full-codegen/ia32/full-codegen-ia32.cc @@ -1923,6 +1923,10 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { PushOperand(Smi::FromInt(DONT_ENUM)); CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + + case ClassLiteral::Property::FIELD: + UNREACHABLE(); + break; } } } diff --git a/src/full-codegen/mips/full-codegen-mips.cc b/src/full-codegen/mips/full-codegen-mips.cc index 544874c198..e4b96dc84d 100644 --- a/src/full-codegen/mips/full-codegen-mips.cc +++ b/src/full-codegen/mips/full-codegen-mips.cc @@ -2021,6 +2021,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + case ClassLiteral::Property::FIELD: default: UNREACHABLE(); } diff --git a/src/full-codegen/mips64/full-codegen-mips64.cc b/src/full-codegen/mips64/full-codegen-mips64.cc index 81de0fcc9c..01b2f6e34f 100644 --- a/src/full-codegen/mips64/full-codegen-mips64.cc +++ b/src/full-codegen/mips64/full-codegen-mips64.cc @@ -2021,6 +2021,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + case ClassLiteral::Property::FIELD: default: UNREACHABLE(); } diff --git a/src/full-codegen/ppc/full-codegen-ppc.cc b/src/full-codegen/ppc/full-codegen-ppc.cc index 58aa629461..06162266dd 100644 --- a/src/full-codegen/ppc/full-codegen-ppc.cc +++ b/src/full-codegen/ppc/full-codegen-ppc.cc @@ -2025,6 +2025,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + case ClassLiteral::Property::FIELD: default: UNREACHABLE(); } diff --git a/src/full-codegen/s390/full-codegen-s390.cc b/src/full-codegen/s390/full-codegen-s390.cc index 3b64f711e6..10331f179c 100644 --- a/src/full-codegen/s390/full-codegen-s390.cc +++ b/src/full-codegen/s390/full-codegen-s390.cc @@ -1982,6 +1982,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + case ClassLiteral::Property::FIELD: default: UNREACHABLE(); } diff --git a/src/full-codegen/x64/full-codegen-x64.cc b/src/full-codegen/x64/full-codegen-x64.cc index 42d42978a6..6b68524678 100644 --- a/src/full-codegen/x64/full-codegen-x64.cc +++ b/src/full-codegen/x64/full-codegen-x64.cc @@ -1916,6 +1916,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + case ClassLiteral::Property::FIELD: default: UNREACHABLE(); } diff --git a/src/full-codegen/x87/full-codegen-x87.cc b/src/full-codegen/x87/full-codegen-x87.cc index a7a437c479..9cefbdda31 100644 --- a/src/full-codegen/x87/full-codegen-x87.cc +++ b/src/full-codegen/x87/full-codegen-x87.cc @@ -1904,6 +1904,10 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) { PushOperand(Smi::FromInt(DONT_ENUM)); CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked); break; + + case ClassLiteral::Property::FIELD: + UNREACHABLE(); + break; } } } diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index d88ea4bce4..27158cd8bf 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -1593,6 +1593,10 @@ void BytecodeGenerator::VisitClassLiteralProperties(ClassLiteral* expr, receiver, 4); break; } + case ClassLiteral::Property::FIELD: { + UNREACHABLE(); + break; + } } } } diff --git a/src/objects-inl.h b/src/objects-inl.h index 32e244ae38..b226501d7c 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -6107,6 +6107,10 @@ BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_default_constructor, kIsDefaultConstructor) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_asm_wasm_broken, kIsAsmWasmBroken) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, requires_class_field_init, + kRequiresClassFieldInit) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_class_field_initializer, + kIsClassFieldInitializer) inline bool SharedFunctionInfo::is_resumable() const { return is_generator() || is_async(); diff --git a/src/objects.cc b/src/objects.cc index 0f6ce61c06..0fd85f1f4f 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -13630,7 +13630,7 @@ void SharedFunctionInfo::InitFromFunctionLiteral( Handle shared_info, FunctionLiteral* lit) { // When adding fields here, make sure DeclarationScope::AnalyzePartially is // updated accordingly. - shared_info->set_length(lit->scope()->default_function_length()); + shared_info->set_length(lit->scope()->arity()); shared_info->set_internal_formal_parameter_count(lit->parameter_count()); shared_info->set_function_token_position(lit->function_token_position()); shared_info->set_start_position(lit->start_position()); @@ -13654,6 +13654,9 @@ void SharedFunctionInfo::InitFromFunctionLiteral( } shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject()); shared_info->set_asm_function(lit->scope()->asm_function()); + shared_info->set_requires_class_field_init(lit->requires_class_field_init()); + shared_info->set_is_class_field_initializer( + lit->is_class_field_initializer()); SetExpectedNofPropertiesFromEstimate(shared_info, lit); } diff --git a/src/objects.h b/src/objects.h index de3026eefc..f7e3964741 100644 --- a/src/objects.h +++ b/src/objects.h @@ -7384,6 +7384,12 @@ class SharedFunctionInfo: public HeapObject { // Indicates that this function is a default constructor. DECL_BOOLEAN_ACCESSORS(is_default_constructor) + // Indicates that this is a constructor for a base class with instance fields. + DECL_BOOLEAN_ACCESSORS(requires_class_field_init) + // Indicates that this is a synthesized function to set up class instance + // fields. + DECL_BOOLEAN_ACCESSORS(is_class_field_initializer) + // Indicates that this function is an asm function. DECL_BOOLEAN_ACCESSORS(asm_function) @@ -7672,6 +7678,8 @@ class SharedFunctionInfo: public HeapObject { kDeserialized, kIsDeclaration, kIsAsmWasmBroken, + kRequiresClassFieldInit, + kIsClassFieldInitializer, kCompilerHintsCount, // Pseudo entry }; // kFunctionKind has to be byte-aligned diff --git a/src/parsing/parse-info.cc b/src/parsing/parse-info.cc index 6b8fdf5f0d..2f812efee0 100644 --- a/src/parsing/parse-info.cc +++ b/src/parsing/parse-info.cc @@ -101,6 +101,15 @@ bool ParseInfo::is_default_constructor() const { 0; } +bool ParseInfo::requires_class_field_init() const { + return (compiler_hints_ & + (1 << SharedFunctionInfo::kRequiresClassFieldInit)) != 0; +} +bool ParseInfo::is_class_field_initializer() const { + return (compiler_hints_ & + (1 << SharedFunctionInfo::kIsClassFieldInitializer)) != 0; +} + FunctionKind ParseInfo::function_kind() const { return SharedFunctionInfo::FunctionKindBits::decode(compiler_hints_); } diff --git a/src/parsing/parse-info.h b/src/parsing/parse-info.h index e3fb5bd0c1..26a764038f 100644 --- a/src/parsing/parse-info.h +++ b/src/parsing/parse-info.h @@ -151,6 +151,8 @@ class ParseInfo { bool is_arrow() const; bool is_async() const; bool is_default_constructor() const; + bool requires_class_field_init() const; + bool is_class_field_initializer() const; FunctionKind function_kind() const; //-------------------------------------------------------------------------- diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h index cc8baf03bf..67d91a6c44 100644 --- a/src/parsing/parser-base.h +++ b/src/parsing/parser-base.h @@ -199,7 +199,8 @@ class ParserBase { allow_harmony_function_sent_(false), allow_harmony_async_await_(false), allow_harmony_restrictive_generators_(false), - allow_harmony_trailing_commas_(false) {} + allow_harmony_trailing_commas_(false), + allow_harmony_class_fields_(false) {} #define ALLOW_ACCESSORS(name) \ bool allow_##name() const { return allow_##name##_; } \ @@ -215,6 +216,7 @@ class ParserBase { ALLOW_ACCESSORS(harmony_async_await); ALLOW_ACCESSORS(harmony_restrictive_generators); ALLOW_ACCESSORS(harmony_trailing_commas); + ALLOW_ACCESSORS(harmony_class_fields); #undef ALLOW_ACCESSORS @@ -1100,6 +1102,7 @@ class ParserBase { kValueProperty, kShorthandProperty, kMethodProperty, + kClassField, kNotSet }; @@ -1112,6 +1115,8 @@ class ParserBase { ClassLiteralPropertyT ParseClassPropertyDefinition( ClassLiteralChecker* checker, bool has_extends, bool* is_computed_name, bool* has_seen_constructor, bool* ok); + FunctionLiteralT ParseClassFieldForInitializer(bool has_initializer, + bool* ok); ObjectLiteralPropertyT ParseObjectPropertyDefinition( ObjectLiteralChecker* checker, bool* is_computed_name, bool* ok); ExpressionListT ParseArguments(Scanner::Location* first_spread_pos, @@ -1387,6 +1392,7 @@ class ParserBase { bool allow_harmony_async_await_; bool allow_harmony_restrictive_generators_; bool allow_harmony_trailing_commas_; + bool allow_harmony_class_fields_; friend class DiscardableZoneScope; }; @@ -1942,6 +1948,10 @@ bool ParserBase::SetPropertyKindFromToken(Token::Value token, case Token::LPAREN: *kind = PropertyKind::kMethodProperty; return true; + case Token::MUL: + case Token::SEMICOLON: + *kind = PropertyKind::kClassField; + return true; default: break; } @@ -2079,6 +2089,10 @@ ParserBase::ParseClassPropertyDefinition(ClassLiteralChecker* checker, kind = PropertyKind::kMethodProperty; name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static' name_expression = factory()->NewStringLiteral(name, position()); + } else if (peek() == Token::ASSIGN || peek() == Token::SEMICOLON || + peek() == Token::RBRACE) { + name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static' + name_expression = factory()->NewStringLiteral(name, position()); } else { is_static = true; name_expression = ParsePropertyName( @@ -2092,11 +2106,29 @@ ParserBase::ParseClassPropertyDefinition(ClassLiteralChecker* checker, } switch (kind) { + case PropertyKind::kClassField: + case PropertyKind::kNotSet: // This case is a name followed by a name or + // other property. Here we have to assume + // that's an uninitialized field followed by a + // linebreak followed by a property, with ASI + // adding the semicolon. If not, there will be + // a syntax error after parsing the first name + // as an uninitialized field. case PropertyKind::kShorthandProperty: case PropertyKind::kValueProperty: - ReportUnexpectedToken(Next()); - *ok = false; - return impl()->EmptyClassLiteralProperty(); + if (allow_harmony_class_fields()) { + bool has_initializer = Check(Token::ASSIGN); + ExpressionT function_literal = ParseClassFieldForInitializer( + has_initializer, CHECK_OK_CUSTOM(EmptyClassLiteralProperty)); + ExpectSemicolon(CHECK_OK_CUSTOM(EmptyClassLiteralProperty)); + return factory()->NewClassLiteralProperty( + name_expression, function_literal, ClassLiteralProperty::FIELD, + is_static, *is_computed_name); + } else { + ReportUnexpectedToken(Next()); + *ok = false; + return impl()->EmptyClassLiteralProperty(); + } case PropertyKind::kMethodProperty: { DCHECK(!is_get && !is_set); @@ -2163,16 +2195,48 @@ ParserBase::ParseClassPropertyDefinition(ClassLiteralChecker* checker, is_get ? ClassLiteralProperty::GETTER : ClassLiteralProperty::SETTER, is_static, *is_computed_name); } - - case PropertyKind::kNotSet: - ReportUnexpectedToken(Next()); - *ok = false; - return impl()->EmptyClassLiteralProperty(); } UNREACHABLE(); return impl()->EmptyClassLiteralProperty(); } +template +typename ParserBase::FunctionLiteralT +ParserBase::ParseClassFieldForInitializer(bool has_initializer, + bool* ok) { + // Makes a concise method which evaluates and returns the initialized value + // (or undefined if absent). + FunctionKind kind = FunctionKind::kConciseMethod; + DeclarationScope* initializer_scope = NewFunctionScope(kind); + initializer_scope->set_start_position(scanner()->location().end_pos); + FunctionState initializer_state(&function_state_, &scope_state_, + initializer_scope, kind); + DCHECK(scope() == initializer_scope); + scope()->SetLanguageMode(STRICT); + ExpressionClassifier expression_classifier(this); + ExpressionT value; + if (has_initializer) { + value = this->ParseAssignmentExpression( + true, CHECK_OK_CUSTOM(EmptyFunctionLiteral)); + impl()->RewriteNonPattern(CHECK_OK_CUSTOM(EmptyFunctionLiteral)); + } else { + value = factory()->NewUndefinedLiteral(kNoSourcePosition); + } + initializer_scope->set_end_position(scanner()->location().end_pos); + typename Types::StatementList body = impl()->NewStatementList(1); + body->Add(factory()->NewReturnStatement(value, kNoSourcePosition), zone()); + FunctionLiteralT function_literal = factory()->NewFunctionLiteral( + impl()->EmptyIdentifierString(), initializer_scope, body, + initializer_state.materialized_literal_count(), + initializer_state.expected_property_count(), 0, + FunctionLiteral::kNoDuplicateParameters, + FunctionLiteral::kAnonymousExpression, + FunctionLiteral::kShouldLazyCompile, kind, + initializer_scope->start_position()); + function_literal->set_is_class_field_initializer(true); + return function_literal; +} + template typename ParserBase::ObjectLiteralPropertyT ParserBase::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, @@ -2343,6 +2407,7 @@ ParserBase::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, *is_computed_name); } + case PropertyKind::kClassField: case PropertyKind::kNotSet: ReportUnexpectedToken(Next()); *ok = false; diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc index 799c7b9616..dd8ccd8e1c 100644 --- a/src/parsing/parser.cc +++ b/src/parsing/parser.cc @@ -151,8 +151,9 @@ void Parser::SetCachedData(ParseInfo* info) { } FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name, - bool call_super, int pos, - int end_pos, + bool call_super, + bool requires_class_field_init, + int pos, int end_pos, LanguageMode language_mode) { int materialized_literal_count = -1; int expected_property_count = -1; @@ -221,6 +222,8 @@ FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name, FunctionLiteral::kAnonymousExpression, FunctionLiteral::kShouldLazyCompile, kind, pos); + function_literal->set_requires_class_field_init(requires_class_field_init); + return function_literal; } @@ -590,6 +593,7 @@ Parser::Parser(ParseInfo* info) set_allow_harmony_async_await(FLAG_harmony_async_await); set_allow_harmony_restrictive_generators(FLAG_harmony_restrictive_generators); set_allow_harmony_trailing_commas(FLAG_harmony_trailing_commas); + set_allow_harmony_class_fields(FLAG_harmony_class_fields); for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; ++feature) { use_counts_[feature] = 0; @@ -962,7 +966,8 @@ FunctionLiteral* Parser::DoParseLazy(ParseInfo* info, DCHECK_EQ(scope(), outer); result = DefaultConstructor( raw_name, IsSubclassConstructor(info->function_kind()), - info->start_position(), info->end_position(), info->language_mode()); + info->requires_class_field_init(), info->start_position(), + info->end_position(), info->language_mode()); } else { result = ParseFunctionLiteral(raw_name, Scanner::Location::invalid(), kSkipFunctionNameCheck, @@ -3722,6 +3727,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser( SET_ALLOW(harmony_restrictive_declarations); SET_ALLOW(harmony_async_await); SET_ALLOW(harmony_trailing_commas); + SET_ALLOW(harmony_class_fields); #undef SET_ALLOW } PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction( @@ -3782,8 +3788,15 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name, ClassLiteralChecker checker(this); ZoneList* properties = NewClassPropertyList(4); + ZoneList* instance_field_initializers = + new (zone()) ZoneList(0, zone()); FunctionLiteral* constructor = nullptr; bool has_seen_constructor = false; + Variable* static_initializer_var = nullptr; + + Block* do_block = factory()->NewBlock(nullptr, 1, false, pos); + Variable* result_var = NewTemporary(ast_value_factory()->empty_string()); + DoExpression* do_expr = factory()->NewDoExpression(do_block, result_var, pos); Expect(Token::LBRACE, CHECK_OK); @@ -3806,6 +3819,10 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name, constructor->set_raw_name( name != nullptr ? name : ast_value_factory()->empty_string()); } else { + if (property->kind() == ClassLiteralProperty::FIELD) { + DCHECK(allow_harmony_class_fields()); + continue; // TODO(bakkot) implementation + } properties->Add(property, zone()); } @@ -3816,13 +3833,18 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name, Expect(Token::RBRACE, CHECK_OK); int end_pos = scanner()->location().end_pos; - if (constructor == nullptr) { - constructor = DefaultConstructor(name, has_extends, pos, end_pos, - block_state.language_mode()); + bool has_instance_fields = instance_field_initializers->length() > 0; + DCHECK(!has_instance_fields || allow_harmony_class_fields()); + bool has_default_constructor = constructor == nullptr; + if (has_default_constructor) { + constructor = DefaultConstructor(name, has_extends, has_instance_fields, + pos, end_pos, block_state.language_mode()); } - // Note that we do not finalize this block scope because it is - // used as a sentinel value indicating an anonymous class. + if (has_instance_fields && extends == nullptr) { + constructor->set_requires_class_field_init(true); + } // The derived case is handled by rewriting super calls. + block_state.set_end_position(end_pos); if (name != nullptr) { @@ -3830,18 +3852,23 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name, proxy->var()->set_initializer_position(end_pos); } - Block* do_block = factory()->NewBlock(nullptr, 1, false, pos); - Variable* result_var = NewTemporary(ast_value_factory()->empty_string()); - do_block->set_scope(block_state.FinalizedBlockScope()); - DoExpression* do_expr = factory()->NewDoExpression(do_block, result_var, pos); - ClassLiteral* class_literal = factory()->NewClassLiteral( proxy, extends, constructor, properties, pos, end_pos); + if (static_initializer_var != nullptr) { + class_literal->set_static_initializer_proxy( + factory()->NewVariableProxy(static_initializer_var)); + } + do_block->statements()->Add( - factory()->NewExpressionStatement(class_literal, pos), zone()); + factory()->NewExpressionStatement( + factory()->NewAssignment(Token::ASSIGN, + factory()->NewVariableProxy(result_var), + class_literal, kNoSourcePosition), + pos), + zone()); + do_block->set_scope(block_state.FinalizedBlockScope()); do_expr->set_represented_function(constructor); - Rewriter::Rewrite(this, GetClosureScope(), do_expr, ast_value_factory()); return do_expr; } diff --git a/src/parsing/parser.h b/src/parsing/parser.h index e226e25ae7..b037bf29d0 100644 --- a/src/parsing/parser.h +++ b/src/parsing/parser.h @@ -464,8 +464,8 @@ class Parser : public ParserBase { // Factory methods. FunctionLiteral* DefaultConstructor(const AstRawString* name, bool call_super, - int pos, int end_pos, - LanguageMode language_mode); + bool requires_class_field_init, int pos, + int end_pos, LanguageMode language_mode); // Skip over a lazy function, either using cached data if we have it, or // by parsing the function with PreParser. Consumes the ending }. @@ -542,6 +542,8 @@ class Parser : public ParserBase { int pos); Expression* SpreadCallNew(Expression* function, ZoneList* args, int pos); + Expression* CallClassFieldInitializer(Scope* scope, Expression* this_expr); + Expression* RewriteSuperCall(Expression* call_expression); void SetLanguageMode(Scope* scope, LanguageMode mode); void SetAsmModule(); diff --git a/src/parsing/preparser.h b/src/parsing/preparser.h index ddc74c5318..b14f1479d1 100644 --- a/src/parsing/preparser.h +++ b/src/parsing/preparser.h @@ -316,6 +316,8 @@ class PreParserExpression { int position() const { return kNoSourcePosition; } void set_function_token_position(int position) {} + void set_is_class_field_initializer(bool is_class_field_initializer) {} + private: enum Type { kEmpty, @@ -477,6 +479,9 @@ class PreParserFactory { int pos) { return PreParserExpression::Default(); } + PreParserExpression NewUndefinedLiteral(int pos) { + return PreParserExpression::Default(); + } PreParserExpression NewRegExpLiteral(PreParserIdentifier js_pattern, int js_flags, int literal_index, int pos) { diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 8fa8fb8d8e..8628131dc3 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -1490,6 +1490,7 @@ enum ParserFlag { kAllowHarmonyAsyncAwait, kAllowHarmonyRestrictiveGenerators, kAllowHarmonyTrailingCommas, + kAllowHarmonyClassFields, }; enum ParserSyncTestResult { @@ -1514,6 +1515,8 @@ void SetParserFlags(i::ParserBase* parser, flags.Contains(kAllowHarmonyRestrictiveGenerators)); parser->set_allow_harmony_trailing_commas( flags.Contains(kAllowHarmonyTrailingCommas)); + parser->set_allow_harmony_class_fields( + flags.Contains(kAllowHarmonyClassFields)); } @@ -4569,6 +4572,160 @@ TEST(ClassPropertyNameNoErrors) { RunParserSyncTest(context_data, name_data, kSuccess); } +TEST(ClassFieldsNoErrors) { + // clang-format off + // Tests proposed class fields syntax. + const char* context_data[][2] = {{"(class {", "});"}, + {"(class extends Base {", "});"}, + {"class C {", "}"}, + {"class C extends Base {", "}"}, + {NULL, NULL}}; + const char* class_body_data[] = { + // Basic syntax + "a = 0;", + "a = 0; b", + "a = 0; b(){}", + "a = 0; *b(){}", + "a = 0; ['b'](){}", + "a;", + "a; b;", + "a; b(){}", + "a; *b(){}", + "a; ['b'](){}", + "['a'] = 0;", + "['a'] = 0; b", + "['a'] = 0; b(){}", + "['a'] = 0; *b(){}", + "['a'] = 0; ['b'](){}", + "['a'];", + "['a']; b;", + "['a']; b(){}", + "['a']; *b(){}", + "['a']; ['b'](){}", + + "0 = 0;", + "0;", + "'a' = 0;", + "'a';", + + "static a = 0;", + "static a;", + "static ['a'] = 0", + "static ['a']", + "static 0 = 0;", + "static 0;", + "static 'a' = 0;", + "static 'a';", + + // ASI + "a = 0\n", + "a = 0\n b", + "a = 0\n b(){}", + "a\n", + "a\n b\n", + "a\n b(){}", + "a\n *b(){}", + "a\n ['b'](){}", + "['a'] = 0\n", + "['a'] = 0\n b", + "['a'] = 0\n b(){}", + "['a']\n", + "['a']\n b\n", + "['a']\n b(){}", + "['a']\n *b(){}", + "['a']\n ['b'](){}", + + // ASI edge cases + "a\n get", + "get\n *a(){}", + "a\n static", + + // Misc edge cases + "yield", + "yield = 0", + "yield\n a", + NULL + }; + // clang-format on + + static const ParserFlag without_async[] = {kAllowHarmonyClassFields}; + RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0, + without_async, arraysize(without_async)); + + // clang-format off + const char* async_data[] = { + "async;", + "async = 0;", + "static async;" + "async", + "async = 0", + "static async", + "async\n a(){}", // a field named async, and a method named a. + "async\n a", + "await;", + "await = 0;", + "await\n a", + NULL + }; + // clang-format on + + static const ParserFlag with_async[] = {kAllowHarmonyClassFields, + kAllowHarmonyAsyncAwait}; + RunParserSyncTest(context_data, async_data, kSuccess, NULL, 0, with_async, + arraysize(with_async)); +} + +TEST(ClassFieldsErrors) { + // clang-format off + // Tests proposed class fields syntax. + const char* context_data[][2] = {{"(class {", "});"}, + {"(class extends Base {", "});"}, + {"class C {", "}"}, + {"class C extends Base {", "}"}, + {NULL, NULL}}; + const char* class_body_data[] = { + "a : 0", + "a =", + "*a = 0", + "*a", + "get a", + "yield a", + "a : 0;", + "a =;", + "*a = 0;", + "*a;", + "get a;", + "yield a;", + + // ASI requires a linebreak + "a b", + "a = 0 b", + + // ASI requires that the next token is not part of any legal production + "a = 0\n *b(){}", + "a = 0\n ['b'](){}", + "get\n a", + NULL + }; + // clang-format on + + static const ParserFlag without_async[] = {kAllowHarmonyClassFields}; + RunParserSyncTest(context_data, class_body_data, kError, NULL, 0, + without_async, arraysize(without_async)); + + // clang-format off + const char* async_data[] = { + "async a = 0", + "async a", + NULL + }; + // clang-format on + + static const ParserFlag with_async[] = {kAllowHarmonyClassFields, + kAllowHarmonyAsyncAwait}; + RunParserSyncTest(context_data, async_data, kError, NULL, 0, with_async, + arraysize(with_async)); +} TEST(ClassExpressionErrors) { const char* context_data[][2] = {{"(", ");"},