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:
bakkot 2016-09-15 17:42:30 -07:00 committed by Commit bot
parent 9df94139d8
commit fe6b76d491
26 changed files with 385 additions and 41 deletions

View File

@ -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_;
};

View File

@ -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" : "",

View File

@ -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:

View File

@ -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++) {

View File

@ -1652,6 +1652,10 @@ void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
NewNode(op, receiver, key, value, attr);
break;
}
case ClassLiteral::Property::FIELD: {
UNREACHABLE();
break;
}
}
}

View File

@ -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) \

View File

@ -2018,6 +2018,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
default:
UNREACHABLE();
}

View File

@ -1919,6 +1919,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
default:
UNREACHABLE();
}

View File

@ -1923,6 +1923,10 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
PushOperand(Smi::FromInt(DONT_ENUM));
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
UNREACHABLE();
break;
}
}
}

View File

@ -2021,6 +2021,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
default:
UNREACHABLE();
}

View File

@ -2021,6 +2021,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
default:
UNREACHABLE();
}

View File

@ -2025,6 +2025,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
default:
UNREACHABLE();
}

View File

@ -1982,6 +1982,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
default:
UNREACHABLE();
}

View File

@ -1916,6 +1916,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
default:
UNREACHABLE();
}

View File

@ -1904,6 +1904,10 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
PushOperand(Smi::FromInt(DONT_ENUM));
CallRuntimeWithOperands(Runtime::kDefineSetterPropertyUnchecked);
break;
case ClassLiteral::Property::FIELD:
UNREACHABLE();
break;
}
}
}

View File

@ -1593,6 +1593,10 @@ void BytecodeGenerator::VisitClassLiteralProperties(ClassLiteral* expr,
receiver, 4);
break;
}
case ClassLiteral::Property::FIELD: {
UNREACHABLE();
break;
}
}
}
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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

View File

@ -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_);
}

View File

@ -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;
//--------------------------------------------------------------------------

View File

@ -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;

View File

@ -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;
}

View File

@ -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();

View File

@ -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) {

View File

@ -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] = {{"(", ");"},