Commit f9a3a040 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[class] Implement runtime semantics for instance fields in base class

Creates a new initializer function to instantiate instance class
fields in a base class.

An initializer function (similar to the one created for static fields)
is created during class declaration and assigned to a synthetic
context allocated variable.

This function is loaded from the variable during instantiation (when
the constructor is run) and run.

Bug: v8:5367
Change-Id: Ie11c2183b3001234ae41d7bcc2cb9b02c0764ab5
Reviewed-on: https://chromium-review.googlesource.com/754445
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49207}
parent fd8c7929
......@@ -204,6 +204,10 @@ void AstExpressionRewriter::VisitClassLiteral(ClassLiteral* node) {
if (node->static_fields_initializer() != nullptr) {
AST_REWRITE_PROPERTY(FunctionLiteral, node, static_fields_initializer);
}
if (node->instance_fields_initializer_function() != nullptr) {
AST_REWRITE_PROPERTY(FunctionLiteral, node,
instance_fields_initializer_function);
}
ZoneList<typename ClassLiteral::Property*>* properties = node->properties();
for (int i = 0; i < properties->length(); i++) {
VisitLiteralProperty(properties->at(i));
......
......@@ -307,6 +307,9 @@ void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) {
if (node->static_fields_initializer() != nullptr) {
Visit(node->static_fields_initializer());
}
if (node->instance_fields_initializer_function() != nullptr) {
Visit(node->instance_fields_initializer_function());
}
for (int i = 0; i < node->properties()->length(); i++) {
VisitLiteralProperty(node->properties()->at(i));
}
......
......@@ -475,6 +475,9 @@ void AstTraversalVisitor<Subclass>::VisitClassLiteral(ClassLiteral* expr) {
if (expr->static_fields_initializer() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->static_fields_initializer()));
}
if (expr->instance_fields_initializer_function() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->instance_fields_initializer_function()));
}
ZoneList<ClassLiteralProperty*>* props = expr->properties();
for (int i = 0; i < props->length(); ++i) {
ClassLiteralProperty* prop = props->at(i);
......
......@@ -190,48 +190,49 @@ class AstBigInt {
};
// For generating constants.
#define AST_STRING_CONSTANTS(F) \
F(anonymous_function, "(anonymous function)") \
F(arguments, "arguments") \
F(async, "async") \
F(await, "await") \
F(bigint, "bigint") \
F(boolean, "boolean") \
F(constructor, "constructor") \
F(default, "default") \
F(done, "done") \
F(dot, ".") \
F(dot_for, ".for") \
F(dot_generator_object, ".generator_object") \
F(dot_iterator, ".iterator") \
F(dot_result, ".result") \
F(dot_switch_tag, ".switch_tag") \
F(dot_catch, ".catch") \
F(empty, "") \
F(eval, "eval") \
F(function, "function") \
F(get_space, "get ") \
F(length, "length") \
F(let, "let") \
F(name, "name") \
F(native, "native") \
F(new_target, ".new.target") \
F(next, "next") \
F(number, "number") \
F(object, "object") \
F(proto, "__proto__") \
F(prototype, "prototype") \
F(return, "return") \
F(set_space, "set ") \
F(star_default_star, "*default*") \
F(string, "string") \
F(symbol, "symbol") \
F(this, "this") \
F(this_function, ".this_function") \
F(throw, "throw") \
F(undefined, "undefined") \
F(use_asm, "use asm") \
F(use_strict, "use strict") \
#define AST_STRING_CONSTANTS(F) \
F(anonymous_function, "(anonymous function)") \
F(arguments, "arguments") \
F(async, "async") \
F(await, "await") \
F(bigint, "bigint") \
F(boolean, "boolean") \
F(constructor, "constructor") \
F(default, "default") \
F(done, "done") \
F(dot, ".") \
F(dot_for, ".for") \
F(dot_generator_object, ".generator_object") \
F(dot_instance_fields_initializer, ".instance_fields_initializer") \
F(dot_iterator, ".iterator") \
F(dot_result, ".result") \
F(dot_switch_tag, ".switch_tag") \
F(dot_catch, ".catch") \
F(empty, "") \
F(eval, "eval") \
F(function, "function") \
F(get_space, "get ") \
F(length, "length") \
F(let, "let") \
F(name, "name") \
F(native, "native") \
F(new_target, ".new.target") \
F(next, "next") \
F(number, "number") \
F(object, "object") \
F(proto, "__proto__") \
F(prototype, "prototype") \
F(return, "return") \
F(set_space, "set ") \
F(star_default_star, "*default*") \
F(string, "string") \
F(symbol, "symbol") \
F(this, "this") \
F(this_function, ".this_function") \
F(throw, "throw") \
F(undefined, "undefined") \
F(use_asm, "use asm") \
F(use_strict, "use strict") \
F(value, "value")
class AstStringConstants final {
......
......@@ -2345,6 +2345,13 @@ class FunctionLiteral final : public Expression {
function_literal_id_ = function_literal_id;
}
void set_instance_class_fields_initializer(Variable* initializer) {
instance_class_fields_initializer_ = initializer;
}
Variable* instance_class_fields_initializer() {
return instance_class_fields_initializer_;
}
ProducedPreParsedScopeData* produced_preparsed_scope_data() const {
return produced_preparsed_scope_data_;
}
......@@ -2372,6 +2379,7 @@ class FunctionLiteral final : public Expression {
body_(body),
raw_inferred_name_(ast_value_factory->empty_cons_string()),
function_literal_id_(function_literal_id),
instance_class_fields_initializer_(nullptr),
produced_preparsed_scope_data_(produced_preparsed_scope_data) {
bit_field_ |= FunctionTypeBits::encode(function_type) |
Pretenure::encode(false) |
......@@ -2402,6 +2410,7 @@ class FunctionLiteral final : public Expression {
const AstConsString* raw_inferred_name_;
Handle<String> inferred_name_;
int function_literal_id_;
Variable* instance_class_fields_initializer_;
ProducedPreParsedScopeData* produced_preparsed_scope_data_;
};
......@@ -2478,12 +2487,30 @@ class ClassLiteral final : public Expression {
static_fields_initializer_ = initializer;
}
FunctionLiteral* instance_fields_initializer_function() const {
return instance_fields_initializer_function_;
}
void set_instance_fields_initializer_function(FunctionLiteral* initializer) {
instance_fields_initializer_function_ = initializer;
}
Variable* instance_fields_initializer_var() const {
return instance_fields_initializer_var_;
}
void set_instance_fields_initializer_var(Variable* var) {
instance_fields_initializer_var_ = var;
}
private:
friend class AstNodeFactory;
ClassLiteral(Scope* scope, Variable* class_variable, Expression* extends,
FunctionLiteral* constructor, ZoneList<Property*>* properties,
FunctionLiteral* static_fields_initializer, int start_position,
FunctionLiteral* static_fields_initializer,
FunctionLiteral* instance_fields_initializer_function,
Variable* instance_fields_initializer_var, int start_position,
int end_position, bool has_name_static_property,
bool has_static_computed_names, bool is_anonymous)
: Expression(start_position, kClassLiteral),
......@@ -2493,7 +2520,10 @@ class ClassLiteral final : public Expression {
extends_(extends),
constructor_(constructor),
properties_(properties),
static_fields_initializer_(static_fields_initializer) {
static_fields_initializer_(static_fields_initializer),
instance_fields_initializer_function_(
instance_fields_initializer_function),
instance_fields_initializer_var_(instance_fields_initializer_var) {
bit_field_ |= HasNameStaticProperty::encode(has_name_static_property) |
HasStaticComputedNames::encode(has_static_computed_names) |
IsAnonymousExpression::encode(is_anonymous);
......@@ -2506,7 +2536,8 @@ class ClassLiteral final : public Expression {
FunctionLiteral* constructor_;
ZoneList<Property*>* properties_;
FunctionLiteral* static_fields_initializer_;
FunctionLiteral* instance_fields_initializer_function_;
Variable* instance_fields_initializer_var_;
class HasNameStaticProperty
: public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
class HasStaticComputedNames
......@@ -3258,18 +3289,19 @@ class AstNodeFactory final BASE_EMBEDDED {
ClassLiteral::Property(key, value, kind, is_static, is_computed_name);
}
ClassLiteral* NewClassLiteral(Scope* scope, Variable* variable,
Expression* extends,
FunctionLiteral* constructor,
ZoneList<ClassLiteral::Property*>* properties,
FunctionLiteral* static_fields_initializer,
int start_position, int end_position,
bool has_name_static_property,
bool has_static_computed_names,
bool is_anonymous) {
ClassLiteral* NewClassLiteral(
Scope* scope, Variable* variable, Expression* extends,
FunctionLiteral* constructor,
ZoneList<ClassLiteral::Property*>* properties,
FunctionLiteral* static_fields_initializer,
FunctionLiteral* instance_fields_initializer_function,
Variable* instance_fields_initializer_var, int start_position,
int end_position, bool has_name_static_property,
bool has_static_computed_names, bool is_anonymous) {
return new (zone_) ClassLiteral(
scope, variable, extends, constructor, properties,
static_fields_initializer, start_position, end_position,
static_fields_initializer, instance_fields_initializer_function,
instance_fields_initializer_var, start_position, end_position,
has_name_static_property, has_static_computed_names, is_anonymous);
}
......
This diff is collapsed.
......@@ -1007,6 +1007,12 @@ void BytecodeGenerator::GenerateBytecodeBody() {
// Perform a stack-check before the body.
builder()->StackCheck(info()->literal()->start_position());
if (IsBaseConstructor(function_kind()) &&
info()->literal()->instance_class_fields_initializer() != nullptr) {
BuildInstanceFieldInitialization(
info()->literal()->instance_class_fields_initializer());
}
// Visit statements in the function body.
VisitStatements(info()->literal()->body());
......@@ -1798,10 +1804,23 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
HoleCheckMode::kElided);
}
if (expr->instance_fields_initializer_function() != nullptr) {
Register initializer =
VisitForRegisterValue(expr->instance_fields_initializer_function());
// TODO(gsathya): Support super property access in instance field
// initializers.
builder()->LoadAccumulatorWithRegister(initializer);
BuildVariableAssignment(expr->instance_fields_initializer_var(),
Token::INIT, HoleCheckMode::kElided);
builder()->LoadAccumulatorWithRegister(constructor);
}
if (expr->static_fields_initializer() != nullptr) {
RegisterList args = register_allocator()->NewRegisterList(1);
Register initializer = register_allocator()->NewRegister();
VisitForRegisterValue(expr->static_fields_initializer(), initializer);
Register initializer =
VisitForRegisterValue(expr->static_fields_initializer());
if (FunctionLiteral::NeedsHomeObject(expr->static_fields_initializer())) {
FeedbackSlot slot = feedback_spec()->AddStoreICSlot(language_mode());
......@@ -1853,18 +1872,20 @@ void BytecodeGenerator::VisitClassLiteralProperties(ClassLiteral* expr,
}
BuildLoadPropertyKey(property, key);
if (property->is_static() && property->is_computed_name()) {
// The static prototype property is read only. We handle the non computed
// property name case in the parser. Since this is the only case where we
// need to check for an own read only property we special case this so we
// do not need to do this for every property.
BytecodeLabel done;
builder()
->LoadLiteral(ast_string_constants()->prototype_string())
.CompareOperation(Token::Value::EQ_STRICT, key)
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &done)
.CallRuntime(Runtime::kThrowStaticPrototypeError)
.Bind(&done);
if (property->is_computed_name()) {
if (property->is_static()) {
// The static prototype property is read only. We handle the non
// computed property name case in the parser. Since this is the only
// case where we need to check for an own read only property we special
// case this so we do not need to do this for every property.
BytecodeLabel done;
builder()
->LoadLiteral(ast_string_constants()->prototype_string())
.CompareOperation(Token::Value::EQ_STRICT, key)
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &done)
.CallRuntime(Runtime::kThrowStaticPrototypeError)
.Bind(&done);
}
if (property->kind() == ClassLiteral::Property::FIELD) {
DCHECK_NOT_NULL(property->computed_name_var());
......@@ -1949,6 +1970,19 @@ void BytecodeGenerator::VisitInitializeClassFieldsStatement(
}
}
void BytecodeGenerator::BuildInstanceFieldInitialization(
Variable* initializer_var) {
RegisterList args = register_allocator()->NewRegisterList(1);
Register initializer = register_allocator()->NewRegister();
BuildVariableLoad(initializer_var, HoleCheckMode::kElided);
builder()
->StoreAccumulatorInRegister(initializer)
.MoveRegister(builder()->Receiver(), args[0])
.CallProperty(initializer, args,
feedback_index(feedback_spec()->AddCallICSlot()));
}
void BytecodeGenerator::BuildClassLiteralNameProperty(ClassLiteral* expr,
Register literal) {
if (!expr->has_name_static_property() &&
......
......@@ -159,6 +159,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void BuildClassLiteral(ClassLiteral* expr);
void VisitNewTargetVariable(Variable* variable);
void VisitThisFunctionVariable(Variable* variable);
void BuildInstanceFieldInitialization(Variable* initializer_var);
void BuildGeneratorObjectVariableInitialization();
void VisitBlockDeclarationsAndStatements(Block* stmt);
void VisitFunctionClosureForContext();
......
......@@ -561,24 +561,32 @@ class ParserBase {
extends(parser->impl()->NullExpression()),
properties(parser->impl()->NewClassPropertyList(4)),
static_fields(parser->impl()->NewClassPropertyList(4)),
instance_fields(parser->impl()->NewClassPropertyList(4)),
constructor(parser->impl()->NullExpression()),
has_seen_constructor(false),
has_name_static_property(false),
has_static_computed_names(false),
has_static_class_fields(false),
has_instance_class_fields(false),
is_anonymous(false),
field_scope(nullptr) {}
static_fields_scope(nullptr),
instance_fields_scope(nullptr) {}
Variable* variable;
ExpressionT extends;
typename Types::ClassPropertyList properties;
typename Types::ClassPropertyList static_fields;
typename Types::ClassPropertyList instance_fields;
FunctionLiteralT constructor;
// TODO(gsathya): Use a bitfield store all the booleans.
bool has_seen_constructor;
bool has_name_static_property;
bool has_static_computed_names;
bool has_static_class_fields;
bool has_instance_class_fields;
bool is_anonymous;
DeclarationScope* field_scope;
DeclarationScope* static_fields_scope;
DeclarationScope* instance_fields_scope;
};
DeclarationScope* NewScriptScope() const {
......@@ -1104,8 +1112,8 @@ class ParserBase {
bool* is_computed_name, bool* has_seen_constructor,
ClassLiteralProperty::Kind* property_kind, bool* is_static,
bool* has_name_static_property, bool* ok);
FunctionLiteralT ParseClassFieldForInitializer(bool has_initializer,
bool* ok);
ExpressionT ParseClassFieldInitializer(ClassInfo* class_info, bool is_static,
bool* ok);
ObjectLiteralPropertyT ParseObjectPropertyDefinition(
ObjectLiteralChecker* checker, bool* is_computed_name,
bool* is_rest_property, bool* ok);
......@@ -2297,31 +2305,10 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
case PropertyKind::kShorthandProperty:
case PropertyKind::kValueProperty:
if (allow_harmony_class_fields()) {
bool has_initializer = Check(Token::ASSIGN);
ExpressionT initializer;
class_info->has_static_class_fields = true;
if (class_info->field_scope == nullptr) {
class_info->field_scope =
NewFunctionScope(FunctionKind::kConciseMethod);
// TODO(gsathya): Make scopes be non contiguous.
class_info->field_scope->set_start_position(
scanner()->location().end_pos);
scope()->SetLanguageMode(LanguageMode::kStrict);
}
if (has_initializer) {
FunctionState initializer_state(&function_state_, &scope_,
class_info->field_scope);
ExpressionClassifier expression_classifier(this);
initializer =
ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpression));
impl()->RewriteNonPattern(CHECK_OK_CUSTOM(NullExpression));
} else {
initializer = factory()->NewUndefinedLiteral(kNoSourcePosition);
}
class_info->field_scope->set_end_position(
scanner()->location().end_pos);
ExpectSemicolon(CHECK_OK_CUSTOM(NullLiteralProperty));
*property_kind = ClassLiteralProperty::FIELD;
ExpressionT initializer = ParseClassFieldInitializer(
class_info, *is_static, CHECK_OK_CUSTOM(NullLiteralProperty));
ExpectSemicolon(CHECK_OK_CUSTOM(NullLiteralProperty));
ClassLiteralPropertyT result = factory()->NewClassLiteralProperty(
name_expression, initializer, *property_kind, *is_static,
*is_computed_name);
......@@ -2420,36 +2407,43 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
}
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_, initializer_scope);
DCHECK_EQ(initializer_scope, scope());
scope()->SetLanguageMode(LanguageMode::kStrict);
ExpressionClassifier expression_classifier(this);
ExpressionT value;
if (has_initializer) {
value =
this->ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpression));
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info,
bool is_static, bool* ok) {
DeclarationScope* initializer_scope = is_static
? class_info->static_fields_scope
: class_info->instance_fields_scope;
if (initializer_scope == nullptr) {
initializer_scope = NewFunctionScope(FunctionKind::kConciseMethod);
// TODO(gsathya): Make scopes be non contiguous.
initializer_scope->set_start_position(scanner()->location().end_pos);
initializer_scope->SetLanguageMode(LanguageMode::kStrict);
}
ExpressionT initializer;
if (Check(Token::ASSIGN)) {
FunctionState initializer_state(&function_state_, &scope_,
initializer_scope);
ExpressionClassifier expression_classifier(this);
initializer =
ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpression));
impl()->RewriteNonPattern(CHECK_OK_CUSTOM(NullExpression));
} else {
value = factory()->NewUndefinedLiteral(kNoSourcePosition);
initializer = 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.expected_property_count(), 0, 0,
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression, default_eager_compile_hint_,
initializer_scope->start_position(), true, GetNextFunctionLiteralId());
return function_literal;
if (is_static) {
class_info->static_fields_scope = initializer_scope;
class_info->has_static_class_fields = true;
} else {
class_info->instance_fields_scope = initializer_scope;
class_info->has_instance_class_fields = true;
}
return initializer;
}
template <typename Impl>
......
......@@ -3204,6 +3204,20 @@ const AstRawString* ClassFieldVariableName(AstValueFactory* ast_value_factory,
} // namespace
// TODO(gsathya): Ideally, this should just bypass scope analysis and
// allocate a slot directly on the context. We should just store this
// index in the AST, instead of storing the variable.
Variable* Parser::CreateSyntheticContextVariable(const AstRawString* name,
bool* ok) {
VariableProxy* proxy = factory()->NewVariableProxy(name, NORMAL_VARIABLE);
Declaration* declaration =
factory()->NewVariableDeclaration(proxy, kNoSourcePosition);
Variable* var = Declare(declaration, DeclarationDescriptor::NORMAL, CONST,
Variable::DefaultInitializationFlag(CONST), CHECK_OK);
var->ForceContextAllocation();
return var;
}
// This method declares a property of the given class. It updates the
// following fields of class_info, as appropriate:
// - constructor
......@@ -3223,41 +3237,47 @@ void Parser::DeclareClassProperty(const AstRawString* class_name,
return;
}
if (property->kind() == ClassLiteralProperty::FIELD && is_static) {
DCHECK(allow_harmony_class_fields());
if (property->kind() != ClassLiteralProperty::FIELD) {
class_info->properties->Add(property, zone());
return;
}
DCHECK(allow_harmony_class_fields());
if (is_static) {
class_info->static_fields->Add(property, zone());
if (property->is_computed_name()) {
int index = class_info->static_fields->length();
// We create a synthetic variable name here so that scope
// analysis doesn't dedupe the vars.
//
// TODO(gsathya): Ideally, this should just bypass scope
// analysis and allocate a slot directly on the context. We
// should just store this index in the AST, instead of storing
// the variable.
const AstRawString* synthetic_name =
ClassFieldVariableName(ast_value_factory(), index);
VariableProxy* proxy =
factory()->NewVariableProxy(synthetic_name, NORMAL_VARIABLE);
Declaration* declaration =
factory()->NewVariableDeclaration(proxy, kNoSourcePosition);
Variable* computed_name_var =
Declare(declaration, DeclarationDescriptor::NORMAL, CONST,
Variable::DefaultInitializationFlag(CONST), ok);
// Force context allocation because the computed property
// variable will accessed from inside the initializer
// function. We don't actually create a VariableProxy in the
// initializer function scope referring to this variable so
// scope analysis is unable to figure this out for us.
computed_name_var->ForceContextAllocation();
property->set_computed_name_var(computed_name_var);
class_info->properties->Add(property, zone());
}
} else {
class_info->instance_fields->Add(property, zone());
}
int index = class_info->static_fields->length() +
class_info->instance_fields->length();
if (property->is_computed_name()) {
// We create a synthetic variable name here so that scope
// analysis doesn't dedupe the vars.
Variable* computed_name_var = CreateSyntheticContextVariable(
ClassFieldVariableName(ast_value_factory(), index), CHECK_OK_VOID);
property->set_computed_name_var(computed_name_var);
class_info->properties->Add(property, zone());
}
}
FunctionLiteral* Parser::CreateInitializerFunction(
DeclarationScope* scope, ZoneList<ClassLiteral::Property*>* fields) {
// function() { .. class fields initializer .. }
ZoneList<Statement*>* statements = NewStatementList(1);
InitializeClassFieldsStatement* static_fields =
factory()->NewInitializeClassFieldsStatement(fields, kNoSourcePosition);
statements->Add(static_fields, zone());
return factory()->NewFunctionLiteral(
ast_value_factory()->empty_string(), scope, statements, 0, 0, 0,
FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression,
FunctionLiteral::kShouldEagerCompile, scope->start_position(), true,
GetNextFunctionLiteralId());
}
// This method generates a ClassLiteral AST node.
// It uses the following fields of class_info:
// - constructor (if missing, it updates it with a default constructor)
......@@ -3288,25 +3308,31 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope,
FunctionLiteral* static_fields_initializer = nullptr;
if (class_info->has_static_class_fields) {
// function() { .. static class fields initializer .. }
ZoneList<Statement*>* statements = NewStatementList(1);
InitializeClassFieldsStatement* class_fields =
factory()->NewInitializeClassFieldsStatement(class_info->static_fields,
kNoSourcePosition);
statements->Add(class_fields, zone());
static_fields_initializer = factory()->NewFunctionLiteral(
ast_value_factory()->empty_string(), class_info->field_scope,
statements, 0, 0, 0, FunctionLiteral::kNoDuplicateParameters,
FunctionLiteral::kAnonymousExpression,
FunctionLiteral::kShouldEagerCompile,
class_info->field_scope->start_position(), true,
GetNextFunctionLiteralId());
static_fields_initializer = CreateInitializerFunction(
class_info->static_fields_scope, class_info->static_fields);
}
FunctionLiteral* instance_fields_initializer_function = nullptr;
Variable* instance_fields_initializer_var = nullptr;
if (class_info->has_instance_class_fields) {
instance_fields_initializer_function = CreateInitializerFunction(
class_info->instance_fields_scope, class_info->instance_fields);
instance_fields_initializer_var = CreateSyntheticContextVariable(
ast_value_factory()->dot_instance_fields_initializer_string(),
CHECK_OK);
class_info->constructor->set_instance_class_fields_initializer(
instance_fields_initializer_var);
// TODO(gsathya): Add support for lazy parsing instance class fields.
class_info->constructor->SetShouldEagerCompile();
}
ClassLiteral* class_literal = factory()->NewClassLiteral(
block_scope, class_info->variable, class_info->extends,
class_info->constructor, class_info->properties,
static_fields_initializer, pos, end_pos,
static_fields_initializer, instance_fields_initializer_function,
instance_fields_initializer_var, pos, end_pos,
class_info->has_name_static_property,
class_info->has_static_computed_names, class_info->is_anonymous);
......
......@@ -354,6 +354,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
FunctionLiteral* function, VariableMode mode,
int pos, bool is_sloppy_block_function,
ZoneList<const AstRawString*>* names, bool* ok);
Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name,
bool* ok);
FunctionLiteral* CreateInitializerFunction(
DeclarationScope* scope, ZoneList<ClassLiteral::Property*>* fields);
V8_INLINE Statement* DeclareClass(const AstRawString* variable_name,
Expression* value,
ZoneList<const AstRawString*>* names,
......
......@@ -1173,6 +1173,9 @@ class PreParser : public ParserBase<PreParser> {
if (class_info->has_static_class_fields) {
GetNextFunctionLiteralId();
}
if (class_info->has_instance_class_fields) {
GetNextFunctionLiteralId();
}
return PreParserExpression::Default();
}
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-class-fields
"use strict";
{
class C {
a;
}
assertEquals(undefined, C.a);
let c = new C;
let descriptor = Object.getOwnPropertyDescriptor(c, 'a');
assertTrue(c.hasOwnProperty('a'));
assertTrue(descriptor.writable);
assertTrue(descriptor.enumerable);
assertTrue(descriptor.configurable);
assertEquals(undefined, c.a);
}
{
let x = 'a';
class C {
a;
b = x;
c = 1;
hasOwnProperty() { return 1;}
static [x] = 2;
static b = 3;
static d;
}
assertEquals(2, C.a);
assertEquals(3, C.b);
assertEquals(undefined, C.d);
assertEquals(undefined, C.c);
let c = new C;
assertEquals(undefined, c.a);
assertEquals('a', c.b);
assertEquals(1, c.c);
assertEquals(undefined, c.d);
assertEquals(1, c.hasOwnProperty());
}
{
class C {
c = this;
d = () => this;
}
let c = new C;
assertEquals(c, c.c);
assertEquals(c, c.d());
assertEquals(undefined, C.c);
assertEquals(undefined, C.d);
}
{
class C {
c = 1;
d = this.c;
}
let c = new C;
assertEquals(1, c.c);
assertEquals(1, c.d);
assertEquals(undefined, C.c);
assertEquals(undefined, C.d);
}
{
class C {
b = 1;
c = () => this.b;
}
let c = new C;
assertEquals(1, c.b);
assertEquals(1, c.c());
assertEquals(undefined, C.c);
assertEquals(undefined, C.b);
}
{
let x = 'a';
class C {
b = 1;
c = () => this.b;
e = () => x;
}
let c = new C;
assertEquals(1, c.b);
assertEquals('a', c.e());
let a = {b : 2 };
assertEquals(1, c.c.call(a));
assertEquals(undefined, C.b);
assertEquals(undefined, C.c);
}
{
let x = 'a';
class C {
c = 1;
d = function() { return this.c; };
e = function() { return x; };
}
let c = new C;
assertEquals(1, c.c);
assertEquals(1, c.d());
assertEquals('a', c.e());
c.c = 2;
assertEquals(2, c.d());
let a = {c : 3 };
assertEquals(3, c.d.call(a));
assertThrows(c.d.bind(undefined));
assertEquals(undefined, C.c);
assertEquals(undefined, C.d);
assertEquals(undefined, C.e);
}
{
class C {
c = function() { return 1 };
}
let c = new C;
assertEquals('c', c.c.name);
}
{
let d = function() { return new.target; }
class C {
c = d;
}
let c = new C;
assertEquals(undefined, c.c());
assertEquals(new d, new c.c());
}
{
class C {
c = () => new.target;
}
let c = new C;
assertEquals(undefined, c.c());
}
{
let run = false;
class C {
c = () => {
let b;
class A {
constructor() {
b = new.target;
}
};
new A;
run = true;
assertEquals(A, b);
}
}
let c = new C;
c.c();
assertTrue(run);
}
{
class C {
c = new.target;
}
let c = new C;
assertEquals(undefined, c.c);
}
{
class B {
c = 1;
}
class C extends B {}
let c = new C;
assertEquals(1, c.c);
}
{
assertThrows(() => {
class C {
c = new C;
}
let c = new C;
});
}
(function test() {
function makeC() {
var x = 1;
return class {
a = () => () => x;
}
}
let C = makeC();
let c = new C;
let f = c.a();
assertEquals(1, f());
})()
{
let c1 = "c";
class C {
["a"] = 1;
["b"];
[c1];
}
let c = new C;
assertEquals(1, c.a);
assertEquals(undefined, c.b);
assertEquals(undefined, c.c1);
}
{
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
[run(1)] = run(7);
[run(2)] = run(8);
[run(3)]() { run(9);}
static [run(4)] = run(6);
[run(5)]() { throw new Error('should not execute');};
}
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
function x() {
// This tests lazy parsing.
return function() {
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
[run(1)] = run(7);
[run(2)] = run(8);
[run(3)]() { run(9);}
static [run(4)] = run(6);
[run(5)]() { throw new Error('should not execute');};
}
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
}
x();
{
class C {}
class D {
[C];
}
let d = new D;
assertThrows(() => { class X { [X] } let x = new X;});
assertEquals(undefined, d[C]);
}
......@@ -4,12 +4,19 @@
// Flags: --harmony-class-fields
"use strict";
{
class C {
static a;
}
assertEquals(undefined, C.a);
let descriptor = Object.getOwnPropertyDescriptor(C, 'a');
assertTrue(C.hasOwnProperty('a'));
assertTrue(descriptor.writable);
assertTrue(descriptor.enumerable);
assertTrue(descriptor.configurable);
let c = new C;
assertEquals(undefined, c.a);
......@@ -19,6 +26,7 @@
let x = 'a';
class C {
static a;
static hasOwnProperty = function() { return 1; }
static b = x;
static c = 1;
}
......@@ -26,6 +34,7 @@
assertEquals(undefined, C.a);
assertEquals('a', C.b);
assertEquals(1, C.c);
assertEquals(1, C.hasOwnProperty());
let c = new C;
assertEquals(undefined, c.a);
......@@ -47,18 +56,6 @@
assertEquals(undefined, c.d);
}
{
this.c = 1;
class C {
static c = this.c;
}
assertEquals(undefined, C.c);
let c = new C;
assertEquals(undefined, c.c);
}
{
class C {
static c = 1;
......@@ -140,7 +137,7 @@
}
{
d = function() { return new.target; }
let d = function() { return new.target; }
class C {
static c = d;
}
......@@ -247,7 +244,7 @@
})()
{
var c = "c";
let c = "c";
class C {
static ["a"] = 1;
static ["b"];
......@@ -260,21 +257,21 @@
}
{
var log = [];
function eval(i) {
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
static [eval(1)] = eval(6);
static [eval(2)] = eval(7);
[eval(3)]() { eval(9);}
static [eval(4)] = eval(8);
static [eval(5)]() { throw new Error('should not execute');};
static [run(1)] = run(6);
static [run(2)] = run(7);
[run(3)]() { run(9);}
static [run(4)] = run(8);
static [run(5)]() { throw new Error('should not execute');};
}
var c = new C;
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
......@@ -285,25 +282,26 @@ function x() {
// This tests lazy parsing.
return function() {
var log = [];
function eval(i) {
let log = [];
function run(i) {
log.push(i);
return i;
}
class C {
static [eval(1)] = eval(6);
static [eval(2)] = eval(7);
[eval(3)]() { eval(9);}
static [eval(4)] = eval(8);
static [eval(5)]() { throw new Error('should not execute');};
static [run(1)] = run(6);
static [run(2)] = run(7);
[run(3)]() { run(9);}
static [run(4)] = run(8);
static [run(5)]() { throw new Error('should not execute');};
}
var c = new C;
let c = new C;
c[3]();
assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], log);
}
}
x();
{
class C {}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment