Class fields, part 1 (parsing and infrastructure)
This is one part of a WIP implementation of the stage-2 proposal to add fields to classes: https://github.com/tc39/proposal-class-public-fields See design doc: https://docs.google.com/document/d/1WRtNm3ZLNJT1WVr8aq4RJuByYgfuAFAhj20LwTW6JVE/ This adds support for parsing fields in classes, including infrastructure. In particular, it adds: * Two booleans on function literal AST nodes * Two compiler hints on SharedFunctionInfos representing said bools * A new type of ClassLiteralProperty, FIELD * Parser support for the syntax * Syntax tests * A flag to enable it. Currently the fields are parsed and then droppped. Subsequent patches will add semantics, mostly by desugaring in the parser and the remainder in the non-crankshaft backends. BUG=v8:5367 Review-Url: https://codereview.chromium.org/2315733003 Cr-Commit-Position: refs/heads/master@{#39459}
This commit is contained in:
parent
9df94139d8
commit
fe6b76d491
@ -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<FunctionType, 0, 2> {};
|
||||
class Pretenure : public BitField16<bool, 2, 1> {};
|
||||
class HasDuplicateParameters : public BitField16<bool, 3, 1> {};
|
||||
class IsFunction : public BitField16<bool, 4, 1> {};
|
||||
class ShouldEagerCompile : public BitField16<bool, 5, 1> {};
|
||||
class ShouldBeUsedOnceHint : public BitField16<bool, 6, 1> {};
|
||||
class FunctionKindBits : public BitField16<FunctionKind, 7, 9> {};
|
||||
class FunctionTypeBits : public BitField<FunctionType, 0, 2> {};
|
||||
class Pretenure : public BitField<bool, 2, 1> {};
|
||||
class HasDuplicateParameters : public BitField<bool, 3, 1> {};
|
||||
class IsFunction : public BitField<bool, 4, 1> {};
|
||||
class ShouldEagerCompile : public BitField<bool, 5, 1> {};
|
||||
class ShouldBeUsedOnceHint : public BitField<bool, 6, 1> {};
|
||||
class RequiresClassFieldInit : public BitField<bool, 7, 1> {};
|
||||
class IsClassFieldInitializer : public BitField<bool, 8, 1> {};
|
||||
class FunctionKindBits : public BitField<FunctionKind, 9, 9> {};
|
||||
|
||||
// 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<Property*>* properties_;
|
||||
VariableProxy* static_initializer_proxy_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -919,6 +919,9 @@ void AstPrinter::PrintClassProperties(
|
||||
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", property->is_static() ? " - STATIC" : "",
|
||||
|
@ -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:
|
||||
|
@ -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<Context> native_context,
|
||||
const char* name, Handle<Symbol> 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++) {
|
||||
|
@ -1652,6 +1652,10 @@ void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
|
||||
NewNode(op, receiver, key, value, attr);
|
||||
break;
|
||||
}
|
||||
case ClassLiteral::Property::FIELD: {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -2018,6 +2018,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -1919,6 +1919,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -1923,6 +1923,10 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
PushOperand(Smi::FromInt(DONT_ENUM));
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2021,6 +2021,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -2021,6 +2021,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -2025,6 +2025,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -1982,6 +1982,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -1916,6 +1916,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -1904,6 +1904,10 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
PushOperand(Smi::FromInt(DONT_ENUM));
|
||||
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
|
||||
break;
|
||||
|
||||
case ClassLiteral::Property::FIELD:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1593,6 +1593,10 @@ void BytecodeGenerator::VisitClassLiteralProperties(ClassLiteral* expr,
|
||||
receiver, 4);
|
||||
break;
|
||||
}
|
||||
case ClassLiteral::Property::FIELD: {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -13630,7 +13630,7 @@ void SharedFunctionInfo::InitFromFunctionLiteral(
|
||||
Handle<SharedFunctionInfo> 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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
@ -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<Impl>::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<Impl>::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<Impl>::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<Impl>::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 Impl>
|
||||
typename ParserBase<Impl>::FunctionLiteralT
|
||||
ParserBase<Impl>::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 Impl>
|
||||
typename ParserBase<Impl>::ObjectLiteralPropertyT
|
||||
ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker,
|
||||
@ -2343,6 +2407,7 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker,
|
||||
*is_computed_name);
|
||||
}
|
||||
|
||||
case PropertyKind::kClassField:
|
||||
case PropertyKind::kNotSet:
|
||||
ReportUnexpectedToken(Next());
|
||||
*ok = false;
|
||||
|
@ -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<ClassLiteral::Property*>* properties = NewClassPropertyList(4);
|
||||
ZoneList<Expression*>* instance_field_initializers =
|
||||
new (zone()) ZoneList<Expression*>(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;
|
||||
}
|
||||
|
@ -464,8 +464,8 @@ class Parser : public ParserBase<Parser> {
|
||||
|
||||
// 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<Parser> {
|
||||
int pos);
|
||||
Expression* SpreadCallNew(Expression* function, ZoneList<Expression*>* args,
|
||||
int pos);
|
||||
Expression* CallClassFieldInitializer(Scope* scope, Expression* this_expr);
|
||||
Expression* RewriteSuperCall(Expression* call_expression);
|
||||
|
||||
void SetLanguageMode(Scope* scope, LanguageMode mode);
|
||||
void SetAsmModule();
|
||||
|
@ -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) {
|
||||
|
@ -1490,6 +1490,7 @@ enum ParserFlag {
|
||||
kAllowHarmonyAsyncAwait,
|
||||
kAllowHarmonyRestrictiveGenerators,
|
||||
kAllowHarmonyTrailingCommas,
|
||||
kAllowHarmonyClassFields,
|
||||
};
|
||||
|
||||
enum ParserSyncTestResult {
|
||||
@ -1514,6 +1515,8 @@ void SetParserFlags(i::ParserBase<Traits>* 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] = {{"(", ");"},
|
||||
|
Loading…
Reference in New Issue
Block a user