void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
- // TODO(arv): Implement.
UNREACHABLE();
}
}
#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); \
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()) {
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());
}
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"]
};
pos);
}
-Expression* ParserTraits::ClassLiteral(
+Expression* ParserTraits::ClassExpression(
const AstRawString* name, Expression* extends, Expression* constructor,
ZoneList<ObjectLiteral::Property*>* properties, int pos,
AstNodeFactory<AstConstructionVisitor>* factory) {
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;
}
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) {
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,
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:
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) {
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();
}
*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(),
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) {
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();
}
Expect(Token::RBRACE, CHECK_OK);
- return this->ClassLiteral(name, extends, constructor, properties, pos,
- factory());
+ return this->ClassExpression(name, extends, constructor, properties, pos,
+ factory());
}
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
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) \
--- /dev/null
+// 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();
+})();
+*/