__super() method is now properly set on context
parent
f43959640c
commit
5cb0b8355f
|
@ -22,7 +22,8 @@
|
|||
* @package core
|
||||
*/
|
||||
|
||||
var util = require( './util' ),
|
||||
var util = require( __dirname + '/util' ),
|
||||
|
||||
fallback = util.definePropertyFallback(),
|
||||
visibility = [ 'public', 'protected', 'private' ];
|
||||
|
||||
|
@ -361,11 +362,18 @@ function overrideMethod( super_method, new_method, instCallback, cid )
|
|||
retval = undefined
|
||||
;
|
||||
|
||||
// the _super property will contain the parent method
|
||||
this.__super = super_method;
|
||||
// the _super property will contain the parent method (we don't
|
||||
// store the previous value for performance reasons and because,
|
||||
// during conventional use, it's completely unnecessary)
|
||||
context.__super = super_method;
|
||||
|
||||
retval = new_method.apply( context, arguments );
|
||||
|
||||
// prevent sneaky bastards from breaking encapsulation by stealing
|
||||
// method references (we set to undefined rather than deleting it
|
||||
// because deletion causes performance degradation within V8)
|
||||
context.__super = undefined;
|
||||
|
||||
// if the value returned from the method was the context that we
|
||||
// passed in, return the actual instance (to ensure we do not break
|
||||
// encapsulation)
|
||||
|
|
|
@ -24,12 +24,13 @@
|
|||
|
||||
var common = require( './common' ),
|
||||
assert = require( 'assert' ),
|
||||
mb_common = require( __dirname + '/inc-member_builder-common' )
|
||||
mb_common = require( __dirname + '/inc-member_builder-common' ),
|
||||
builder = common.require( 'member_builder' )
|
||||
;
|
||||
|
||||
mb_common.funcVal = 'foobar';
|
||||
mb_common.value = function() { return mb_common.funcVal; };
|
||||
mb_common.buildMember = common.require( 'member_builder' ).buildMethod;
|
||||
mb_common.buildMember = builder.buildMethod;
|
||||
|
||||
// do assertions common to all member builders
|
||||
mb_common.assertCommon();
|
||||
|
@ -137,6 +138,57 @@ mb_common.assertCommon();
|
|||
} )();
|
||||
|
||||
|
||||
/**
|
||||
* If the method is called when bound to a different context (e.g. for
|
||||
* protected/private members), __super may not be properly bound.
|
||||
*
|
||||
* This test is in response to a bug found after implementing visibility
|
||||
* support. The __super() method was previously defined on 'this', which may or
|
||||
* may not be the context that is actually used. Likely, it's not.
|
||||
*/
|
||||
( function testSuperMethodWorksProperlyWhenContextDiffers()
|
||||
{
|
||||
var members = builder.initMembers(),
|
||||
super_called = false,
|
||||
retobj = {},
|
||||
instCallback = function()
|
||||
{
|
||||
return retobj;
|
||||
},
|
||||
|
||||
// the overriding method
|
||||
newfunc = function()
|
||||
{
|
||||
this.__super();
|
||||
}
|
||||
;
|
||||
|
||||
// super method to be overridden
|
||||
members[ 'public' ].foo = function()
|
||||
{
|
||||
super_called = true;
|
||||
};
|
||||
|
||||
// override
|
||||
builder.buildMethod( members, {}, 'foo', newfunc, {}, instCallback );
|
||||
|
||||
// call the overriding method
|
||||
members[ 'public' ].foo();
|
||||
|
||||
// ensure that the super method was called
|
||||
assert.equal( super_called, true,
|
||||
"__super() method is called even when context differs"
|
||||
);
|
||||
|
||||
// finally, ensure that __super is no longer set on the returned object
|
||||
// after the call to ensure that the caller cannot break encapsulation by
|
||||
// stealing a method reference (sneaky, sneaky)
|
||||
assert.equal( retobj.__super, undefined,
|
||||
"__super() method is unset after being called"
|
||||
);
|
||||
} )();
|
||||
|
||||
|
||||
/**
|
||||
* Once a concrete implementation has been defined for a method, a subtype
|
||||
* cannot make it abstract.
|
||||
|
|
Loading…
Reference in New Issue