Commit 753edc41 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[Class] Remove initializer function call desugaring

Instead of creating a runtime call for the static class field
initializer in the AST, we do it in the bytecode generator.

This adds the initializer function to the ClassLiteral AST node.

Bug: v8:5367
Change-Id: Iffaa6531511023812011ee19fc96cea9e5c9d3f3
Reviewed-on: https://chromium-review.googlesource.com/736315Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49008}
parent 488faeb6
...@@ -201,6 +201,9 @@ void AstExpressionRewriter::VisitClassLiteral(ClassLiteral* node) { ...@@ -201,6 +201,9 @@ void AstExpressionRewriter::VisitClassLiteral(ClassLiteral* node) {
AST_REWRITE_PROPERTY(Expression, node, extends); AST_REWRITE_PROPERTY(Expression, node, extends);
} }
AST_REWRITE_PROPERTY(FunctionLiteral, node, constructor); AST_REWRITE_PROPERTY(FunctionLiteral, node, constructor);
if (node->static_fields_initializer() != nullptr) {
AST_REWRITE_PROPERTY(FunctionLiteral, node, static_fields_initializer);
}
ZoneList<typename ClassLiteral::Property*>* properties = node->properties(); ZoneList<typename ClassLiteral::Property*>* properties = node->properties();
for (int i = 0; i < properties->length(); i++) { for (int i = 0; i < properties->length(); i++) {
VisitLiteralProperty(properties->at(i)); VisitLiteralProperty(properties->at(i));
......
...@@ -304,6 +304,9 @@ void AstNumberingVisitor::VisitForStatement(ForStatement* node) { ...@@ -304,6 +304,9 @@ void AstNumberingVisitor::VisitForStatement(ForStatement* node) {
void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) { void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) {
if (node->extends()) Visit(node->extends()); if (node->extends()) Visit(node->extends());
if (node->constructor()) Visit(node->constructor()); if (node->constructor()) Visit(node->constructor());
if (node->static_fields_initializer() != nullptr) {
Visit(node->static_fields_initializer());
}
for (int i = 0; i < node->properties()->length(); i++) { for (int i = 0; i < node->properties()->length(); i++) {
VisitLiteralProperty(node->properties()->at(i)); VisitLiteralProperty(node->properties()->at(i));
} }
......
...@@ -472,6 +472,9 @@ void AstTraversalVisitor<Subclass>::VisitClassLiteral(ClassLiteral* expr) { ...@@ -472,6 +472,9 @@ void AstTraversalVisitor<Subclass>::VisitClassLiteral(ClassLiteral* expr) {
RECURSE_EXPRESSION(Visit(expr->extends())); RECURSE_EXPRESSION(Visit(expr->extends()));
} }
RECURSE_EXPRESSION(Visit(expr->constructor())); RECURSE_EXPRESSION(Visit(expr->constructor()));
if (expr->static_fields_initializer() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->static_fields_initializer()));
}
ZoneList<ClassLiteralProperty*>* props = expr->properties(); ZoneList<ClassLiteralProperty*>* props = expr->properties();
for (int i = 0; i < props->length(); ++i) { for (int i = 0; i < props->length(); ++i) {
ClassLiteralProperty* prop = props->at(i); ClassLiteralProperty* prop = props->at(i);
......
...@@ -2411,21 +2411,30 @@ class ClassLiteral final : public Expression { ...@@ -2411,21 +2411,30 @@ class ClassLiteral final : public Expression {
return is_anonymous_expression(); return is_anonymous_expression();
} }
FunctionLiteral* static_fields_initializer() const {
return static_fields_initializer_;
}
void set_static_fields_initializer(FunctionLiteral* initializer) {
static_fields_initializer_ = initializer;
}
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
ClassLiteral(Scope* scope, Variable* class_variable, Expression* extends, ClassLiteral(Scope* scope, Variable* class_variable, Expression* extends,
FunctionLiteral* constructor, ZoneList<Property*>* properties, FunctionLiteral* constructor, ZoneList<Property*>* properties,
int start_position, int end_position, FunctionLiteral* static_fields_initializer, int start_position,
bool has_name_static_property, bool has_static_computed_names, int end_position, bool has_name_static_property,
bool is_anonymous) bool has_static_computed_names, bool is_anonymous)
: Expression(start_position, kClassLiteral), : Expression(start_position, kClassLiteral),
end_position_(end_position), end_position_(end_position),
scope_(scope), scope_(scope),
class_variable_(class_variable), class_variable_(class_variable),
extends_(extends), extends_(extends),
constructor_(constructor), constructor_(constructor),
properties_(properties) { properties_(properties),
static_fields_initializer_(static_fields_initializer) {
bit_field_ |= HasNameStaticProperty::encode(has_name_static_property) | bit_field_ |= HasNameStaticProperty::encode(has_name_static_property) |
HasStaticComputedNames::encode(has_static_computed_names) | HasStaticComputedNames::encode(has_static_computed_names) |
IsAnonymousExpression::encode(is_anonymous); IsAnonymousExpression::encode(is_anonymous);
...@@ -2437,6 +2446,7 @@ class ClassLiteral final : public Expression { ...@@ -2437,6 +2446,7 @@ class ClassLiteral final : public Expression {
Expression* extends_; Expression* extends_;
FunctionLiteral* constructor_; FunctionLiteral* constructor_;
ZoneList<Property*>* properties_; ZoneList<Property*>* properties_;
FunctionLiteral* static_fields_initializer_;
class HasNameStaticProperty class HasNameStaticProperty
: public BitField<bool, Expression::kNextBitFieldIndex, 1> {}; : public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
...@@ -3196,14 +3206,15 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3196,14 +3206,15 @@ class AstNodeFactory final BASE_EMBEDDED {
Expression* extends, Expression* extends,
FunctionLiteral* constructor, FunctionLiteral* constructor,
ZoneList<ClassLiteral::Property*>* properties, ZoneList<ClassLiteral::Property*>* properties,
FunctionLiteral* static_fields_initializer,
int start_position, int end_position, int start_position, int end_position,
bool has_name_static_property, bool has_name_static_property,
bool has_static_computed_names, bool has_static_computed_names,
bool is_anonymous) { bool is_anonymous) {
return new (zone_) return new (zone_) ClassLiteral(
ClassLiteral(scope, variable, extends, constructor, properties, scope, variable, extends, constructor, properties,
start_position, end_position, has_name_static_property, static_fields_initializer, start_position, end_position,
has_static_computed_names, is_anonymous); has_name_static_property, has_static_computed_names, is_anonymous);
} }
NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name, NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name,
......
...@@ -1747,9 +1747,7 @@ void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { ...@@ -1747,9 +1747,7 @@ void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
} }
void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
// TODO(gsathya): Fix the DCHECK once class literals use do expressions. DCHECK(expr->scope()->outer_scope() == current_scope());
DCHECK(expr->scope()->outer_scope() == current_scope() ||
FLAG_harmony_class_fields);
uint8_t flags = CreateClosureFlags::Encode( uint8_t flags = CreateClosureFlags::Encode(
expr->pretenure(), closure_scope()->is_function_scope()); expr->pretenure(), closure_scope()->is_function_scope());
size_t entry = builder()->AllocateDeferredConstantPoolEntry(); size_t entry = builder()->AllocateDeferredConstantPoolEntry();
...@@ -1786,6 +1784,7 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) { ...@@ -1786,6 +1784,7 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
VisitClassLiteralProperties(expr, constructor, prototype); VisitClassLiteralProperties(expr, constructor, prototype);
BuildClassLiteralNameProperty(expr, constructor); BuildClassLiteralNameProperty(expr, constructor);
// TODO(gsathya): Run this after initializing class static fields.
builder()->CallRuntime(Runtime::kToFastProperties, constructor); builder()->CallRuntime(Runtime::kToFastProperties, constructor);
// Assign to class variable. // Assign to class variable.
if (expr->class_variable() != nullptr) { if (expr->class_variable() != nullptr) {
...@@ -1794,6 +1793,17 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) { ...@@ -1794,6 +1793,17 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
BuildVariableAssignment(expr->class_variable(), Token::INIT, BuildVariableAssignment(expr->class_variable(), Token::INIT,
HoleCheckMode::kElided); HoleCheckMode::kElided);
} }
if (expr->static_fields_initializer() != nullptr) {
RegisterList args = register_allocator()->NewRegisterList(1);
Register initializer = register_allocator()->NewRegister();
VisitForRegisterValue(expr->static_fields_initializer(), initializer);
builder()
->MoveRegister(constructor, args[0])
.CallProperty(initializer, args,
feedback_index(feedback_spec()->AddCallICSlot()))
.LoadAccumulatorWithRegister(constructor);
}
} }
void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr) { void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
......
...@@ -45,6 +45,12 @@ void Reparenter::VisitClassLiteral(ClassLiteral* class_literal) { ...@@ -45,6 +45,12 @@ void Reparenter::VisitClassLiteral(ClassLiteral* class_literal) {
// scope on its scope chain. // scope on its scope chain.
DCHECK_EQ(class_literal->constructor()->scope()->outer_scope(), DCHECK_EQ(class_literal->constructor()->scope()->outer_scope(),
class_literal->scope()); class_literal->scope());
if (class_literal->static_fields_initializer() != nullptr) {
DCHECK_EQ(
class_literal->static_fields_initializer()->scope()->outer_scope(),
class_literal->scope());
}
#if DEBUG #if DEBUG
// The same goes for the rest of the class, but we do some // The same goes for the rest of the class, but we do some
// sanity checking in debug mode. // sanity checking in debug mode.
......
...@@ -565,6 +565,7 @@ class ParserBase { ...@@ -565,6 +565,7 @@ class ParserBase {
has_seen_constructor(false), has_seen_constructor(false),
has_name_static_property(false), has_name_static_property(false),
has_static_computed_names(false), has_static_computed_names(false),
has_static_class_fields(false),
is_anonymous(false), is_anonymous(false),
field_scope(nullptr) {} field_scope(nullptr) {}
Variable* variable; Variable* variable;
...@@ -575,6 +576,7 @@ class ParserBase { ...@@ -575,6 +576,7 @@ class ParserBase {
bool has_seen_constructor; bool has_seen_constructor;
bool has_name_static_property; bool has_name_static_property;
bool has_static_computed_names; bool has_static_computed_names;
bool has_static_class_fields;
bool is_anonymous; bool is_anonymous;
DeclarationScope* field_scope; DeclarationScope* field_scope;
}; };
...@@ -2297,6 +2299,7 @@ ParserBase<Impl>::ParseClassPropertyDefinition( ...@@ -2297,6 +2299,7 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
if (allow_harmony_class_fields()) { if (allow_harmony_class_fields()) {
bool has_initializer = Check(Token::ASSIGN); bool has_initializer = Check(Token::ASSIGN);
ExpressionT initializer; ExpressionT initializer;
class_info->has_static_class_fields = true;
if (class_info->field_scope == nullptr) { if (class_info->field_scope == nullptr) {
class_info->field_scope = class_info->field_scope =
NewFunctionScope(FunctionKind::kConciseMethod); NewFunctionScope(FunctionKind::kConciseMethod);
......
...@@ -3252,72 +3252,33 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope, ...@@ -3252,72 +3252,33 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope,
class_info->variable->set_initializer_position(end_pos); class_info->variable->set_initializer_position(end_pos);
} }
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,
class_info->field_scope->NeedsHomeObject(), 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());
}
ClassLiteral* class_literal = factory()->NewClassLiteral( ClassLiteral* class_literal = factory()->NewClassLiteral(
block_scope, class_info->variable, class_info->extends, block_scope, class_info->variable, class_info->extends,
class_info->constructor, class_info->properties, pos, end_pos, class_info->constructor, class_info->properties,
static_fields_initializer, pos, end_pos,
class_info->has_name_static_property, class_info->has_name_static_property,
class_info->has_static_computed_names, class_info->is_anonymous); class_info->has_static_computed_names, class_info->is_anonymous);
AddFunctionForNameInference(class_info->constructor); AddFunctionForNameInference(class_info->constructor);
return class_literal;
Expression* result = class_literal;
if (class_info->static_fields->length() > 0) {
// The class literal is rewritten to:
// do {
// temp = class { .. };
// %_Call(function() { ... static class fields initializer ... }, temp);
// temp;
// }
Block* do_block = factory()->NewBlock(2, false);
Variable* class_var = NewTemporary(ast_value_factory()->empty_string());
{
// temp = class { .. };
VariableProxy* class_proxy = factory()->NewVariableProxy(class_var);
Assignment* assign_class_proxy = factory()->NewAssignment(
Token::ASSIGN, class_proxy, class_literal, kNoSourcePosition);
do_block->statements()->Add(factory()->NewExpressionStatement(
assign_class_proxy, kNoSourcePosition),
zone());
}
FunctionLiteral* initializer;
{
// function() { .. static class fields initializer .. }
ZoneList<Statement*>* statements = NewStatementList(1);
InitializeClassFieldsStatement* class_fields =
factory()->NewInitializeClassFieldsStatement(
class_info->static_fields,
class_info->field_scope->NeedsHomeObject(), kNoSourcePosition);
statements->Add(class_fields, zone());
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());
}
{
// %_Call(function() { .. initializer .. }, temp);
auto args = new (zone()) ZoneList<Expression*>(2, zone());
args->Add(initializer, zone());
args->Add(factory()->NewVariableProxy(class_var), zone());
Expression* call = factory()->NewCallRuntime(Runtime::kInlineCall, args,
kNoSourcePosition);
do_block->statements()->Add(
factory()->NewExpressionStatement(call, kNoSourcePosition), zone());
}
result = factory()->NewDoExpression(do_block, class_var,
class_literal->position());
}
return result;
} }
void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) { void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
......
...@@ -1170,6 +1170,9 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1170,6 +1170,9 @@ class PreParser : public ParserBase<PreParser> {
FunctionState function_state(&function_state_, &scope_, function_scope); FunctionState function_state(&function_state_, &scope_, function_scope);
GetNextFunctionLiteralId(); GetNextFunctionLiteralId();
} }
if (class_info->has_static_class_fields) {
GetNextFunctionLiteralId();
}
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
......
...@@ -216,33 +216,45 @@ ...@@ -216,33 +216,45 @@
} }
{
let C = class {
static c;
};
// { assertEquals("C", C.name);
// let C = class { }
// static c;
// };
// assertEquals("C", C.name); {
// } class C {
static c = new C;
}
assertTrue(C.c instanceof C);
}
// { (function test() {
// class C { function makeC() {
// static c = new C; var x = 1;
// }
// assertTrue(c instanceof C); return class {
// } static a = () => () => x;
}
}
// (function test() { let C = makeC();
// function makeC() { let f = C.a();
// var x = 1; assertEquals(1, f());
})()
// return class { {
// static a = () => () => x; var c = "c";
// } class C {
// } static ["a"] = 1;
static ["b"];
static [c];
}
// let C = makeC(); assertEquals(1, C.a);
// let f = C.a(); assertEquals(undefined, C.b);
// assertEquals(1, f()); assertEquals(undefined, C.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