Classes: implement 'new super'.

R=ishell@chromium.org, arv@chromium.org
BUG=v8:3330
LOG=N

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24822 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dslomov@chromium.org 2014-10-23 08:11:51 +00:00
parent ea3a205af2
commit 99cafa0d5a
6 changed files with 104 additions and 30 deletions

View File

@ -2964,6 +2964,14 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
}
void FullCodeGenerator::EmitLoadSuperConstructor(SuperReference* expr) {
DCHECK(super_ref != NULL);
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ Push(r0);
__ CallRuntime(Runtime::kGetPrototype, 1);
}
void FullCodeGenerator::VisitCall(Call* expr) {
#ifdef DEBUG
// We want to verify that RecordJSReturnSite gets called on all paths
@ -3081,10 +3089,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
}
} else if (call_type == Call::SUPER_CALL) {
SuperReference* super_ref = callee->AsSuperReference();
DCHECK(super_ref != NULL);
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ Push(r0);
__ CallRuntime(Runtime::kGetPrototype, 1);
EmitLoadSuperConstructor(super_ref);
__ Push(result_register());
VisitForStackValue(super_ref->this_var());
EmitCall(expr, CallICState::METHOD);
@ -3116,7 +3121,12 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
// Push constructor on the stack. If it's not a function it's used as
// receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
// ignored.
VisitForStackValue(expr->expression());
if (expr->expression()->IsSuperReference()) {
EmitLoadSuperConstructor(expr->expression()->AsSuperReference());
__ Push(result_register());
} else {
VisitForStackValue(expr->expression());
}
// Push the arguments ("left-to-right") on the stack.
ZoneList<Expression*>* args = expr->arguments();

View File

@ -2628,6 +2628,14 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
}
void FullCodeGenerator::EmitLoadSuperConstructor(SuperReference* expr) {
DCHECK(super_ref != NULL);
__ ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ Push(x0);
__ CallRuntime(Runtime::kGetPrototype, 1);
}
void FullCodeGenerator::VisitCall(Call* expr) {
#ifdef DEBUG
// We want to verify that RecordJSReturnSite gets called on all paths
@ -2746,10 +2754,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
}
} else if (call_type == Call::SUPER_CALL) {
SuperReference* super_ref = callee->AsSuperReference();
DCHECK(super_ref != NULL);
__ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ Push(x0);
__ CallRuntime(Runtime::kGetPrototype, 1);
EmitLoadSuperConstructor(super_ref);
__ Push(result_register());
VisitForStackValue(super_ref->this_var());
EmitCall(expr, CallICState::METHOD);
@ -2781,7 +2786,12 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
// Push constructor on the stack. If it's not a function it's used as
// receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
// ignored.
VisitForStackValue(expr->expression());
if (expr->expression()->IsSuperReference()) {
EmitLoadSuperConstructor(expr->expression()->AsSuperReference());
__ Push(result_register());
} else {
VisitForStackValue(expr->expression());
}
// Push the arguments ("left-to-right") on the stack.
ZoneList<Expression*>* args = expr->arguments();

View File

@ -609,6 +609,8 @@ class FullCodeGenerator: public AstVisitor {
void EmitLoadHomeObject(SuperReference* expr);
void EmitLoadSuperConstructor(SuperReference* expr);
void CallIC(Handle<Code> code,
TypeFeedbackId id = TypeFeedbackId::None());

View File

@ -2867,6 +2867,13 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
}
void FullCodeGenerator::EmitLoadSuperConstructor(SuperReference* expr) {
DCHECK(super_ref != NULL);
__ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ CallRuntime(Runtime::kGetPrototype, 1);
}
void FullCodeGenerator::VisitCall(Call* expr) {
#ifdef DEBUG
// We want to verify that RecordJSReturnSite gets called on all paths
@ -2977,9 +2984,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
}
} else if (call_type == Call::SUPER_CALL) {
SuperReference* super_ref = callee->AsSuperReference();
DCHECK(super_ref != NULL);
__ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ CallRuntime(Runtime::kGetPrototype, 1);
EmitLoadSuperConstructor(super_ref);
__ push(result_register());
VisitForStackValue(super_ref->this_var());
EmitCall(expr, CallICState::METHOD);
@ -3010,7 +3015,12 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
// Push constructor on the stack. If it's not a function it's used as
// receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
// ignored.
VisitForStackValue(expr->expression());
if (expr->expression()->IsSuperReference()) {
EmitLoadSuperConstructor(expr->expression()->AsSuperReference());
__ push(result_register());
} else {
VisitForStackValue(expr->expression());
}
// Push the arguments ("left-to-right") on the stack.
ZoneList<Expression*>* args = expr->arguments();

View File

@ -2867,6 +2867,13 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
}
void FullCodeGenerator::EmitLoadSuperConstructor(SuperReference* expr) {
DCHECK(super_ref != NULL);
__ Push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
__ CallRuntime(Runtime::kGetPrototype, 1);
}
void FullCodeGenerator::VisitCall(Call* expr) {
#ifdef DEBUG
// We want to verify that RecordJSReturnSite gets called on all paths
@ -2976,9 +2983,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
}
} else if (call_type == Call::SUPER_CALL) {
SuperReference* super_ref = callee->AsSuperReference();
DCHECK(super_ref != NULL);
__ Push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
__ CallRuntime(Runtime::kGetPrototype, 1);
EmitLoadSuperConstructor(super_ref);
__ Push(result_register());
VisitForStackValue(super_ref->this_var());
EmitCall(expr, CallICState::METHOD);
@ -3009,7 +3014,12 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
// Push constructor on the stack. If it's not a function it's used as
// receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
// ignored.
VisitForStackValue(expr->expression());
if (expr->expression()->IsSuperReference()) {
EmitLoadSuperConstructor(expr->expression()->AsSuperReference());
__ Push(result_register());
} else {
VisitForStackValue(expr->expression());
}
// Push the arguments ("left-to-right") on the stack.
ZoneList<Expression*>* args = expr->arguments();

View File

@ -1735,19 +1735,19 @@
}());
(function TestSuperCall() {
function Subclass(base, constructor) {
var homeObject = {
__proto__: base.prototype,
constructor: constructor
};
constructor.__proto__ = base;
constructor.prototype = homeObject;
// not doing toMethod: home object is not required for
// super constructor calls.
return constructor;
}
function Subclass(base, constructor) {
var homeObject = {
__proto__: base.prototype,
constructor: constructor
};
constructor.__proto__ = base;
constructor.prototype = homeObject;
// not doing toMethod: home object is not required for
// super constructor calls.
return constructor;
}
(function TestSuperCall() {
var baseCalled = 0;
var derivedCalled = 0;
var derivedDerivedCalled = 0;
@ -1819,6 +1819,32 @@
}());
(function TestNewSuper() {
var baseCalled = 0;
var derivedCalled = 0;
function Base() {
baseCalled++;
this.x = 15;
}
var Derived = Subclass(Base, function() {
baseCalled = 0;
var b = new super();
assertEquals(1, baseCalled)
assertEquals(Base.prototype, b.__proto__);
assertEquals(15, b.x);
assertEquals(undefined, this.x);
derivedCalled++;
});
derivedCalled = 0;
new Derived();
assertEquals(1, derivedCalled);
}());
(function TestSuperCallErrorCases() {
function T() {
super();
@ -1828,4 +1854,10 @@
// we throw TypeError.
// Filed https://bugs.ecmascript.org/show_bug.cgi?id=3282
assertThrows(function() { new T(); }, TypeError);
function T1() {
var b = new super();
}
T1.__proto = null;
assertThrows(function() { new T1(); }, TypeError);
}());