From: arv@chromium.org Date: Tue, 7 Oct 2014 16:24:59 +0000 (+0000) Subject: This uses a runtime function to set up the the constructor and its X-Git-Tag: upstream/4.7.83~6510 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=583868288a8008a64dda0afeb6c004b47eaacb46;p=platform%2Fupstream%2Fv8.git This uses a runtime function to set up the the constructor and its prototype. This does not add the methods/accessors to the prototype or the constructor. BUG=v8:3330 LOG=Y R=dslomov@chromium.org Review URL: https://codereview.chromium.org/631433002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24442 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc index fa98779..fa3e88b 100644 --- a/src/compiler/ast-graph-builder.cc +++ b/src/compiler/ast-graph-builder.cc @@ -813,7 +813,6 @@ void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) { - // TODO(arv): Implement. UNREACHABLE(); } diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index ca58e17..469a7ed 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -145,7 +145,7 @@ class AstGraphBuilderWithPositions : public AstGraphBuilder { } #define DEF_VISIT(type) \ - virtual void Visit##type(type* node) OVERRIDE { \ + virtual void Visit##type(type* node) OVERRIDE { \ SourcePositionTable::Scope pos(source_positions_, \ SourcePosition(node->position())); \ AstGraphBuilder::Visit##type(node); \ @@ -173,7 +173,7 @@ Handle Pipeline::GenerateCode() { info()->function()->dont_optimize_reason() == kForOfStatement || // TODO(turbofan): Make super work and remove this bailout. info()->function()->dont_optimize_reason() == kSuperReference || - // TODO(turbofan): Make classliterals work and remove this bailout. + // TODO(turbofan): Make class literals work and remove this bailout. info()->function()->dont_optimize_reason() == kClassLiteral || // TODO(turbofan): Make OSR work and remove this bailout. info()->is_osr()) { diff --git a/src/full-codegen.cc b/src/full-codegen.cc index 35d51d9..8177a35 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -1542,12 +1542,30 @@ void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { void FullCodeGenerator::VisitClassLiteral(ClassLiteral* expr) { - // TODO(arv): Implement Comment cmnt(masm_, "[ ClassLiteral"); + + if (expr->raw_name() != NULL) { + __ Push(expr->name()); + } else { + __ Push(isolate()->factory()->undefined_value()); + } + if (expr->extends() != NULL) { - VisitForEffect(expr->extends()); + VisitForStackValue(expr->extends()); + } else { + __ Push(isolate()->factory()->the_hole_value()); } - context()->Plug(isolate()->factory()->undefined_value()); + + if (expr->constructor() != NULL) { + VisitForStackValue(expr->constructor()); + } else { + __ Push(isolate()->factory()->undefined_value()); + } + + // TODO(arv): Process methods + + __ CallRuntime(Runtime::kDefineClass, 3); + context()->Plug(result_register()); } diff --git a/src/messages.js b/src/messages.js index 4a71a61..2dddffa 100644 --- a/src/messages.js +++ b/src/messages.js @@ -174,7 +174,9 @@ var kMessages = { invalid_module_path: ["Module does not export '", "%0", "', or export is not itself a module"], module_type_error: ["Module '", "%0", "' used improperly"], module_export_undefined: ["Export '", "%0", "' is not defined in module"], - unexpected_super: ["'super' keyword unexpected here"] + unexpected_super: ["'super' keyword unexpected here"], + extends_value_not_a_function: ["Class extends value ", "%0", " is not a function or null"], + prototype_parent_not_an_object: ["Class extends value does not have valid prototype property ", "%0"] }; diff --git a/src/parser.cc b/src/parser.cc index 2b91443..04efd1e 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -646,7 +646,7 @@ Expression* ParserTraits::SuperReference( pos); } -Expression* ParserTraits::ClassLiteral( +Expression* ParserTraits::ClassExpression( const AstRawString* name, Expression* extends, Expression* constructor, ZoneList* properties, int pos, AstNodeFactory* factory) { @@ -1956,21 +1956,18 @@ Statement* Parser::ParseClassDeclaration(ZoneList* names, Expression* value = ParseClassLiteral(name, scanner()->location(), is_strict_reserved, pos, CHECK_OK); - Block* block = factory()->NewBlock(NULL, 1, true, pos); - VariableMode mode = LET; - VariableProxy* proxy = NewUnresolved(name, mode, Interface::NewValue()); + VariableProxy* proxy = NewUnresolved(name, LET, Interface::NewValue()); Declaration* declaration = - factory()->NewVariableDeclaration(proxy, mode, scope_, pos); + factory()->NewVariableDeclaration(proxy, LET, scope_, pos); Declare(declaration, true, CHECK_OK); + proxy->var()->set_initializer_position(pos); Token::Value init_op = Token::INIT_LET; Assignment* assignment = factory()->NewAssignment(init_op, proxy, value, pos); - block->AddStatement( - factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition), - zone()); - + Statement* assignment_statement = + factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); if (names) names->Add(name, zone()); - return block; + return assignment_statement; } diff --git a/src/parser.h b/src/parser.h index 300cc03..e461a99 100644 --- a/src/parser.h +++ b/src/parser.h @@ -417,6 +417,15 @@ class ParserTraits { return string->AsArrayIndex(index); } + bool IsConstructorProperty(ObjectLiteral::Property* property) { + return property->key()->raw_value()->EqualsString( + ast_value_factory()->constructor_string()); + } + + static Expression* GetPropertyValue(ObjectLiteral::Property* property) { + return property->value(); + } + // Functions for encapsulating the differences between parsing and preparsing; // operations interleaved with the recursive descent. static void PushLiteralName(FuncNameInferrer* fni, const AstRawString* id) { @@ -546,11 +555,11 @@ class ParserTraits { Expression* SuperReference(Scope* scope, AstNodeFactory* factory, int pos = RelocInfo::kNoPosition); - Expression* ClassLiteral(const AstRawString* name, Expression* extends, - Expression* constructor, - ZoneList* properties, - int pos, - AstNodeFactory* factory); + Expression* ClassExpression(const AstRawString* name, Expression* extends, + Expression* constructor, + ZoneList* properties, + int pos, + AstNodeFactory* factory); Literal* ExpressionFromLiteral( Token::Value token, int pos, Scanner* scanner, diff --git a/src/preparser.h b/src/preparser.h index 5c2a622..1def94f 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -150,6 +150,13 @@ class ParserBase : public Traits { scope_(scope) { *scope_stack_ = scope_; } + BlockState(typename Traits::Type::Scope** scope_stack, + typename Traits::Type::Scope** scope) + : scope_stack_(scope_stack), + outer_scope_(*scope_stack), + scope_(*scope) { + *scope_stack_ = scope_; + } ~BlockState() { *scope_stack_ = outer_scope_; } private: @@ -1195,6 +1202,13 @@ class PreParserTraits { return false; } + bool IsConstructorProperty(PreParserExpression property) { return false; } + + static PreParserExpression GetPropertyValue(PreParserExpression property) { + UNREACHABLE(); + return PreParserExpression::Default(); + } + // Functions for encapsulating the differences between parsing and preparsing; // operations interleaved with the recursive descent. static void PushLiteralName(FuncNameInferrer* fni, PreParserIdentifier id) { @@ -1320,12 +1334,12 @@ class PreParserTraits { return PreParserExpression::Super(); } - static PreParserExpression ClassLiteral(PreParserIdentifier name, - PreParserExpression extends, - PreParserExpression constructor, - PreParserExpressionList properties, - int position, - PreParserFactory* factory) { + static PreParserExpression ClassExpression(PreParserIdentifier name, + PreParserExpression extends, + PreParserExpression constructor, + PreParserExpressionList properties, + int position, + PreParserFactory* factory) { return PreParserExpression::Default(); } @@ -1978,16 +1992,22 @@ typename ParserBase::ObjectLiteralPropertyT ParserBase< *ok = false; return this->EmptyObjectLiteralProperty(); } - if (is_generator && in_class && !is_static && this->IsConstructor(name)) { - ReportMessageAt(scanner()->location(), "constructor_special_method"); - *ok = false; - return this->EmptyObjectLiteralProperty(); + + FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod + : FunctionKind::kConciseMethod; + + if (in_class && !is_static && this->IsConstructor(name)) { + if (is_generator) { + ReportMessageAt(scanner()->location(), "constructor_special_method"); + *ok = false; + return this->EmptyObjectLiteralProperty(); + } + + kind = FunctionKind::kNormalFunction; } checker->CheckProperty(name_token, kValueProperty, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); - FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod - : FunctionKind::kConciseMethod; value = this->ParseFunctionLiteral( name, scanner()->location(), @@ -2745,25 +2765,24 @@ typename ParserBase::ExpressionT ParserBase::ParseClassLiteral( return this->EmptyExpression(); } - // TODO(arv): Implement scopes and name binding in class body only. - // TODO(arv): Maybe add CLASS_SCOPE? - typename Traits::Type::ScopePtr extends_scope = - this->NewScope(scope_, BLOCK_SCOPE); - FunctionState extends_function_state( - &function_state_, &scope_, &extends_scope, zone(), - this->ast_value_factory(), ast_node_id_gen_); - scope_->SetStrictMode(STRICT); - scope_->SetScopeName(name); - ExpressionT extends = this->EmptyExpression(); if (Check(Token::EXTENDS)) { + typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE); + BlockState block_state(&scope_, &scope); + scope_->SetStrictMode(STRICT); extends = this->ParseLeftHandSideExpression(CHECK_OK); } + // TODO(arv): Implement scopes and name binding in class body only. + typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE); + BlockState block_state(&scope_, &scope); + scope_->SetStrictMode(STRICT); + scope_->SetScopeName(name); + ObjectLiteralChecker checker(this, STRICT); typename Traits::Type::PropertyList properties = this->NewPropertyList(4, zone_); - FunctionLiteralT constructor = this->EmptyFunctionLiteral(); + ExpressionT constructor = this->EmptyExpression(); Expect(Token::LBRACE, CHECK_OK); while (peek() != Token::RBRACE) { @@ -2775,7 +2794,11 @@ typename ParserBase::ExpressionT ParserBase::ParseClassLiteral( ObjectLiteralPropertyT property = this->ParsePropertyDefinition(&checker, in_class, is_static, CHECK_OK); - properties->Add(property, zone()); + if (this->IsConstructorProperty(property)) { + constructor = this->GetPropertyValue(property); + } else { + properties->Add(property, zone()); + } if (fni_ != NULL) { fni_->Infer(); @@ -2784,8 +2807,8 @@ typename ParserBase::ExpressionT ParserBase::ParseClassLiteral( } Expect(Token::RBRACE, CHECK_OK); - return this->ClassLiteral(name, extends, constructor, properties, pos, - factory()); + return this->ClassExpression(name, extends, constructor, properties, pos, + factory()); } diff --git a/src/runtime/runtime-classes.cc b/src/runtime/runtime-classes.cc index 20e98ef..b8cfa9b 100644 --- a/src/runtime/runtime-classes.cc +++ b/src/runtime/runtime-classes.cc @@ -151,5 +151,84 @@ RUNTIME_FUNCTION(Runtime_StoreToSuper_Sloppy) { return StoreToSuper(isolate, home_object, receiver, name, value, SLOPPY); } + + +RUNTIME_FUNCTION(Runtime_DefineClass) { + HandleScope scope(isolate); + DCHECK(args.length() == 3); + CONVERT_ARG_HANDLE_CHECKED(Object, name, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 1); + CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 2); + + Handle prototype_parent; + Handle constructor_parent; + + if (super_class->IsTheHole()) { + prototype_parent = isolate->initial_object_prototype(); + } else { + if (super_class->IsNull()) { + prototype_parent = isolate->factory()->null_value(); + } else if (super_class->IsSpecFunction()) { + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, prototype_parent, + Runtime::GetObjectProperty(isolate, super_class, + isolate->factory()->prototype_string())); + if (!prototype_parent->IsNull() && !prototype_parent->IsSpecObject()) { + Handle args[1] = {prototype_parent}; + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError("prototype_parent_not_an_object", + HandleVector(args, 1))); + } + constructor_parent = super_class; + } else { + // TODO(arv): Should be IsConstructor. + Handle args[1] = {super_class}; + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, + NewTypeError("extends_value_not_a_function", HandleVector(args, 1))); + } + } + + Handle map = + isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); + map->set_prototype(*prototype_parent); + Handle prototype = isolate->factory()->NewJSObjectFromMap(map); + + Handle name_string = name->IsString() + ? Handle::cast(name) + : isolate->factory()->empty_string(); + + Handle ctor; + if (constructor->IsSpecFunction()) { + ctor = Handle::cast(constructor); + JSFunction::SetPrototype(ctor, prototype); + PropertyAttributes attribs = + static_cast(DONT_ENUM | DONT_DELETE | READ_ONLY); + RETURN_FAILURE_ON_EXCEPTION( + isolate, + JSObject::SetOwnPropertyIgnoreAttributes( + ctor, isolate->factory()->prototype_string(), prototype, attribs)); + } else { + // TODO(arv): This should not use an empty function but a function that + // calls super. + Handle code(isolate->builtins()->builtin(Builtins::kEmptyFunction)); + ctor = isolate->factory()->NewFunction(name_string, code, prototype, true); + } + + Handle home_object_symbol(isolate->heap()->home_object_symbol()); + RETURN_FAILURE_ON_EXCEPTION( + isolate, JSObject::SetOwnPropertyIgnoreAttributes( + ctor, home_object_symbol, prototype, DONT_ENUM)); + + if (!constructor_parent.is_null()) { + RETURN_FAILURE_ON_EXCEPTION( + isolate, JSObject::SetPrototype(ctor, constructor_parent, false)); + } + + JSObject::AddProperty(prototype, isolate->factory()->constructor_string(), + ctor, DONT_ENUM); + + return *ctor; +} } } // namespace v8::internal diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 7997415..b42063a 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -192,7 +192,8 @@ namespace internal { F(LoadFromSuper, 3, 1) \ F(LoadKeyedFromSuper, 3, 1) \ F(StoreToSuper_Strict, 4, 1) \ - F(StoreToSuper_Sloppy, 4, 1) + F(StoreToSuper_Sloppy, 4, 1) \ + F(DefineClass, 3, 1) #define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \ diff --git a/test/mjsunit/harmony/classes.js b/test/mjsunit/harmony/classes.js new file mode 100644 index 0000000..7156dbd --- /dev/null +++ b/test/mjsunit/harmony/classes.js @@ -0,0 +1,167 @@ +// Copyright 2014 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 + +(function TestBasics() { + var C = class C {} + assertEquals(typeof C, 'function'); + assertEquals(C.__proto__, Function.prototype); + assertEquals(Object.prototype, Object.getPrototypeOf(C.prototype)); + assertEquals(Function.prototype, Object.getPrototypeOf(C)); + assertEquals('C', C.name); + + class D {} + assertEquals(typeof D, 'function'); + assertEquals(D.__proto__, Function.prototype); + assertEquals(Object.prototype, Object.getPrototypeOf(D.prototype)); + assertEquals(Function.prototype, Object.getPrototypeOf(D)); + assertEquals('D', D.name); + + var E = class {} + assertEquals('', E.name); +})(); + + +(function TestBasicsExtends() { + class C extends null {} + assertEquals(typeof C, 'function'); + assertEquals(C.__proto__, Function.prototype); + assertEquals(null, Object.getPrototypeOf(C.prototype)); + + class D extends C {} + assertEquals(typeof D, 'function'); + assertEquals(D.__proto__, C); + assertEquals(C.prototype, Object.getPrototypeOf(D.prototype)); +})(); + + +(function TestSideEffectInExtends() { + var calls = 0; + class C {} + class D extends (calls++, C) {} + assertEquals(1, calls); + assertEquals(typeof D, 'function'); + assertEquals(D.__proto__, C); + assertEquals(C.prototype, Object.getPrototypeOf(D.prototype)); +})(); + + +(function TestInvalidExtends() { + assertThrows(function() { + class C extends 42 {} + }, TypeError); + + assertThrows(function() { + // Function but its .prototype is not null or a function. + class C extends Math.abs {} + }, TypeError); + + assertThrows(function() { + Math.abs.prototype = 42; + class C extends Math.abs {} + }, TypeError); + delete Math.abs.prototype; +})(); + + +(function TestConstructorProperty() { + class C {} + assertEquals(C, C.prototype.constructor); + var descr = Object.getOwnPropertyDescriptor(C.prototype, 'constructor'); + assertTrue(descr.configurable); + assertFalse(descr.enumerable); + assertTrue(descr.writable); +})(); + + +(function TestPrototypeProperty() { + class C {} + var descr = Object.getOwnPropertyDescriptor(C, 'prototype'); + assertFalse(descr.configurable); + assertFalse(descr.enumerable); + assertFalse(descr.writable); +})(); + + +(function TestConstructor() { + var count = 0; + class C { + constructor() { + assertEquals(Object.getPrototypeOf(this), C.prototype); + count++; + } + } + assertEquals(C, C.prototype.constructor); + var descr = Object.getOwnPropertyDescriptor(C.prototype, 'constructor'); + assertTrue(descr.configurable); + assertFalse(descr.enumerable); + assertTrue(descr.writable); + + var c = new C(); + assertEquals(1, count); + assertEquals(Object.getPrototypeOf(c), C.prototype); +})(); + + +(function TestImplicitConstructor() { + class C {} + var c = new C(); + assertEquals(Object.getPrototypeOf(c), C.prototype); +})(); + + +(function TestConstructorStrict() { + class C { + constructor() { + assertThrows(function() { + nonExistingBinding = 42; + }, ReferenceError); + } + } + new C(); +})(); + + +(function TestSuperInConstructor() { + var calls = 0; + class B {} + B.prototype.x = 42; + + class C extends B { + constructor() { + calls++; + assertEquals(42, super.x); + } + } + + new C; + assertEquals(1, calls); +})(); + + +(function TestStrictMode() { + class C {} + + with ({a: 1}) { + assertEquals(1, a); + } + + assertThrows('class C extends function B() { with ({}); return B; }() {}', + SyntaxError); + +})(); + +/* TODO(arv): Implement +(function TestNameBindingInConstructor() { + class C { + constructor() { + assertThrows(function() { + C = 42; + }, ReferenceError); + } + } + new C(); +})(); +*/