Commit 049844a1 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[class] Implement static public class fields

This patch implements the runtime semantics of static public
class fields.

Adds a new InitializeClassFieldsStatement AST node that contains
all the static class fields and their initializers. 

ClassLiteral is now desugared to be included in a do-exp that calls 
an initializer function which contains this new AST node.

Bug: v8:5367
Change-Id: I3574e4c685f1c039de42521c122e24f8d28e5d6c
Reviewed-on: https://chromium-review.googlesource.com/714817Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48835}
parent d8550099
......@@ -207,6 +207,14 @@ void AstExpressionRewriter::VisitClassLiteral(ClassLiteral* node) {
}
}
void AstExpressionRewriter::VisitInitializeClassFieldsStatement(
InitializeClassFieldsStatement* node) {
ZoneList<typename ClassLiteral::Property*>* properties = node->fields();
for (int i = 0; i < properties->length(); i++) {
VisitLiteralProperty(properties->at(i));
}
}
void AstExpressionRewriter::VisitNativeFunctionLiteral(
NativeFunctionLiteral* node) {
REWRITE_THIS(node);
......
......@@ -302,6 +302,13 @@ void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) {
}
}
void AstNumberingVisitor::VisitInitializeClassFieldsStatement(
InitializeClassFieldsStatement* node) {
for (int i = 0; i < node->fields()->length(); i++) {
VisitLiteralProperty(node->fields()->at(i));
}
}
void AstNumberingVisitor::VisitObjectLiteral(ObjectLiteral* node) {
for (int i = 0; i < node->properties()->length(); i++) {
VisitLiteralProperty(node->properties()->at(i));
......
......@@ -473,6 +473,20 @@ void AstTraversalVisitor<Subclass>::VisitClassLiteral(ClassLiteral* expr) {
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitInitializeClassFieldsStatement(
InitializeClassFieldsStatement* stmt) {
PROCESS_NODE(stmt);
ZoneList<ClassLiteralProperty*>* props = stmt->fields();
for (int i = 0; i < props->length(); ++i) {
ClassLiteralProperty* prop = props->at(i);
if (!prop->key()->IsLiteral()) {
RECURSE(Visit(prop->key()));
}
RECURSE(Visit(prop->value()));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSpread(Spread* expr) {
PROCESS_EXPRESSION(expr);
......
......@@ -61,7 +61,8 @@ namespace internal {
V(WithStatement) \
V(TryCatchStatement) \
V(TryFinallyStatement) \
V(DebuggerStatement)
V(DebuggerStatement) \
V(InitializeClassFieldsStatement)
#define LITERAL_NODE_LIST(V) \
V(RegExpLiteral) \
......@@ -2270,6 +2271,25 @@ class ClassLiteralProperty final : public LiteralProperty {
bool is_static_;
};
class InitializeClassFieldsStatement final : public Statement {
public:
typedef ClassLiteralProperty Property;
ZoneList<Property*>* fields() const { return fields_; }
bool needs_home_object() const { return needs_home_object_; }
private:
friend class AstNodeFactory;
InitializeClassFieldsStatement(ZoneList<Property*>* fields,
bool needs_home_object, int pos)
: Statement(pos, kInitializeClassFieldsStatement),
fields_(fields),
needs_home_object_(needs_home_object) {}
ZoneList<Property*>* fields_;
bool needs_home_object_;
};
class ClassLiteral final : public Expression {
public:
typedef ClassLiteralProperty Property;
......@@ -3137,6 +3157,12 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) ImportCallExpression(args, pos);
}
InitializeClassFieldsStatement* NewInitializeClassFieldsStatement(
ZoneList<ClassLiteralProperty*>* args, bool needs_home_object, int pos) {
return new (zone_)
InitializeClassFieldsStatement(args, needs_home_object, pos);
}
Zone* zone() const { return zone_; }
void set_zone(Zone* zone) { zone_ = zone; }
......
......@@ -198,6 +198,12 @@ void CallPrinter::VisitClassLiteral(ClassLiteral* node) {
}
}
void CallPrinter::VisitInitializeClassFieldsStatement(
InitializeClassFieldsStatement* node) {
for (int i = 0; i < node->fields()->length(); i++) {
Find(node->fields()->at(i)->value());
}
}
void CallPrinter::VisitNativeFunctionLiteral(NativeFunctionLiteral* node) {}
......@@ -951,6 +957,12 @@ void AstPrinter::VisitClassLiteral(ClassLiteral* node) {
PrintClassProperties(node->properties());
}
void AstPrinter::VisitInitializeClassFieldsStatement(
InitializeClassFieldsStatement* node) {
IndentedScope indent(this, "INITIALIZE CLASS FIELDS", node->position());
PrintClassProperties(node->fields());
}
void AstPrinter::PrintClassProperties(
ZoneList<ClassLiteral::Property*>* properties) {
for (int i = 0; i < properties->length(); i++) {
......
......@@ -1747,7 +1747,9 @@ void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
}
void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
DCHECK_EQ(expr->scope()->outer_scope(), current_scope());
// TODO(gsathya): Fix the DCHECK once class literals use do expressions.
DCHECK(expr->scope()->outer_scope() == current_scope() ||
FLAG_harmony_class_fields);
uint8_t flags = CreateClosureFlags::Encode(
expr->pretenure(), closure_scope()->is_function_scope());
size_t entry = builder()->AllocateDeferredConstantPoolEntry();
......@@ -1883,6 +1885,36 @@ void BytecodeGenerator::VisitClassLiteralProperties(ClassLiteral* expr,
}
}
void BytecodeGenerator::VisitInitializeClassFieldsStatement(
InitializeClassFieldsStatement* expr) {
Register constructor(builder()->Receiver());
if (expr->needs_home_object()) {
FeedbackSlot slot = feedback_spec()->AddStoreICSlot(language_mode());
builder()
->LoadAccumulatorWithRegister(constructor)
.StoreHomeObjectProperty(Register::function_closure(),
feedback_index(slot), language_mode());
}
Register key = register_allocator()->NewRegister();
Register value = register_allocator()->NewRegister();
// TODO(gsathya): Fix evaluation order for computed properties.
for (int i = 0; i < expr->fields()->length(); i++) {
ClassLiteral::Property* property = expr->fields()->at(i);
BuildLoadPropertyKey(property, key);
DataPropertyInLiteralFlags flags = DataPropertyInLiteralFlag::kNoFlags;
FeedbackSlot slot = feedback_spec()->AddStoreDataPropertyInLiteralICSlot();
VisitForRegisterValue(property->value(), value);
VisitSetHomeObject(value, constructor, property);
builder()->LoadAccumulatorWithRegister(value).StoreDataPropertyInLiteral(
constructor, key, flags, feedback_index(slot));
}
}
void BytecodeGenerator::BuildClassLiteralNameProperty(ClassLiteral* expr,
Register literal) {
if (!expr->has_name_static_property() &&
......
......@@ -563,19 +563,23 @@ class ParserBase {
: variable(nullptr),
extends(parser->impl()->NullExpression()),
properties(parser->impl()->NewClassPropertyList(4)),
static_fields(parser->impl()->NewClassPropertyList(4)),
constructor(parser->impl()->NullExpression()),
has_seen_constructor(false),
has_name_static_property(false),
has_static_computed_names(false),
is_anonymous(false) {}
is_anonymous(false),
field_scope(nullptr) {}
Variable* variable;
ExpressionT extends;
typename Types::ClassPropertyList properties;
typename Types::ClassPropertyList static_fields;
FunctionLiteralT constructor;
bool has_seen_constructor;
bool has_name_static_property;
bool has_static_computed_names;
bool is_anonymous;
DeclarationScope* field_scope;
};
DeclarationScope* NewScriptScope() const {
......@@ -1088,9 +1092,10 @@ class ParserBase {
bool* ok);
ExpressionT ParseObjectLiteral(bool* ok);
ClassLiteralPropertyT ParseClassPropertyDefinition(
ClassLiteralChecker* checker, bool has_extends, bool* is_computed_name,
bool* has_seen_constructor, ClassLiteralProperty::Kind* property_kind,
bool* is_static, bool* has_name_static_property, bool* ok);
ClassLiteralChecker* checker, ClassInfo* class_info, bool has_extends,
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);
ObjectLiteralPropertyT ParseObjectPropertyDefinition(
......@@ -2226,9 +2231,10 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName(
template <typename Impl>
typename ParserBase<Impl>::ClassLiteralPropertyT
ParserBase<Impl>::ParseClassPropertyDefinition(
ClassLiteralChecker* checker, bool has_extends, bool* is_computed_name,
bool* has_seen_constructor, ClassLiteralProperty::Kind* property_kind,
bool* is_static, bool* has_name_static_property, bool* ok) {
ClassLiteralChecker* checker, ClassInfo* class_info, bool has_extends,
bool* is_computed_name, bool* has_seen_constructor,
ClassLiteralProperty::Kind* property_kind, bool* is_static,
bool* has_name_static_property, bool* ok) {
DCHECK_NOT_NULL(has_seen_constructor);
DCHECK_NOT_NULL(has_name_static_property);
bool is_get = false;
......@@ -2284,12 +2290,31 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
case PropertyKind::kValueProperty:
if (allow_harmony_class_fields()) {
bool has_initializer = Check(Token::ASSIGN);
ExpressionT function_literal = ParseClassFieldForInitializer(
has_initializer, CHECK_OK_CUSTOM(NullLiteralProperty));
ExpressionT initializer;
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;
ClassLiteralPropertyT result = factory()->NewClassLiteralProperty(
name_expression, function_literal, *property_kind, *is_static,
name_expression, initializer, *property_kind, *is_static,
*is_computed_name);
impl()->SetFunctionNameFromPropertyName(result, name);
return result;
......@@ -4448,7 +4473,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
// property.
bool is_constructor = !class_info.has_seen_constructor;
ClassLiteralPropertyT property = ParseClassPropertyDefinition(
&checker, has_extends, &is_computed_name,
&checker, &class_info, has_extends, &is_computed_name,
&class_info.has_seen_constructor, &property_kind, &is_static,
&class_info.has_name_static_property, CHECK_OK);
if (!class_info.has_static_computed_names && is_static &&
......
......@@ -3166,11 +3166,12 @@ void Parser::DeclareClassProperty(const AstRawString* class_name,
return;
}
if (property->kind() == ClassLiteralProperty::FIELD) {
if (property->kind() == ClassLiteralProperty::FIELD && is_static) {
DCHECK(allow_harmony_class_fields());
// TODO(littledan): Implement class fields
class_info->static_fields->Add(property, zone());
} else {
class_info->properties->Add(property, zone());
}
class_info->properties->Add(property, zone());
}
// This method generates a ClassLiteral AST node.
......@@ -3209,7 +3210,64 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope,
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) {
......
......@@ -772,6 +772,7 @@ NOT_A_PATTERN(WithStatement)
NOT_A_PATTERN(Yield)
NOT_A_PATTERN(YieldStar)
NOT_A_PATTERN(Await)
NOT_A_PATTERN(InitializeClassFieldsStatement)
#undef NOT_A_PATTERN
} // namespace internal
......
......@@ -335,6 +335,10 @@ void Processor::VisitDebuggerStatement(DebuggerStatement* node) {
replacement_ = node;
}
void Processor::VisitInitializeClassFieldsStatement(
InitializeClassFieldsStatement* node) {
replacement_ = node;
}
// Expressions are never visited.
#define DEF_VISIT(type) \
......
// 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
{
class C {
static a;
}
assertEquals(undefined, C.a);
let c = new C;
assertEquals(undefined, c.a);
}
{
let x = 'a';
class C {
static a;
static b = x;
static c = 1;
}
assertEquals(undefined, C.a);
assertEquals('a', C.b);
assertEquals(1, C.c);
let c = new C;
assertEquals(undefined, c.a);
assertEquals(undefined, c.b);
assertEquals(undefined, c.c);
}
{
class C {
static c = this;
static d = () => this;
}
assertEquals(C, C.c);
assertEquals(C, C.d());
let c = new C;
assertEquals(undefined, c.c);
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;
static d = this.c;
}
assertEquals(1, C.c);
assertEquals(1, C.d);
let c = new C;
assertEquals(undefined, c.c);
assertEquals(undefined, c.d);
}
{
class C {
static b = 1;
static c = () => this.b;
}
assertEquals(1, C.b);
assertEquals(1, C.c());
let c = new C;
assertEquals(undefined, c.c);
}
{
let x = 'a';
class C {
static b = 1;
static c = () => this.b;
static e = () => x;
}
assertEquals(1, C.b);
assertEquals('a', C.e());
let a = {b : 2 };
assertEquals(1, C.c.call(a));
let c = new C;
assertEquals(undefined, c.b);
assertEquals(undefined, c.c);
}
{
let x = 'a';
class C {
static c = 1;
static d = function() { return this.c; };
static e = function() { return x; };
}
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));
let c = new C;
assertEquals(undefined, c.c);
assertEquals(undefined, c.d);
assertEquals(undefined, c.e);
}
{
class C {
static c = function() { return 1 };
}
assertEquals('c', C.c.name);
}
{
d = function() { return new.target; }
class C {
static c = d;
}
assertEquals(undefined, C.c());
assertEquals(new d, new C.c());
}
{
class C {
static c = () => new.target;
}
assertEquals(undefined, C.c());
}
{
class C {
static c = () => {
let b;
class A {
constructor() {
b = new.target;
}
};
new A;
assertEquals(A, b);
}
}
C.c();
}
{
class C {
static c = new.target;
}
assertEquals(undefined, C.c);
}
{
class B {
static d = 1;
static b = () => this.d;
}
class C extends B {
static c = super.d;
static d = () => super.d;
static e = () => super.b();
}
assertEquals(1, C.c);
assertEquals(1, C.d());
assertEquals(1, C.e());
}
{
let foo = undefined;
class B {
static set d(x) {
foo = x;
}
}
class C extends B {
static d = 2;
}
assertEquals(undefined, foo);
assertEquals(2, C.d);
}
// {
// let C = class {
// static c;
// };
// assertEquals("C", C.name);
// }
// {
// class C {
// static c = new C;
// }
// assertTrue(c instanceof C);
// }
// (function test() {
// function makeC() {
// var x = 1;
// return class {
// static a = () => () => x;
// }
// }
// let C = makeC();
// let f = C.a();
// assertEquals(1, f());
// })()
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