From 99cafa0d5a34bb9091688952da0ee80853f8472a Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Thu, 23 Oct 2014 08:11:51 +0000 Subject: [PATCH] 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 --- src/arm/full-codegen-arm.cc | 20 +++++++++--- src/arm64/full-codegen-arm64.cc | 20 +++++++++--- src/full-codegen.h | 2 ++ src/ia32/full-codegen-ia32.cc | 18 ++++++++--- src/x64/full-codegen-x64.cc | 18 ++++++++--- test/mjsunit/harmony/super.js | 56 ++++++++++++++++++++++++++------- 6 files changed, 104 insertions(+), 30 deletions(-) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 01fa5c5698..23d4ecfce3 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -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* args = expr->arguments(); diff --git a/src/arm64/full-codegen-arm64.cc b/src/arm64/full-codegen-arm64.cc index d46183339b..2fee487eb1 100644 --- a/src/arm64/full-codegen-arm64.cc +++ b/src/arm64/full-codegen-arm64.cc @@ -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* args = expr->arguments(); diff --git a/src/full-codegen.h b/src/full-codegen.h index 078b69be6a..1aeb86196c 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -609,6 +609,8 @@ class FullCodeGenerator: public AstVisitor { void EmitLoadHomeObject(SuperReference* expr); + void EmitLoadSuperConstructor(SuperReference* expr); + void CallIC(Handle code, TypeFeedbackId id = TypeFeedbackId::None()); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index aa9acf3a48..903422fecd 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -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* args = expr->arguments(); diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index e6fecdd342..1a594ec043 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -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* args = expr->arguments(); diff --git a/test/mjsunit/harmony/super.js b/test/mjsunit/harmony/super.js index a2e07ed032..d972407520 100644 --- a/test/mjsunit/harmony/super.js +++ b/test/mjsunit/harmony/super.js @@ -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); }());