Commit 0f418385 authored by mstarzinger's avatar mstarzinger Committed by Commit bot

First simple implementation of class literals in TurboFan.

R=rossberg@chromium.org,jarin@chromium.org
TEST=cctest/test-run-jsops/ClassLiteral

Review URL: https://codereview.chromium.org/798873006

Cr-Commit-Position: refs/heads/master@{#26101}
parent 5a117601
......@@ -452,7 +452,7 @@ void AstNumberingVisitor::VisitForStatement(ForStatement* node) {
void AstNumberingVisitor::VisitClassLiteral(ClassLiteral* node) {
IncrementNodeCount();
DisableTurbofan(kClassLiteral);
DisableCrankshaft(kClassLiteral);
node->set_base_id(ReserveIdRange(ClassLiteral::num_ids()));
if (node->extends()) Visit(node->extends());
if (node->constructor()) Visit(node->constructor());
......
......@@ -2628,6 +2628,11 @@ class ClassLiteral FINAL : public Expression {
int start_position() const { return position(); }
int end_position() const { return end_position_; }
static int num_ids() { return parent_num_ids() + 3; }
BailoutId EntryId() const { return BailoutId(local_id(0)); }
BailoutId DeclsId() const { return BailoutId(local_id(1)); }
BailoutId ExitId() { return BailoutId(local_id(2)); }
protected:
ClassLiteral(Zone* zone, const AstRawString* name, Scope* scope,
VariableProxy* class_variable_proxy, Expression* extends,
......@@ -2641,8 +2646,11 @@ class ClassLiteral FINAL : public Expression {
constructor_(constructor),
properties_(properties),
end_position_(end_position) {}
static int parent_num_ids() { return Expression::num_ids(); }
private:
int local_id(int n) const { return base_id() + parent_num_ids() + n; }
const AstRawString* raw_name_;
Scope* scope_;
VariableProxy* class_variable_proxy_;
......
......@@ -320,6 +320,14 @@ void AstGraphBuilder::VisitForValueOrNull(Expression* expr) {
}
void AstGraphBuilder::VisitForValueOrTheHole(Expression* expr) {
if (expr == NULL) {
return environment()->Push(jsgraph()->TheHoleConstant());
}
VisitForValue(expr);
}
void AstGraphBuilder::VisitForValues(ZoneList<Expression*>* exprs) {
for (int i = 0; i < exprs->length(); ++i) {
VisitForValue(exprs->at(i));
......@@ -469,12 +477,9 @@ void AstGraphBuilder::VisitBlock(Block* stmt) {
// Visit statements in the same scope, no declarations.
VisitStatements(stmt->statements());
} else {
const Operator* op = javascript()->CreateBlockContext();
Node* scope_info = jsgraph()->Constant(stmt->scope()->GetScopeInfo());
Node* context = NewNode(op, scope_info, GetFunctionClosure());
ContextScope scope(this, stmt->scope(), context);
// Visit declarations and statements in a block scope.
Node* context = BuildLocalBlockContext(stmt->scope());
ContextScope scope(this, stmt->scope(), context);
VisitDeclarations(stmt->scope()->declarations());
VisitStatements(stmt->statements());
}
......@@ -842,7 +847,105 @@ void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
UNREACHABLE();
if (expr->scope() == NULL) {
// Visit class literal in the same scope, no declarations.
VisitClassLiteralContents(expr);
} else {
// Visit declarations and class literal in a block scope.
Node* context = BuildLocalBlockContext(expr->scope());
ContextScope scope(this, expr->scope(), context);
VisitDeclarations(expr->scope()->declarations());
VisitClassLiteralContents(expr);
}
}
void AstGraphBuilder::VisitClassLiteralContents(ClassLiteral* expr) {
Node* class_name = expr->raw_name() ? jsgraph()->Constant(expr->name())
: jsgraph()->UndefinedConstant();
// The class name is expected on the operand stack.
environment()->Push(class_name);
VisitForValueOrTheHole(expr->extends());
VisitForValue(expr->constructor());
// Create node to instantiate a new class.
Node* constructor = environment()->Pop();
Node* extends = environment()->Pop();
Node* name = environment()->Pop();
Node* script = jsgraph()->Constant(info()->script());
Node* start = jsgraph()->Constant(expr->start_position());
Node* end = jsgraph()->Constant(expr->end_position());
const Operator* opc = javascript()->CallRuntime(Runtime::kDefineClass, 6);
Node* literal = NewNode(opc, name, extends, constructor, script, start, end);
// The prototype is ensured to exist by Runtime_DefineClass. No access check
// is needed here since the constructor is created by the class literal.
Node* proto =
BuildLoadObjectField(literal, JSFunction::kPrototypeOrInitialMapOffset);
// The class literal and the prototype are both expected on the operand stack
// during evaluation of the method values.
environment()->Push(literal);
environment()->Push(proto);
// Create nodes to store method values into the literal.
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
environment()->Push(property->is_static() ? literal : proto);
VisitForValue(property->key());
VisitForValue(property->value());
Node* value = environment()->Pop();
Node* key = environment()->Pop();
Node* receiver = environment()->Pop();
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
UNREACHABLE();
case ObjectLiteral::Property::COMPUTED:
case ObjectLiteral::Property::PROTOTYPE: {
const Operator* op =
javascript()->CallRuntime(Runtime::kDefineClassMethod, 3);
NewNode(op, receiver, key, value);
break;
}
case ObjectLiteral::Property::GETTER: {
const Operator* op = javascript()->CallRuntime(
Runtime::kDefineGetterPropertyUnchecked, 3);
NewNode(op, receiver, key, value);
break;
}
case ObjectLiteral::Property::SETTER: {
const Operator* op = javascript()->CallRuntime(
Runtime::kDefineSetterPropertyUnchecked, 3);
NewNode(op, receiver, key, value);
break;
}
}
// TODO(mstarzinger): This is temporary to make "super" work and replicates
// the existing FullCodeGenerator::NeedsHomeObject predicate.
if (FunctionLiteral::NeedsHomeObject(property->value())) {
Unique<Name> name =
MakeUnique(isolate()->factory()->home_object_symbol());
NewNode(javascript()->StoreNamed(strict_mode(), name), value, receiver);
}
}
// Transform both the class literal and the prototype to fast properties.
const Operator* op = javascript()->CallRuntime(Runtime::kToFastProperties, 1);
NewNode(op, environment()->Pop()); // prototype
NewNode(op, environment()->Pop()); // literal
// Assign to class variable.
if (expr->scope() != NULL) {
DCHECK_NOT_NULL(expr->class_variable_proxy());
Variable* var = expr->class_variable_proxy()->var();
BuildVariableAssignment(var, literal, Token::INIT_CONST, BailoutId::None());
}
ast_context()->ProduceValue(literal);
}
......@@ -1829,6 +1932,18 @@ Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context, Node* closure) {
}
Node* AstGraphBuilder::BuildLocalBlockContext(Scope* scope) {
Node* closure = GetFunctionClosure();
// Allocate a new local context.
const Operator* op = javascript()->CreateBlockContext();
Node* scope_info = jsgraph()->Constant(scope->GetScopeInfo());
Node* local_context = NewNode(op, scope_info, closure);
return local_context;
}
Node* AstGraphBuilder::BuildArgumentsObject(Variable* arguments) {
if (arguments == NULL) return NULL;
......
......@@ -72,8 +72,9 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
// Builder to create a receiver check for sloppy mode.
Node* BuildPatchReceiverToGlobalProxy(Node* receiver);
// Builder to create a local function context.
// Builders to create local function and block contexts.
Node* BuildLocalFunctionContext(Node* context, Node* closure);
Node* BuildLocalBlockContext(Scope* scope);
// Builder to create an arguments object if it is used.
Node* BuildArgumentsObject(Variable* arguments);
......@@ -174,6 +175,7 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
void VisitForEffect(Expression* expr);
void VisitForValue(Expression* expr);
void VisitForValueOrNull(Expression* expr);
void VisitForValueOrTheHole(Expression* expr);
void VisitForValues(ZoneList<Expression*>* exprs);
// Common for all IterationStatement bodies.
......@@ -196,6 +198,9 @@ class AstGraphBuilder : public StructuredGraphBuilder, public AstVisitor {
// Dispatched from VisitForInStatement.
void VisitForInAssignment(Expression* expr, Node* value);
// Dispatched from VisitClassLiteral.
void VisitClassLiteralContents(ClassLiteral* expr);
// Builds deoptimization for a given node.
void PrepareFrameState(
Node* node, BailoutId ast_id,
......
......@@ -756,8 +756,6 @@ Handle<Code> Pipeline::GenerateCode() {
info()->function()->dont_optimize_reason() == kTryFinallyStatement ||
// TODO(turbofan): Make super work and remove this bailout.
info()->function()->dont_optimize_reason() == kSuperReference ||
// TODO(turbofan): Make class literals work and remove this bailout.
info()->function()->dont_optimize_reason() == kClassLiteral ||
// TODO(turbofan): Make OSR work with inner loops and remove this bailout.
(info()->is_osr() && !FLAG_turbo_osr)) {
return Handle<Code>::null();
......
......@@ -1589,8 +1589,7 @@ void FullCodeGenerator::VisitClassLiteral(ClassLiteral* lit) {
{
EnterBlockScopeIfNeeded block_scope_state(
this, lit->scope(), BailoutId::None(), BailoutId::None(),
BailoutId::None());
this, lit->scope(), lit->EntryId(), lit->DeclsId(), lit->ExitId());
if (lit->raw_name() != NULL) {
__ Push(lit->name());
......
......@@ -251,6 +251,7 @@ TypeImpl<Config>::BitsetType::Lub(i::Map* map) {
case FIXED_ARRAY_TYPE:
case BYTE_ARRAY_TYPE:
case FOREIGN_TYPE:
case SCRIPT_TYPE:
case CODE_TYPE:
return kInternal & kTaggedPointer;
default:
......
......@@ -522,3 +522,24 @@ TEST(RegExpLiteral) {
T.CheckTrue(T.Val("abc"));
T.CheckFalse(T.Val("xyz"));
}
TEST(ClassLiteral) {
FLAG_harmony_classes = true;
FLAG_harmony_sloppy = true;
FLAG_harmony_object_literals = true;
const char* src =
"(function(a,b) {"
" class C {"
" x() { return a; }"
" static y() { return b; }"
" get z() { return 0; }"
" constructor() {}"
" }"
" return new C().x() + C.y();"
"})";
FunctionTester T(src);
T.CheckCall(T.Val(65), T.Val(23), T.Val(42));
T.CheckCall(T.Val("ab"), T.Val("a"), T.Val("b"));
}
......@@ -193,6 +193,10 @@
# nosse2. Also for arm novfp3.
'regress/regress-2989': [FAIL, NO_VARIANTS, ['system == linux and arch == x87 or arch == arm and simulator == True', PASS]],
# Skip endain dependent test for mips due to different typed views of the same
# array buffer.
'nans': [PASS, ],
# This test variant makes only sense on arm.
'math-floor-of-div-nosudiv': [PASS, SLOW, ['arch not in [arm, arm64, android_arm, android_arm64]', SKIP]],
......
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