Revision: 24442
Author: [email protected]
Date: Tue Oct 7 16:24:59 2014 UTC
Log: 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
[email protected]
Review URL: https://codereview.chromium.org/631433002
https://code.google.com/p/v8/source/detail?r=24442
Added:
/branches/bleeding_edge/test/mjsunit/harmony/classes.js
Modified:
/branches/bleeding_edge/src/compiler/ast-graph-builder.cc
/branches/bleeding_edge/src/compiler/pipeline.cc
/branches/bleeding_edge/src/full-codegen.cc
/branches/bleeding_edge/src/messages.js
/branches/bleeding_edge/src/parser.cc
/branches/bleeding_edge/src/parser.h
/branches/bleeding_edge/src/preparser.h
/branches/bleeding_edge/src/runtime/runtime-classes.cc
/branches/bleeding_edge/src/runtime/runtime.h
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/harmony/classes.js Tue Oct 7
16:24:59 2014 UTC
@@ -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();
+})();
+*/
=======================================
--- /branches/bleeding_edge/src/compiler/ast-graph-builder.cc Wed Oct 1
14:03:02 2014 UTC
+++ /branches/bleeding_edge/src/compiler/ast-graph-builder.cc Tue Oct 7
16:24:59 2014 UTC
@@ -813,7 +813,6 @@
void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
- // TODO(arv): Implement.
UNREACHABLE();
}
=======================================
--- /branches/bleeding_edge/src/compiler/pipeline.cc Tue Oct 7 12:35:06
2014 UTC
+++ /branches/bleeding_edge/src/compiler/pipeline.cc Tue Oct 7 16:24:59
2014 UTC
@@ -145,7 +145,7 @@
}
#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 @@
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()) {
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Thu Oct 2 11:58:21 2014 UTC
+++ /branches/bleeding_edge/src/full-codegen.cc Tue Oct 7 16:24:59 2014 UTC
@@ -1542,12 +1542,30 @@
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());
}
=======================================
--- /branches/bleeding_edge/src/messages.js Wed Sep 24 08:39:04 2014 UTC
+++ /branches/bleeding_edge/src/messages.js Tue Oct 7 16:24:59 2014 UTC
@@ -174,7 +174,9 @@
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"]
};
=======================================
--- /branches/bleeding_edge/src/parser.cc Tue Oct 7 12:16:28 2014 UTC
+++ /branches/bleeding_edge/src/parser.cc Tue Oct 7 16:24:59 2014 UTC
@@ -646,7 +646,7 @@
pos);
}
-Expression* ParserTraits::ClassLiteral(
+Expression* ParserTraits::ClassExpression(
const AstRawString* name, Expression* extends, Expression* constructor,
ZoneList<ObjectLiteral::Property*>* properties, int pos,
AstNodeFactory<AstConstructionVisitor>* factory) {
@@ -1956,21 +1956,18 @@
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;
}
=======================================
--- /branches/bleeding_edge/src/parser.h Tue Oct 7 12:16:28 2014 UTC
+++ /branches/bleeding_edge/src/parser.h Tue Oct 7 16:24:59 2014 UTC
@@ -416,6 +416,15 @@
static bool IsArrayIndex(const AstRawString* string, uint32_t* index) {
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.
@@ -546,11 +555,11 @@
Expression* SuperReference(Scope* scope,
AstNodeFactory<AstConstructionVisitor>*
factory,
int pos = RelocInfo::kNoPosition);
- Expression* ClassLiteral(const AstRawString* name, Expression* extends,
- Expression* constructor,
- ZoneList<ObjectLiteral::Property*>* properties,
- int pos,
- AstNodeFactory<AstConstructionVisitor>*
factory);
+ Expression* ClassExpression(const AstRawString* name, Expression*
extends,
+ Expression* constructor,
+ ZoneList<ObjectLiteral::Property*>*
properties,
+ int pos,
+ AstNodeFactory<AstConstructionVisitor>*
factory);
Literal* ExpressionFromLiteral(
Token::Value token, int pos, Scanner* scanner,
=======================================
--- /branches/bleeding_edge/src/preparser.h Tue Oct 7 12:16:28 2014 UTC
+++ /branches/bleeding_edge/src/preparser.h Tue Oct 7 16:24:59 2014 UTC
@@ -150,6 +150,13 @@
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:
@@ -1194,6 +1201,13 @@
static bool IsArrayIndex(PreParserIdentifier string, uint32_t* index) {
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.
@@ -1320,12 +1334,12 @@
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 @@
*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(),
@@ -2744,26 +2764,25 @@
*ok = false;
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 @@
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 @@
}
Expect(Token::RBRACE, CHECK_OK);
- return this->ClassLiteral(name, extends, constructor, properties, pos,
- factory());
+ return this->ClassExpression(name, extends, constructor, properties, pos,
+ factory());
}
=======================================
--- /branches/bleeding_edge/src/runtime/runtime-classes.cc Mon Oct 6
08:25:27 2014 UTC
+++ /branches/bleeding_edge/src/runtime/runtime-classes.cc Tue Oct 7
16:24:59 2014 UTC
@@ -151,5 +151,84 @@
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<Object> prototype_parent;
+ Handle<Object> 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<Object> 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<Object> args[1] = {super_class};
+ THROW_NEW_ERROR_RETURN_FAILURE(
+ isolate,
+ NewTypeError("extends_value_not_a_function", HandleVector(args,
1)));
+ }
+ }
+
+ Handle<Map> map =
+ isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ map->set_prototype(*prototype_parent);
+ Handle<JSObject> prototype = isolate->factory()->NewJSObjectFromMap(map);
+
+ Handle<String> name_string = name->IsString()
+ ? Handle<String>::cast(name)
+ : isolate->factory()->empty_string();
+
+ Handle<JSFunction> ctor;
+ if (constructor->IsSpecFunction()) {
+ ctor = Handle<JSFunction>::cast(constructor);
+ JSFunction::SetPrototype(ctor, prototype);
+ PropertyAttributes attribs =
+ static_cast<PropertyAttributes>(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>
code(isolate->builtins()->builtin(Builtins::kEmptyFunction));
+ ctor = isolate->factory()->NewFunction(name_string, code, prototype,
true);
+ }
+
+ Handle<Symbol> 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
=======================================
--- /branches/bleeding_edge/src/runtime/runtime.h Tue Oct 7 13:30:28 2014
UTC
+++ /branches/bleeding_edge/src/runtime/runtime.h Tue Oct 7 16:24:59 2014
UTC
@@ -192,7 +192,8 @@
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) \
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.