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:
parent
ea3a205af2
commit
99cafa0d5a
@ -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) {
|
void FullCodeGenerator::VisitCall(Call* expr) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// We want to verify that RecordJSReturnSite gets called on all paths
|
// 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) {
|
} else if (call_type == Call::SUPER_CALL) {
|
||||||
SuperReference* super_ref = callee->AsSuperReference();
|
SuperReference* super_ref = callee->AsSuperReference();
|
||||||
DCHECK(super_ref != NULL);
|
EmitLoadSuperConstructor(super_ref);
|
||||||
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
|
||||||
__ Push(r0);
|
|
||||||
__ CallRuntime(Runtime::kGetPrototype, 1);
|
|
||||||
__ Push(result_register());
|
__ Push(result_register());
|
||||||
VisitForStackValue(super_ref->this_var());
|
VisitForStackValue(super_ref->this_var());
|
||||||
EmitCall(expr, CallICState::METHOD);
|
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
|
// 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
|
// receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
|
||||||
// ignored.
|
// 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.
|
// Push the arguments ("left-to-right") on the stack.
|
||||||
ZoneList<Expression*>* args = expr->arguments();
|
ZoneList<Expression*>* args = expr->arguments();
|
||||||
|
@ -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) {
|
void FullCodeGenerator::VisitCall(Call* expr) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// We want to verify that RecordJSReturnSite gets called on all paths
|
// 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) {
|
} else if (call_type == Call::SUPER_CALL) {
|
||||||
SuperReference* super_ref = callee->AsSuperReference();
|
SuperReference* super_ref = callee->AsSuperReference();
|
||||||
DCHECK(super_ref != NULL);
|
EmitLoadSuperConstructor(super_ref);
|
||||||
__ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
|
||||||
__ Push(x0);
|
|
||||||
__ CallRuntime(Runtime::kGetPrototype, 1);
|
|
||||||
__ Push(result_register());
|
__ Push(result_register());
|
||||||
VisitForStackValue(super_ref->this_var());
|
VisitForStackValue(super_ref->this_var());
|
||||||
EmitCall(expr, CallICState::METHOD);
|
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
|
// 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
|
// receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
|
||||||
// ignored.
|
// 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.
|
// Push the arguments ("left-to-right") on the stack.
|
||||||
ZoneList<Expression*>* args = expr->arguments();
|
ZoneList<Expression*>* args = expr->arguments();
|
||||||
|
@ -609,6 +609,8 @@ class FullCodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
void EmitLoadHomeObject(SuperReference* expr);
|
void EmitLoadHomeObject(SuperReference* expr);
|
||||||
|
|
||||||
|
void EmitLoadSuperConstructor(SuperReference* expr);
|
||||||
|
|
||||||
void CallIC(Handle<Code> code,
|
void CallIC(Handle<Code> code,
|
||||||
TypeFeedbackId id = TypeFeedbackId::None());
|
TypeFeedbackId id = TypeFeedbackId::None());
|
||||||
|
|
||||||
|
@ -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) {
|
void FullCodeGenerator::VisitCall(Call* expr) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// We want to verify that RecordJSReturnSite gets called on all paths
|
// 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) {
|
} else if (call_type == Call::SUPER_CALL) {
|
||||||
SuperReference* super_ref = callee->AsSuperReference();
|
SuperReference* super_ref = callee->AsSuperReference();
|
||||||
DCHECK(super_ref != NULL);
|
EmitLoadSuperConstructor(super_ref);
|
||||||
__ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
|
|
||||||
__ CallRuntime(Runtime::kGetPrototype, 1);
|
|
||||||
__ push(result_register());
|
__ push(result_register());
|
||||||
VisitForStackValue(super_ref->this_var());
|
VisitForStackValue(super_ref->this_var());
|
||||||
EmitCall(expr, CallICState::METHOD);
|
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
|
// 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
|
// receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
|
||||||
// ignored.
|
// 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.
|
// Push the arguments ("left-to-right") on the stack.
|
||||||
ZoneList<Expression*>* args = expr->arguments();
|
ZoneList<Expression*>* args = expr->arguments();
|
||||||
|
@ -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) {
|
void FullCodeGenerator::VisitCall(Call* expr) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// We want to verify that RecordJSReturnSite gets called on all paths
|
// 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) {
|
} else if (call_type == Call::SUPER_CALL) {
|
||||||
SuperReference* super_ref = callee->AsSuperReference();
|
SuperReference* super_ref = callee->AsSuperReference();
|
||||||
DCHECK(super_ref != NULL);
|
EmitLoadSuperConstructor(super_ref);
|
||||||
__ Push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
|
|
||||||
__ CallRuntime(Runtime::kGetPrototype, 1);
|
|
||||||
__ Push(result_register());
|
__ Push(result_register());
|
||||||
VisitForStackValue(super_ref->this_var());
|
VisitForStackValue(super_ref->this_var());
|
||||||
EmitCall(expr, CallICState::METHOD);
|
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
|
// 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
|
// receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
|
||||||
// ignored.
|
// 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.
|
// Push the arguments ("left-to-right") on the stack.
|
||||||
ZoneList<Expression*>* args = expr->arguments();
|
ZoneList<Expression*>* args = expr->arguments();
|
||||||
|
@ -1735,19 +1735,19 @@
|
|||||||
}());
|
}());
|
||||||
|
|
||||||
|
|
||||||
(function TestSuperCall() {
|
function Subclass(base, constructor) {
|
||||||
function Subclass(base, constructor) {
|
var homeObject = {
|
||||||
var homeObject = {
|
__proto__: base.prototype,
|
||||||
__proto__: base.prototype,
|
constructor: constructor
|
||||||
constructor: constructor
|
};
|
||||||
};
|
constructor.__proto__ = base;
|
||||||
constructor.__proto__ = base;
|
constructor.prototype = homeObject;
|
||||||
constructor.prototype = homeObject;
|
// not doing toMethod: home object is not required for
|
||||||
// not doing toMethod: home object is not required for
|
// super constructor calls.
|
||||||
// super constructor calls.
|
return constructor;
|
||||||
return constructor;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
(function TestSuperCall() {
|
||||||
var baseCalled = 0;
|
var baseCalled = 0;
|
||||||
var derivedCalled = 0;
|
var derivedCalled = 0;
|
||||||
var derivedDerivedCalled = 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 TestSuperCallErrorCases() {
|
||||||
function T() {
|
function T() {
|
||||||
super();
|
super();
|
||||||
@ -1828,4 +1854,10 @@
|
|||||||
// we throw TypeError.
|
// we throw TypeError.
|
||||||
// Filed https://bugs.ecmascript.org/show_bug.cgi?id=3282
|
// Filed https://bugs.ecmascript.org/show_bug.cgi?id=3282
|
||||||
assertThrows(function() { new T(); }, TypeError);
|
assertThrows(function() { new T(); }, TypeError);
|
||||||
|
|
||||||
|
function T1() {
|
||||||
|
var b = new super();
|
||||||
|
}
|
||||||
|
T1.__proto = null;
|
||||||
|
assertThrows(function() { new T1(); }, TypeError);
|
||||||
}());
|
}());
|
||||||
|
Loading…
Reference in New Issue
Block a user