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
This commit is contained in:
arv@chromium.org 2014-10-07 16:24:59 +00:00
parent be69c78e6d
commit 583868288a
10 changed files with 345 additions and 50 deletions

View File

@ -813,7 +813,6 @@ void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
// TODO(arv): Implement.
UNREACHABLE();
}

View File

@ -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<Code> 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()) {

View File

@ -1542,12 +1542,30 @@ void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
void FullCodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
// TODO(arv): Implement
Comment cmnt(masm_, "[ ClassLiteral");
if (expr->extends() != NULL) {
VisitForEffect(expr->extends());
if (expr->raw_name() != NULL) {
__ Push(expr->name());
} else {
__ Push(isolate()->factory()->undefined_value());
}
context()->Plug(isolate()->factory()->undefined_value());
if (expr->extends() != NULL) {
VisitForStackValue(expr->extends());
} else {
__ Push(isolate()->factory()->the_hole_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());
}

View File

@ -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"]
};

View File

@ -646,7 +646,7 @@ Expression* ParserTraits::SuperReference(
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 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* 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;
}

View File

@ -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<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,

View File

@ -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<Traits>::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<Traits>::ExpressionT ParserBase<Traits>::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<Traits>::ExpressionT ParserBase<Traits>::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<Traits>::ExpressionT ParserBase<Traits>::ParseClassLiteral(
}
Expect(Token::RBRACE, CHECK_OK);
return this->ClassLiteral(name, extends, constructor, properties, pos,
factory());
return this->ClassExpression(name, extends, constructor, properties, pos,
factory());
}

View File

@ -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<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

View File

@ -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) \

View File

@ -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();
})();
*/