Commit 1881cee6 authored by arv@chromium.org's avatar arv@chromium.org

Classes: Add basic support for properties

This adds the properties to the prototype and the constructor.

BUG=v8:3330
LOG=Y
R=dslomov@chromium.org

Review URL: https://codereview.chromium.org/680993003

Cr-Commit-Position: refs/heads/master@{#24934}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24934 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a13e2298
......@@ -2505,6 +2505,74 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
}
void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
// Constructor is in r0.
DCHECK(lit != NULL);
__ push(r0);
// No access check is needed here since the constructor is created by the
// class literal.
Register scratch = r1;
__ ldr(scratch,
FieldMemOperand(r0, JSFunction::kPrototypeOrInitialMapOffset));
__ push(scratch);
for (int i = 0; i < lit->properties()->length(); i++) {
ObjectLiteral::Property* property = lit->properties()->at(i);
Literal* key = property->key()->AsLiteral();
Expression* value = property->value();
DCHECK(key != NULL);
if (property->is_static()) {
__ ldr(scratch, MemOperand(sp, kPointerSize)); // constructor
} else {
__ ldr(scratch, MemOperand(sp, 0)); // prototype
}
__ push(scratch);
VisitForStackValue(key);
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
case ObjectLiteral::Property::COMPUTED:
case ObjectLiteral::Property::PROTOTYPE:
VisitForStackValue(value);
__ mov(scratch, Operand(Smi::FromInt(NONE)));
__ push(scratch);
__ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
break;
case ObjectLiteral::Property::GETTER:
VisitForStackValue(value);
__ LoadRoot(scratch, Heap::kNullValueRootIndex);
__ push(scratch);
__ mov(scratch, Operand(Smi::FromInt(NONE)));
__ push(scratch);
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
break;
case ObjectLiteral::Property::SETTER:
__ LoadRoot(scratch, Heap::kNullValueRootIndex);
__ push(scratch);
VisitForStackValue(value);
__ mov(scratch, Operand(Smi::FromInt(NONE)));
__ push(scratch);
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
break;
default:
UNREACHABLE();
}
}
// prototype
__ CallRuntime(Runtime::kToFastProperties, 1);
// constructor
__ CallRuntime(Runtime::kToFastProperties, 1);
}
void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode) {
......
......@@ -300,7 +300,8 @@ void FullCodeGenerator::Generate() {
VisitDeclarations(scope()->declarations());
}
{ Comment cmnt(masm_, "[ Stack check");
{
Comment cmnt(masm_, "[ Stack check");
PrepareForBailoutForId(BailoutId::Declarations(), NO_REGISTERS);
Label ok;
DCHECK(jssp.Is(__ StackPointer()));
......@@ -312,7 +313,8 @@ void FullCodeGenerator::Generate() {
__ Bind(&ok);
}
{ Comment cmnt(masm_, "[ Body");
{
Comment cmnt(masm_, "[ Body");
DCHECK(loop_depth() == 0);
VisitStatements(function()->body());
DCHECK(loop_depth() == 0);
......@@ -2040,7 +2042,7 @@ void FullCodeGenerator::EmitNamedSuperPropertyLoad(Property* prop) {
void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
// Call keyed load IC. It has arguments key and receiver in r0 and r1.
// Call keyed load IC. It has arguments key and receiver in x0 and x1.
Handle<Code> ic = CodeFactory::KeyedLoadIC(isolate()).code();
if (FLAG_vector_ics) {
__ Mov(VectorLoadICDescriptor::SlotRegister(),
......@@ -2175,6 +2177,74 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
}
void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
// Constructor is in x0.
DCHECK(lit != NULL);
__ push(x0);
// No access check is needed here since the constructor is created by the
// class literal.
Register scratch = x1;
__ Ldr(scratch,
FieldMemOperand(x0, JSFunction::kPrototypeOrInitialMapOffset));
__ Push(scratch);
for (int i = 0; i < lit->properties()->length(); i++) {
ObjectLiteral::Property* property = lit->properties()->at(i);
Literal* key = property->key()->AsLiteral();
Expression* value = property->value();
DCHECK(key != NULL);
if (property->is_static()) {
__ Peek(scratch, kPointerSize); // constructor
} else {
__ Peek(scratch, 0); // prototype
}
__ Push(scratch);
VisitForStackValue(key);
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
case ObjectLiteral::Property::COMPUTED:
case ObjectLiteral::Property::PROTOTYPE:
VisitForStackValue(value);
__ Mov(scratch, Smi::FromInt(NONE));
__ Push(scratch);
__ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
break;
case ObjectLiteral::Property::GETTER:
VisitForStackValue(value);
__ LoadRoot(scratch, Heap::kNullValueRootIndex);
__ push(scratch);
__ Mov(scratch, Smi::FromInt(NONE));
__ Push(scratch);
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
break;
case ObjectLiteral::Property::SETTER:
__ LoadRoot(scratch, Heap::kNullValueRootIndex);
__ push(scratch);
VisitForStackValue(value);
__ Mov(scratch, Smi::FromInt(NONE));
__ Push(scratch);
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
break;
default:
UNREACHABLE();
}
}
// prototype
__ CallRuntime(Runtime::kToFastProperties, 1);
// constructor
__ CallRuntime(Runtime::kToFastProperties, 1);
}
void FullCodeGenerator::EmitAssignment(Expression* expr) {
DCHECK(expr->IsValidReferenceExpression());
......@@ -4859,7 +4929,7 @@ void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
// The value stays in x0, and is ultimately read by the resumed generator, as
// if CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. Or it
// is read to throw the value when the resumed generator is already closed. r1
// is read to throw the value when the resumed generator is already closed. x1
// will hold the generator object until the activation has been resumed.
VisitForStackValue(generator);
VisitForAccumulatorValue(value);
......
......@@ -1512,6 +1512,8 @@ class ObjectLiteralProperty FINAL : public ZoneObject {
void set_emit_store(bool emit_store);
bool emit_store();
bool is_static() const { return is_static_; }
protected:
template<class> friend class AstNodeFactory;
......
......@@ -1585,9 +1585,9 @@ void FullCodeGenerator::VisitClassLiteral(ClassLiteral* lit) {
__ Push(Smi::FromInt(lit->start_position()));
__ Push(Smi::FromInt(lit->end_position()));
// TODO(arv): Process methods
__ CallRuntime(Runtime::kDefineClass, 6);
EmitClassDefineProperties(lit);
context()->Plug(result_register());
}
......
......@@ -563,6 +563,11 @@ class FullCodeGenerator: public AstVisitor {
// The receiver and the key is left on the stack by the IC.
void EmitKeyedPropertyLoad(Property* expr);
// Adds the properties to the class (function) object and to its prototype.
// Expects the class (function) in the accumulator. The class (function) is
// in the accumulator after installing all the properties.
void EmitClassDefineProperties(ClassLiteral* lit);
// Apply the compound assignment operator. Expects the left operand on top
// of the stack and the right one in the accumulator.
void EmitBinaryOp(BinaryOperation* expr,
......
......@@ -2421,6 +2421,67 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
}
void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
// Constructor is in eax.
DCHECK(lit != NULL);
__ push(eax);
// No access check is needed here since the constructor is created by the
// class literal.
Register scratch = ebx;
__ mov(scratch, FieldOperand(eax, JSFunction::kPrototypeOrInitialMapOffset));
__ Push(scratch);
for (int i = 0; i < lit->properties()->length(); i++) {
ObjectLiteral::Property* property = lit->properties()->at(i);
Literal* key = property->key()->AsLiteral();
Expression* value = property->value();
DCHECK(key != NULL);
if (property->is_static()) {
__ push(Operand(esp, kPointerSize)); // constructor
} else {
__ push(Operand(esp, 0)); // prototype
}
VisitForStackValue(key);
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
case ObjectLiteral::Property::COMPUTED:
case ObjectLiteral::Property::PROTOTYPE:
VisitForStackValue(value);
__ push(Immediate(Smi::FromInt(NONE)));
__ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
break;
case ObjectLiteral::Property::GETTER:
VisitForStackValue(value);
__ push(Immediate(isolate()->factory()->null_value()));
__ push(Immediate(Smi::FromInt(NONE)));
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
break;
case ObjectLiteral::Property::SETTER:
__ push(Immediate(isolate()->factory()->null_value()));
VisitForStackValue(value);
__ push(Immediate(Smi::FromInt(NONE)));
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
break;
default:
UNREACHABLE();
}
}
// prototype
__ CallRuntime(Runtime::kToFastProperties, 1);
// constructor
__ CallRuntime(Runtime::kToFastProperties, 1);
}
void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode) {
......
......@@ -2420,6 +2420,67 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
}
void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
// Constructor is in rax.
DCHECK(lit != NULL);
__ Push(rax);
// No access check is needed here since the constructor is created by the
// class literal.
Register scratch = rbx;
__ movp(scratch, FieldOperand(rax, JSFunction::kPrototypeOrInitialMapOffset));
__ Push(scratch);
for (int i = 0; i < lit->properties()->length(); i++) {
ObjectLiteral::Property* property = lit->properties()->at(i);
Literal* key = property->key()->AsLiteral();
Expression* value = property->value();
DCHECK(key != NULL);
if (property->is_static()) {
__ Push(Operand(rsp, kPointerSize)); // constructor
} else {
__ Push(Operand(rsp, 0)); // prototype
}
VisitForStackValue(key);
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT:
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
case ObjectLiteral::Property::COMPUTED:
case ObjectLiteral::Property::PROTOTYPE:
VisitForStackValue(value);
__ Push(Smi::FromInt(NONE));
__ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
break;
case ObjectLiteral::Property::GETTER:
VisitForStackValue(value);
__ Push(isolate()->factory()->null_value());
__ Push(Smi::FromInt(NONE));
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
break;
case ObjectLiteral::Property::SETTER:
__ Push(isolate()->factory()->null_value());
VisitForStackValue(value);
__ Push(Smi::FromInt(NONE));
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
break;
default:
UNREACHABLE();
}
}
// prototype
__ CallRuntime(Runtime::kToFastProperties, 1);
// constructor
__ CallRuntime(Runtime::kToFastProperties, 1);
}
void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode) {
......
......@@ -153,6 +153,240 @@
})();
(function TestToString() {
class C {}
assertEquals('class C {}', C.toString());
class D { constructor() { 42; } }
assertEquals('class D { constructor() { 42; } }', D.toString());
class E { x() { 42; } }
assertEquals('class E { x() { 42; } }', E.toString());
})();
function assertMethodDescriptor(object, name) {
var descr = Object.getOwnPropertyDescriptor(object, name);
assertTrue(descr.configurable);
assertTrue(descr.enumerable);
assertTrue(descr.writable);
assertEquals('function', typeof descr.value);
}
function assertGetterDescriptor(object, name) {
var descr = Object.getOwnPropertyDescriptor(object, name);
assertTrue(descr.configurable);
assertTrue(descr.enumerable);
assertEquals('function', typeof descr.get);
assertEquals(undefined, descr.set);
}
function assertSetterDescriptor(object, name) {
var descr = Object.getOwnPropertyDescriptor(object, name);
assertTrue(descr.configurable);
assertTrue(descr.enumerable);
assertEquals(undefined, descr.get);
assertEquals('function', typeof descr.set);
}
function assertAccessorDescriptor(object, name) {
var descr = Object.getOwnPropertyDescriptor(object, name);
assertTrue(descr.configurable);
assertTrue(descr.enumerable);
assertEquals('function', typeof descr.get);
assertEquals('function', typeof descr.set);
}
(function TestMethods() {
class C {
method() { return 1; }
static staticMethod() { return 2; }
method2() { return 3; }
static staticMethod2() { return 4; }
}
assertMethodDescriptor(C.prototype, 'method');
assertMethodDescriptor(C.prototype, 'method2');
assertMethodDescriptor(C, 'staticMethod');
assertMethodDescriptor(C, 'staticMethod2');
assertEquals(1, new C().method());
assertEquals(2, C.staticMethod());
assertEquals(3, new C().method2());
assertEquals(4, C.staticMethod2());
})();
(function TestGetters() {
class C {
get x() { return 1; }
static get staticX() { return 2; }
get y() { return 3; }
static get staticY() { return 4; }
}
assertGetterDescriptor(C.prototype, 'x');
assertGetterDescriptor(C.prototype, 'y');
assertGetterDescriptor(C, 'staticX');
assertGetterDescriptor(C, 'staticY');
assertEquals(1, new C().x);
assertEquals(2, C.staticX);
assertEquals(3, new C().y);
assertEquals(4, C.staticY);
})();
(function TestSetters() {
var x, staticX, y, staticY;
class C {
set x(v) { x = v; }
static set staticX(v) { staticX = v; }
set y(v) { y = v; }
static set staticY(v) { staticY = v; }
}
assertSetterDescriptor(C.prototype, 'x');
assertSetterDescriptor(C.prototype, 'y');
assertSetterDescriptor(C, 'staticX');
assertSetterDescriptor(C, 'staticY');
assertEquals(1, new C().x = 1);
assertEquals(1, x);
assertEquals(2, C.staticX = 2);
assertEquals(2, staticX);
assertEquals(3, new C().y = 3);
assertEquals(3, y);
assertEquals(4, C.staticY = 4);
assertEquals(4, staticY);
})();
(function TestSideEffectsInPropertyDefine() {
function B() {}
B.prototype = {
constructor: B,
set m(v) {
throw Error();
}
};
class C extends B {
m() { return 1; }
}
assertEquals(1, new C().m());
})();
(function TestAccessors() {
class C {
constructor(x) {
this._x = x;
}
get x() { return this._x; }
set x(v) { this._x = v; }
static get staticX() { return this._x; }
static set staticX(v) { this._x = v; }
}
assertAccessorDescriptor(C.prototype, 'x');
assertAccessorDescriptor(C, 'staticX');
var c = new C(1);
c._x = 1;
assertEquals(1, c.x);
c.x = 2;
assertEquals(2, c._x);
C._x = 3;
assertEquals(3, C.staticX);
C._x = 4;
assertEquals(4, C.staticX );
})();
(function TestProto() {
class C {
__proto__() { return 1; }
}
assertMethodDescriptor(C.prototype, '__proto__');
assertEquals(1, new C().__proto__());
})();
(function TestProtoStatic() {
class C {
static __proto__() { return 1; }
}
assertMethodDescriptor(C, '__proto__');
assertEquals(1, C.__proto__());
})();
(function TestProtoAccessor() {
class C {
get __proto__() { return this._p; }
set __proto__(v) { this._p = v; }
}
assertAccessorDescriptor(C.prototype, '__proto__');
var c = new C();
c._p = 1;
assertEquals(1, c.__proto__);
c.__proto__ = 2;
assertEquals(2, c.__proto__);
})();
(function TestStaticProtoAccessor() {
class C {
static get __proto__() { return this._p; }
static set __proto__(v) { this._p = v; }
}
assertAccessorDescriptor(C, '__proto__');
C._p = 1;
assertEquals(1, C.__proto__);
C.__proto__ = 2;
assertEquals(2, C.__proto__);
})();
(function TestSettersOnProto() {
function Base() {}
Base.prototype = {
set constructor(_) {
assertUnreachable();
},
set m(_) {
assertUnreachable();
}
};
Object.defineProperty(Base, 'staticM', {
set: function() {
assertUnreachable();
}
});
class C extends Base {
m() {
return 1;
}
static staticM() {
return 2;
}
}
assertEquals(1, new C().m());
assertEquals(2, C.staticM());
})();
/* TODO(arv): Implement
(function TestNameBindingInConstructor() {
class C {
......@@ -165,15 +399,3 @@
new C();
})();
*/
(function TestToString() {
class C {}
assertEquals('class C {}', C.toString());
class D { constructor() { 42; } }
assertEquals('class D { constructor() { 42; } }', D.toString());
class E { x() { 42; } }
assertEquals('class E { x() { 42; } }', E.toString());
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment