From da86ab5d2381f6bac21719809aa62f476458646e Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Mon, 6 Oct 2014 08:25:27 +0000 Subject: [PATCH] Support for super keyed loads where key is a name. R=arv@chromium.org, ishell@chromium.org BUG=v8:3330 LOG=N Review URL: https://codereview.chromium.org/622523004 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24403 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/full-codegen-arm.cc | 72 ++++++++++++++++++++++--- src/arm64/full-codegen-arm64.cc | 72 ++++++++++++++++++++++--- src/full-codegen.h | 5 ++ src/ia32/full-codegen-ia32.cc | 69 +++++++++++++++++++++--- src/runtime/runtime-classes.cc | 60 ++++++++++++++++++--- src/runtime/runtime.cc | 20 +------ src/runtime/runtime.h | 4 ++ src/x64/full-codegen-x64.cc | 70 ++++++++++++++++++++++--- test/mjsunit/harmony/super.js | 113 +++++++++++++++++++++++++++++++++++++++- 9 files changed, 425 insertions(+), 60 deletions(-) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index c908d10..660c10f 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -2382,6 +2382,14 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } +void FullCodeGenerator::EmitKeyedSuperPropertyLoad(Property* prop) { + // Stack: receiver, home_object, key. + SetSourcePosition(prop->position()); + + __ CallRuntime(Runtime::kLoadKeyedFromSuper, 3); +} + + void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, Token::Value op, OverwriteMode mode, @@ -2685,11 +2693,19 @@ void FullCodeGenerator::VisitProperty(Property* expr) { PrepareForBailoutForId(expr->LoadId(), TOS_REG); context()->Plug(r0); } else { - VisitForStackValue(expr->obj()); - VisitForAccumulatorValue(expr->key()); - __ Move(LoadDescriptor::NameRegister(), r0); - __ pop(LoadDescriptor::ReceiverRegister()); - EmitKeyedPropertyLoad(expr); + if (!expr->IsSuperAccess()) { + VisitForStackValue(expr->obj()); + VisitForAccumulatorValue(expr->key()); + __ Move(LoadDescriptor::NameRegister(), r0); + __ pop(LoadDescriptor::ReceiverRegister()); + EmitKeyedPropertyLoad(expr); + } else { + VisitForStackValue(expr->obj()->AsSuperReference()->this_var()); + EmitLoadHomeObject(expr->obj()->AsSuperReference()); + __ Push(result_register()); + VisitForStackValue(expr->key()); + EmitKeyedSuperPropertyLoad(expr); + } context()->Plug(r0); } } @@ -2801,6 +2817,43 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, } +void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) { + Expression* callee = expr->expression(); + DCHECK(callee->IsProperty()); + Property* prop = callee->AsProperty(); + DCHECK(prop->IsSuperAccess()); + + SetSourcePosition(prop->position()); + // Load the function from the receiver. + const Register scratch = r1; + SuperReference* super_ref = prop->obj()->AsSuperReference(); + EmitLoadHomeObject(super_ref); + __ Push(r0); + VisitForAccumulatorValue(super_ref->this_var()); + __ Push(r0); + __ Push(r0); + __ ldr(scratch, MemOperand(sp, kPointerSize * 2)); + __ Push(scratch); + VisitForStackValue(prop->key()); + + // Stack here: + // - home_object + // - this (receiver) + // - this (receiver) <-- LoadKeyedFromSuper will pop here and below. + // - home_object + // - key + __ CallRuntime(Runtime::kLoadKeyedFromSuper, 3); + + // Replace home_object with target function. + __ str(r0, MemOperand(sp, kPointerSize)); + + // Stack here: + // - target function + // - this (receiver) + EmitCall(expr, CallICState::METHOD); +} + + void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) { // Load the arguments. ZoneList* args = expr->arguments(); @@ -2950,9 +3003,12 @@ void FullCodeGenerator::VisitCall(Call* expr) { } else if (call_type == Call::PROPERTY_CALL) { Property* property = callee->AsProperty(); bool is_named_call = property->key()->IsPropertyName(); - // super.x() is handled in EmitCallWithLoadIC. - if (property->IsSuperAccess() && is_named_call) { - EmitSuperCallWithLoadIC(expr); + if (property->IsSuperAccess()) { + if (is_named_call) { + EmitSuperCallWithLoadIC(expr); + } else { + EmitKeyedSuperCallWithLoadIC(expr); + } } else { { PreservePositionScope scope(masm()->positions_recorder()); diff --git a/src/arm64/full-codegen-arm64.cc b/src/arm64/full-codegen-arm64.cc index f73d214..eeaef5f 100644 --- a/src/arm64/full-codegen-arm64.cc +++ b/src/arm64/full-codegen-arm64.cc @@ -2032,6 +2032,14 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } +void FullCodeGenerator::EmitKeyedSuperPropertyLoad(Property* prop) { + // Stack: receiver, home_object, key. + SetSourcePosition(prop->position()); + + __ CallRuntime(Runtime::kLoadKeyedFromSuper, 3); +} + + void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, Token::Value op, OverwriteMode mode, @@ -2350,11 +2358,19 @@ void FullCodeGenerator::VisitProperty(Property* expr) { PrepareForBailoutForId(expr->LoadId(), TOS_REG); context()->Plug(x0); } else { - VisitForStackValue(expr->obj()); - VisitForAccumulatorValue(expr->key()); - __ Move(LoadDescriptor::NameRegister(), x0); - __ Pop(LoadDescriptor::ReceiverRegister()); - EmitKeyedPropertyLoad(expr); + if (!expr->IsSuperAccess()) { + VisitForStackValue(expr->obj()); + VisitForAccumulatorValue(expr->key()); + __ Move(LoadDescriptor::NameRegister(), x0); + __ Pop(LoadDescriptor::ReceiverRegister()); + EmitKeyedPropertyLoad(expr); + } else { + VisitForStackValue(expr->obj()->AsSuperReference()->this_var()); + EmitLoadHomeObject(expr->obj()->AsSuperReference()); + __ Push(result_register()); + VisitForStackValue(expr->key()); + EmitKeyedSuperPropertyLoad(expr); + } context()->Plug(x0); } } @@ -2463,6 +2479,43 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, } +void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) { + Expression* callee = expr->expression(); + DCHECK(callee->IsProperty()); + Property* prop = callee->AsProperty(); + DCHECK(prop->IsSuperAccess()); + + SetSourcePosition(prop->position()); + + // Load the function from the receiver. + const Register scratch = x10; + SuperReference* super_ref = callee->AsProperty()->obj()->AsSuperReference(); + EmitLoadHomeObject(super_ref); + __ Push(x0); + VisitForAccumulatorValue(super_ref->this_var()); + __ Push(x0); + __ Peek(scratch, kPointerSize); + __ Push(x0, scratch); + VisitForStackValue(prop->key()); + + // Stack here: + // - home_object + // - this (receiver) + // - this (receiver) <-- LoadKeyedFromSuper will pop here and below. + // - home_object + // - key + __ CallRuntime(Runtime::kLoadKeyedFromSuper, 3); + + // Replace home_object with target function. + __ Poke(x0, kPointerSize); + + // Stack here: + // - target function + // - this (receiver) + EmitCall(expr, CallICState::METHOD); +} + + void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) { // Load the arguments. ZoneList* args = expr->arguments(); @@ -2616,9 +2669,12 @@ void FullCodeGenerator::VisitCall(Call* expr) { } else if (call_type == Call::PROPERTY_CALL) { Property* property = callee->AsProperty(); bool is_named_call = property->key()->IsPropertyName(); - // super.x() is handled in EmitCallWithLoadIC. - if (property->IsSuperAccess() && is_named_call) { - EmitSuperCallWithLoadIC(expr); + if (property->IsSuperAccess()) { + if (is_named_call) { + EmitSuperCallWithLoadIC(expr); + } else { + EmitKeyedSuperCallWithLoadIC(expr); + } } else { { PreservePositionScope scope(masm()->positions_recorder()); diff --git a/src/full-codegen.h b/src/full-codegen.h index 3d5af4c..4c53c15 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -479,6 +479,7 @@ class FullCodeGenerator: public AstVisitor { void EmitCallWithLoadIC(Call* expr); void EmitSuperCallWithLoadIC(Call* expr); void EmitKeyedCallWithLoadIC(Call* expr, Expression* key); + void EmitKeyedSuperCallWithLoadIC(Call* expr); // Platform-specific code for inline runtime calls. InlineFunctionGenerator FindInlineFunctionGenerator(Runtime::FunctionId id); @@ -525,6 +526,10 @@ class FullCodeGenerator: public AstVisitor { // Expect receiver ('this' value) and home_object on the stack. void EmitNamedSuperPropertyLoad(Property* expr); + // Load a value from super[keyed] property. + // Expect receiver ('this' value), home_object and key on the stack. + void EmitKeyedSuperPropertyLoad(Property* expr); + // Load a value from a keyed property. // The receiver and the key is left on the stack by the IC. void EmitKeyedPropertyLoad(Property* expr); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 13f24d5..39d4d76 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -2300,6 +2300,14 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } +void FullCodeGenerator::EmitKeyedSuperPropertyLoad(Property* prop) { + // Stack: receiver, home_object, key. + SetSourcePosition(prop->position()); + + __ CallRuntime(Runtime::kLoadKeyedFromSuper, 3); +} + + void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, Token::Value op, OverwriteMode mode, @@ -2605,11 +2613,19 @@ void FullCodeGenerator::VisitProperty(Property* expr) { PrepareForBailoutForId(expr->LoadId(), TOS_REG); context()->Plug(eax); } else { - VisitForStackValue(expr->obj()); - VisitForAccumulatorValue(expr->key()); - __ pop(LoadDescriptor::ReceiverRegister()); // Object. - __ Move(LoadDescriptor::NameRegister(), result_register()); // Key. - EmitKeyedPropertyLoad(expr); + if (!expr->IsSuperAccess()) { + VisitForStackValue(expr->obj()); + VisitForAccumulatorValue(expr->key()); + __ pop(LoadDescriptor::ReceiverRegister()); // Object. + __ Move(LoadDescriptor::NameRegister(), result_register()); // Key. + EmitKeyedPropertyLoad(expr); + } else { + VisitForStackValue(expr->obj()->AsSuperReference()->this_var()); + EmitLoadHomeObject(expr->obj()->AsSuperReference()); + __ push(result_register()); + VisitForStackValue(expr->key()); + EmitKeyedSuperPropertyLoad(expr); + } context()->Plug(eax); } } @@ -2712,6 +2728,40 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, } +void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) { + Expression* callee = expr->expression(); + DCHECK(callee->IsProperty()); + Property* prop = callee->AsProperty(); + DCHECK(prop->IsSuperAccess()); + + SetSourcePosition(prop->position()); + // Load the function from the receiver. + SuperReference* super_ref = callee->AsProperty()->obj()->AsSuperReference(); + EmitLoadHomeObject(super_ref); + __ push(eax); + VisitForAccumulatorValue(super_ref->this_var()); + __ push(eax); + __ push(eax); + __ push(Operand(esp, kPointerSize * 2)); + VisitForStackValue(prop->key()); + // Stack here: + // - home_object + // - this (receiver) + // - this (receiver) <-- LoadKeyedFromSuper will pop here and below. + // - home_object + // - key + __ CallRuntime(Runtime::kLoadKeyedFromSuper, 3); + + // Replace home_object with target function. + __ mov(Operand(esp, kPointerSize), eax); + + // Stack here: + // - target function + // - this (receiver) + EmitCall(expr, CallICState::METHOD); +} + + void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) { // Load the arguments. ZoneList* args = expr->arguments(); @@ -2853,9 +2903,12 @@ void FullCodeGenerator::VisitCall(Call* expr) { } else if (call_type == Call::PROPERTY_CALL) { Property* property = callee->AsProperty(); bool is_named_call = property->key()->IsPropertyName(); - // super.x() is handled in EmitCallWithLoadIC. - if (property->IsSuperAccess() && is_named_call) { - EmitSuperCallWithLoadIC(expr); + if (property->IsSuperAccess()) { + if (is_named_call) { + EmitSuperCallWithLoadIC(expr); + } else { + EmitKeyedSuperCallWithLoadIC(expr); + } } else { { PreservePositionScope scope(masm()->positions_recorder()); diff --git a/src/runtime/runtime-classes.cc b/src/runtime/runtime-classes.cc index ce4b2b5..20e98ef 100644 --- a/src/runtime/runtime-classes.cc +++ b/src/runtime/runtime-classes.cc @@ -16,6 +16,28 @@ namespace v8 { namespace internal { +RUNTIME_FUNCTION(Runtime_ThrowNonMethodError) { + HandleScope scope(isolate); + DCHECK(args.length() == 0); + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewReferenceError("non_method", HandleVector(NULL, 0))); +} + + +static Object* ThrowUnsupportedSuper(Isolate* isolate) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, + NewReferenceError("unsupported_super", HandleVector(NULL, 0))); +} + + +RUNTIME_FUNCTION(Runtime_ThrowUnsupportedSuperError) { + HandleScope scope(isolate); + DCHECK(args.length() == 0); + return ThrowUnsupportedSuper(isolate); +} + + RUNTIME_FUNCTION(Runtime_ToMethod) { HandleScope scope(isolate); DCHECK(args.length() == 2); @@ -35,13 +57,8 @@ RUNTIME_FUNCTION(Runtime_HomeObjectSymbol) { } -RUNTIME_FUNCTION(Runtime_LoadFromSuper) { - HandleScope scope(isolate); - DCHECK(args.length() == 3); - CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); - CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); - CONVERT_ARG_HANDLE_CHECKED(Name, name, 2); - +static Object* LoadFromSuper(Isolate* isolate, Handle receiver, + Handle home_object, Handle name) { if (home_object->IsAccessCheckNeeded() && !isolate->MayNamedAccess(home_object, name, v8::ACCESS_GET)) { isolate->ReportFailedAccessCheck(home_object, v8::ACCESS_GET); @@ -59,6 +76,35 @@ RUNTIME_FUNCTION(Runtime_LoadFromSuper) { } +RUNTIME_FUNCTION(Runtime_LoadFromSuper) { + HandleScope scope(isolate); + DCHECK(args.length() == 3); + CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); + CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); + CONVERT_ARG_HANDLE_CHECKED(Name, name, 2); + + return LoadFromSuper(isolate, receiver, home_object, name); +} + + +RUNTIME_FUNCTION(Runtime_LoadKeyedFromSuper) { + HandleScope scope(isolate); + DCHECK(args.length() == 3); + CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); + CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); + CONVERT_ARG_HANDLE_CHECKED(Object, key, 2); + + Handle name; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name, + Runtime::ToName(isolate, key)); + uint32_t index; + if (name->AsArrayIndex(&index)) { + return ThrowUnsupportedSuper(isolate); + } + return LoadFromSuper(isolate, receiver, home_object, name); +} + + static Object* StoreToSuper(Isolate* isolate, Handle home_object, Handle receiver, Handle name, Handle value, StrictMode strict_mode) { diff --git a/src/runtime/runtime.cc b/src/runtime/runtime.cc index cf050d3..837f689 100644 --- a/src/runtime/runtime.cc +++ b/src/runtime/runtime.cc @@ -463,8 +463,7 @@ MaybeHandle Runtime::GetElementOrCharAt(Isolate* isolate, } -MUST_USE_RESULT -static MaybeHandle ToName(Isolate* isolate, Handle key) { +MaybeHandle Runtime::ToName(Isolate* isolate, Handle key) { if (key->IsName()) { return Handle::cast(key); } else { @@ -1742,23 +1741,6 @@ RUNTIME_FUNCTION(Runtime_ThrowReferenceError) { } -RUNTIME_FUNCTION(Runtime_ThrowNonMethodError) { - HandleScope scope(isolate); - DCHECK(args.length() == 0); - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewReferenceError("non_method", HandleVector(NULL, 0))); -} - - -RUNTIME_FUNCTION(Runtime_ThrowUnsupportedSuperError) { - HandleScope scope(isolate); - DCHECK(args.length() == 0); - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, - NewReferenceError("unsupported_super", HandleVector(NULL, 0))); -} - - RUNTIME_FUNCTION(Runtime_PromiseRejectEvent) { DCHECK(args.length() == 3); HandleScope scope(isolate); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 9fb4c11..e1bc926 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -190,6 +190,7 @@ namespace internal { F(ThrowNonMethodError, 0, 1) \ F(ThrowUnsupportedSuperError, 0, 1) \ F(LoadFromSuper, 3, 1) \ + F(LoadKeyedFromSuper, 3, 1) \ F(StoreToSuper_Strict, 4, 1) \ F(StoreToSuper_Sloppy, 4, 1) @@ -852,6 +853,9 @@ class Runtime : public AllStatic { MUST_USE_RESULT static MaybeHandle GetObjectProperty( Isolate* isolate, Handle object, Handle key); + MUST_USE_RESULT static MaybeHandle ToName(Isolate* isolate, + Handle key); + static void SetupArrayBuffer(Isolate* isolate, Handle array_buffer, bool is_external, void* data, diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 75210c1..d351ff4 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -2332,6 +2332,14 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } +void FullCodeGenerator::EmitKeyedSuperPropertyLoad(Property* prop) { + // Stack: receiver, home_object, key. + SetSourcePosition(prop->position()); + + __ CallRuntime(Runtime::kLoadKeyedFromSuper, 3); +} + + void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, Token::Value op, OverwriteMode mode, @@ -2599,11 +2607,19 @@ void FullCodeGenerator::VisitProperty(Property* expr) { PrepareForBailoutForId(expr->LoadId(), TOS_REG); context()->Plug(rax); } else { - VisitForStackValue(expr->obj()); - VisitForAccumulatorValue(expr->key()); - __ Move(LoadDescriptor::NameRegister(), rax); - __ Pop(LoadDescriptor::ReceiverRegister()); - EmitKeyedPropertyLoad(expr); + if (!expr->IsSuperAccess()) { + VisitForStackValue(expr->obj()); + VisitForAccumulatorValue(expr->key()); + __ Move(LoadDescriptor::NameRegister(), rax); + __ Pop(LoadDescriptor::ReceiverRegister()); + EmitKeyedPropertyLoad(expr); + } else { + VisitForStackValue(expr->obj()->AsSuperReference()->this_var()); + EmitLoadHomeObject(expr->obj()->AsSuperReference()); + __ Push(result_register()); + VisitForStackValue(expr->key()); + EmitKeyedSuperPropertyLoad(expr); + } context()->Plug(rax); } } @@ -2707,6 +2723,41 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, } +void FullCodeGenerator::EmitKeyedSuperCallWithLoadIC(Call* expr) { + Expression* callee = expr->expression(); + DCHECK(callee->IsProperty()); + Property* prop = callee->AsProperty(); + DCHECK(prop->IsSuperAccess()); + + SetSourcePosition(prop->position()); + // Load the function from the receiver. + SuperReference* super_ref = prop->obj()->AsSuperReference(); + EmitLoadHomeObject(super_ref); + __ Push(rax); + VisitForAccumulatorValue(super_ref->this_var()); + __ Push(rax); + __ Push(rax); + __ Push(Operand(rsp, kPointerSize * 2)); + VisitForStackValue(prop->key()); + + // Stack here: + // - home_object + // - this (receiver) + // - this (receiver) <-- LoadKeyedFromSuper will pop here and below. + // - home_object + // - key + __ CallRuntime(Runtime::kLoadKeyedFromSuper, 3); + + // Replace home_object with target function. + __ movp(Operand(rsp, kPointerSize), rax); + + // Stack here: + // - target function + // - this (receiver) + EmitCall(expr, CallICState::METHOD); +} + + void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) { // Load the arguments. ZoneList* args = expr->arguments(); @@ -2849,9 +2900,12 @@ void FullCodeGenerator::VisitCall(Call* expr) { } else if (call_type == Call::PROPERTY_CALL) { Property* property = callee->AsProperty(); bool is_named_call = property->key()->IsPropertyName(); - // super.x() is handled in EmitCallWithLoadIC. - if (property->IsSuperAccess() && is_named_call) { - EmitSuperCallWithLoadIC(expr); + if (property->IsSuperAccess()) { + if (is_named_call) { + EmitSuperCallWithLoadIC(expr); + } else { + EmitKeyedSuperCallWithLoadIC(expr); + } } else { { PreservePositionScope scope(masm()->positions_recorder()); diff --git a/test/mjsunit/harmony/super.js b/test/mjsunit/harmony/super.js index 35f6469..6e56c60 100644 --- a/test/mjsunit/harmony/super.js +++ b/test/mjsunit/harmony/super.js @@ -37,6 +37,41 @@ }()); +(function TestSuperKeyedLoads() { + var x = 'x'; + var derivedDataProperty = 'derivedDataProperty'; + var f = 'f'; + function Base() { } + function Derived() { + this[derivedDataProperty] = 'xxx'; + } + Derived.prototype = Object.create(Base.prototype); + + function fBase() { return "Base " + this.toString(); } + + Base.prototype[f] = fBase.toMethod(Base.prototype); + + function fDerived() { + assertEquals("Base this is Derived", super[f]()); + var a = super[x]; + assertEquals(15, a); + assertEquals(15, super[x]); + assertEquals(27, this[x]); + + return "Derived" + } + + Base.prototype[x] = 15; + Base.prototype.toString = function() { return "this is Base"; }; + Derived.prototype.toString = function() { return "this is Derived"; }; + Derived.prototype[x] = 27; + Derived.prototype[f] = fDerived.toMethod(Derived.prototype); + + assertEquals("Base this is Base", new Base().f()); + assertEquals("Derived", new Derived().f()); +}()); + + (function TestSuperKeywordNonMethod() { function f() { super.unknown(); @@ -79,6 +114,80 @@ }()); +(function TestGetterKeyed() { + var x = 'x'; + function Base() {} + var derived; + Base.prototype = { + constructor: Base, + get x() { + assertSame(this, derived); + return this._x; + }, + _x: 'base' + }; + + function Derived() {} + Derived.__proto__ = Base; + Derived.prototype = { + __proto__: Base.prototype, + constructor: Derived, + _x: 'derived' + }; + Derived.prototype.testGetter = function() { + return super[x]; + }.toMethod(Derived.prototype); + Derived.prototype.testGetterStrict = function() { + 'use strict'; + return super[x]; + }.toMethod(Derived.prototype); + Derived.prototype.testGetterWithToString = function() { + var toStringCalled; + var o = { toString: function() { + toStringCalled++; + return 'x'; + } }; + + toStringCalled = 0; + assertEquals('derived', super[o]); + assertEquals(1, toStringCalled); + + var eToThrow = new Error(); + var oThrowsInToString = { toString: function() { + throw eToThrow; + } }; + + var ex = null; + try { + super[oThrowsInToString]; + } catch(e) { ex = e } + assertEquals(eToThrow, ex); + + var oReturnsNumericString = { toString: function() { + return "1"; + } }; + + ex = null; + try { + super[oReturnsNumericString]; + } catch(e) { ex = e } + assertTrue(ex instanceof ReferenceError); + + ex = null; + try { + super[1]; // Indexed properties unsupported yet. + } catch (e) { ex = e; } + assertTrue(ex instanceof ReferenceError); + }.toMethod(Derived.prototype); + derived = new Derived(); + assertEquals('derived', derived.testGetter()); + derived = new Derived(); + assertEquals('derived', derived.testGetterStrict()); + derived = new Derived(); + derived.testGetterWithToString(); +}()); + + (function TestSetter() { function Base() {} Base.prototype = { @@ -536,6 +645,6 @@ function f1(x) { return super[x]; } function f2(x) { super[x] = 5; } var o = {}; - assertThrows(function(){f1.toMethod(o)(x);}, ReferenceError); - assertThrows(function(){f2.toMethod(o)(x);}, ReferenceError); + assertThrows(function(){f1.toMethod(o)(15);}, ReferenceError); + assertThrows(function(){f2.toMethod(o)(15);}, ReferenceError); }()); -- 2.7.4