From 0f418385d066c724b264e3f500f46e82b60acd61 Mon Sep 17 00:00:00 2001 From: mstarzinger Date: Fri, 16 Jan 2015 04:38:13 -0800 Subject: [PATCH] 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} --- src/ast-numbering.cc | 2 +- src/ast.h | 8 +++ src/compiler/ast-graph-builder.cc | 127 +++++++++++++++++++++++++++++++-- src/compiler/ast-graph-builder.h | 7 +- src/compiler/pipeline.cc | 2 - src/full-codegen.cc | 3 +- src/types.cc | 1 + test/cctest/compiler/test-run-jsops.cc | 21 ++++++ test/mjsunit/mjsunit.status | 4 ++ 9 files changed, 163 insertions(+), 12 deletions(-) diff --git a/src/ast-numbering.cc b/src/ast-numbering.cc index f9144bc..6998187 100644 --- a/src/ast-numbering.cc +++ b/src/ast-numbering.cc @@ -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()); diff --git a/src/ast.h b/src/ast.h index 6cf55c2..716fded 100644 --- a/src/ast.h +++ b/src/ast.h @@ -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_; diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc index 4a22abf..c4b9b31 100644 --- a/src/compiler/ast-graph-builder.cc +++ b/src/compiler/ast-graph-builder.cc @@ -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* 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 = + 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; diff --git a/src/compiler/ast-graph-builder.h b/src/compiler/ast-graph-builder.h index 92c8712..8493d42 100644 --- a/src/compiler/ast-graph-builder.h +++ b/src/compiler/ast-graph-builder.h @@ -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* 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, diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index 082f11d..c43ed1b 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -756,8 +756,6 @@ Handle 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::null(); diff --git a/src/full-codegen.cc b/src/full-codegen.cc index ec674ff..0d1ff4f 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -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()); diff --git a/src/types.cc b/src/types.cc index c4f1bae..c7bd5cb 100644 --- a/src/types.cc +++ b/src/types.cc @@ -251,6 +251,7 @@ TypeImpl::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: diff --git a/test/cctest/compiler/test-run-jsops.cc b/test/cctest/compiler/test-run-jsops.cc index eb39760..df65afa 100644 --- a/test/cctest/compiler/test-run-jsops.cc +++ b/test/cctest/compiler/test-run-jsops.cc @@ -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")); +} diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index 2e76927..26ec10b 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -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]], -- 2.7.4