Revision: 24934
Author: [email protected]
Date: Tue Oct 28 12:43:05 2014 UTC
Log: Classes: Add basic support for properties
This adds the properties to the prototype and the constructor.
BUG=v8:3330
LOG=Y
[email protected]
Review URL: https://codereview.chromium.org/680993003
https://code.google.com/p/v8/source/detail?r=24934
Modified:
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/arm64/full-codegen-arm64.cc
/branches/bleeding_edge/src/ast.h
/branches/bleeding_edge/src/full-codegen.cc
/branches/bleeding_edge/src/full-codegen.h
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
/branches/bleeding_edge/test/mjsunit/harmony/classes.js
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue Oct 28 10:00:37
2014 UTC
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Tue Oct 28 12:43:05
2014 UTC
@@ -2503,6 +2503,74 @@
__ bind(&done);
context()->Plug(r0);
}
+
+
+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,
=======================================
--- /branches/bleeding_edge/src/arm64/full-codegen-arm64.cc Tue Oct 28
10:00:37 2014 UTC
+++ /branches/bleeding_edge/src/arm64/full-codegen-arm64.cc Tue Oct 28
12:43:05 2014 UTC
@@ -300,7 +300,8 @@
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 @@
__ 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::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::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 @@
// 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);
=======================================
--- /branches/bleeding_edge/src/ast.h Mon Oct 27 16:25:11 2014 UTC
+++ /branches/bleeding_edge/src/ast.h Tue Oct 28 12:43:05 2014 UTC
@@ -1511,6 +1511,8 @@
void set_emit_store(bool emit_store);
bool emit_store();
+
+ bool is_static() const { return is_static_; }
protected:
template<class> friend class AstNodeFactory;
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Mon Oct 27 16:25:11 2014 UTC
+++ /branches/bleeding_edge/src/full-codegen.cc Tue Oct 28 12:43:05 2014 UTC
@@ -1585,9 +1585,9 @@
__ 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());
}
=======================================
--- /branches/bleeding_edge/src/full-codegen.h Thu Oct 23 08:32:23 2014 UTC
+++ /branches/bleeding_edge/src/full-codegen.h Tue Oct 28 12:43:05 2014 UTC
@@ -563,6 +563,11 @@
// 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,
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue Oct 28
10:00:37 2014 UTC
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue Oct 28
12:43:05 2014 UTC
@@ -2419,6 +2419,67 @@
__ bind(&done);
context()->Plug(eax);
}
+
+
+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,
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue Oct 28 10:00:37
2014 UTC
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Tue Oct 28 12:43:05
2014 UTC
@@ -2418,6 +2418,67 @@
__ bind(&done);
context()->Plug(rax);
}
+
+
+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,
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/classes.js Wed Oct 8
14:48:48 2014 UTC
+++ /branches/bleeding_edge/test/mjsunit/harmony/classes.js Tue Oct 28
12:43:05 2014 UTC
@@ -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());
-})();
--
--
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.