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) {
AST_REWRITE_PROPERTY(Expression, node, extends);
}
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();
for (int i = 0; i < properties->length(); i++) {
VisitLiteralProperty(properties->at(i));
......
......@@ -304,6 +304,9 @@ void AstNumberingVisitor::VisitForStatement(ForStatement* node) {
void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) {
if (node->extends()) Visit(node->extends());
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++) {
VisitLiteralProperty(node->properties()->at(i));
}
......
......@@ -472,6 +472,9 @@ void AstTraversalVisitor<Subclass>::VisitClassLiteral(ClassLiteral* expr) {
RECURSE_EXPRESSION(Visit(expr->extends()));
}
RECURSE_EXPRESSION(Visit(expr->constructor()));
if (expr->static_fields_initializer() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->static_fields_initializer()));
}
ZoneList<ClassLiteralProperty*>* props = expr->properties();
for (int i = 0; i < props->length(); ++i) {
ClassLiteralProperty* prop = props->at(i);
......
......@@ -2411,21 +2411,30 @@ class ClassLiteral final : public 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:
friend class AstNodeFactory;
ClassLiteral(Scope* scope, Variable* class_variable, Expression* extends,
FunctionLiteral* constructor, ZoneList<Property*>* properties,
int start_position, int end_position,
bool has_name_static_property, bool has_static_computed_names,
bool is_anonymous)
FunctionLiteral* static_fields_initializer, int start_position,
int end_position, bool has_name_static_property,
bool has_static_computed_names, bool is_anonymous)
: Expression(start_position, kClassLiteral),
end_position_(end_position),
scope_(scope),
class_variable_(class_variable),
extends_(extends),
constructor_(constructor),
properties_(properties) {
properties_(properties),
static_fields_initializer_(static_fields_initializer) {
bit_field_ |= HasNameStaticProperty::encode(has_name_static_property) |
HasStaticComputedNames::encode(has_static_computed_names) |
IsAnonymousExpression::encode(is_anonymous);
......@@ -2437,6 +2446,7 @@ class ClassLiteral final : public Expression {
Expression* extends_;
FunctionLiteral* constructor_;
ZoneList<Property*>* properties_;
FunctionLiteral* static_fields_initializer_;
class HasNameStaticProperty
: public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
......@@ -3196,14 +3206,15 @@ class AstNodeFactory final BASE_EMBEDDED {
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) {
return new (zone_)
ClassLiteral(scope, variable, extends, constructor, properties,
start_position, end_position, has_name_static_property,
has_static_computed_names, is_anonymous);
return new (zone_) ClassLiteral(
scope, variable, extends, constructor, properties,
static_fields_initializer, start_position, end_position,
has_name_static_property, has_static_computed_names, is_anonymous);
}
NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name,
......
......@@ -1747,9 +1747,7 @@ void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
}
void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
// TODO(gsathya): Fix the DCHECK once class literals use do expressions.
DCHECK(expr->scope()->outer_scope() == current_scope() ||
FLAG_harmony_class_fields);
DCHECK(expr->scope()->outer_scope() == current_scope());
uint8_t flags = CreateClosureFlags::Encode(
expr->pretenure(), closure_scope()->is_function_scope());
size_t entry = builder()->AllocateDeferredConstantPoolEntry();
......@@ -1786,6 +1784,7 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
VisitClassLiteralProperties(expr, constructor, prototype);
BuildClassLiteralNameProperty(expr, constructor);
// TODO(gsathya): Run this after initializing class static fields.
builder()->CallRuntime(Runtime::kToFastProperties, constructor);
// Assign to class variable.
if (expr->class_variable() != nullptr) {
......@@ -1794,6 +1793,17 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
BuildVariableAssignment(expr->class_variable(), Token::INIT,
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) {
......
......@@ -45,6 +45,12 @@ void Reparenter::VisitClassLiteral(ClassLiteral* class_literal) {
// scope on its scope chain.
DCHECK_EQ(class_literal->constructor()->scope()->outer_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
// The same goes for the rest of the class, but we do some
// sanity checking in debug mode.
......
......@@ -565,6 +565,7 @@ class ParserBase {
has_seen_constructor(false),
has_name_static_property(false),
has_static_computed_names(false),
has_static_class_fields(false),
is_anonymous(false),
field_scope(nullptr) {}
Variable* variable;
......@@ -575,6 +576,7 @@ class ParserBase {
bool has_seen_constructor;
bool has_name_static_property;
bool has_static_computed_names;
bool has_static_class_fields;
bool is_anonymous;
DeclarationScope* field_scope;
};
......@@ -2297,6 +2299,7 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
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);
......
......@@ -3252,72 +3252,33 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope,
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(
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_static_computed_names, class_info->is_anonymous);
AddFunctionForNameInference(class_info->constructor);
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;
return class_literal;
}
void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
......
......@@ -1170,6 +1170,9 @@ class PreParser : public ParserBase<PreParser> {
FunctionState function_state(&function_state_, &scope_, function_scope);
GetNextFunctionLiteralId();
}
if (class_info->has_static_class_fields) {
GetNextFunctionLiteralId();
}
return PreParserExpression::Default();
}
......
......@@ -216,33 +216,45 @@
}
{
let C = class {
static c;
};
// {
// let C = class {
// static c;
// };
assertEquals("C", C.name);
}
// assertEquals("C", C.name);
// }
{
class C {
static c = new C;
}
assertTrue(C.c instanceof C);
}
// {
// class C {
// static c = new C;
// }
(function test() {
function makeC() {
var x = 1;
// assertTrue(c instanceof C);
// }
return class {
static a = () => () => x;
}
}
// (function test() {
// function makeC() {
// var x = 1;
let C = makeC();
let f = C.a();
assertEquals(1, f());
})()
// return class {
// static a = () => () => x;
// }
// }
{
var c = "c";
class C {
static ["a"] = 1;
static ["b"];
static [c];
}
// let C = makeC();
// let f = C.a();
// assertEquals(1, f());
// })()
assertEquals(1, C.a);
assertEquals(undefined, C.b);
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