__super() method is now properly set on context
parent
f43959640c
commit
5cb0b8355f
|
@ -22,7 +22,8 @@
|
||||||
* @package core
|
* @package core
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var util = require( './util' ),
|
var util = require( __dirname + '/util' ),
|
||||||
|
|
||||||
fallback = util.definePropertyFallback(),
|
fallback = util.definePropertyFallback(),
|
||||||
visibility = [ 'public', 'protected', 'private' ];
|
visibility = [ 'public', 'protected', 'private' ];
|
||||||
|
|
||||||
|
@ -361,11 +362,18 @@ function overrideMethod( super_method, new_method, instCallback, cid )
|
||||||
retval = undefined
|
retval = undefined
|
||||||
;
|
;
|
||||||
|
|
||||||
// the _super property will contain the parent method
|
// the _super property will contain the parent method (we don't
|
||||||
this.__super = super_method;
|
// 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 );
|
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
|
// if the value returned from the method was the context that we
|
||||||
// passed in, return the actual instance (to ensure we do not break
|
// passed in, return the actual instance (to ensure we do not break
|
||||||
// encapsulation)
|
// encapsulation)
|
||||||
|
|
|
@ -24,12 +24,13 @@
|
||||||
|
|
||||||
var common = require( './common' ),
|
var common = require( './common' ),
|
||||||
assert = require( 'assert' ),
|
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.funcVal = 'foobar';
|
||||||
mb_common.value = function() { return mb_common.funcVal; };
|
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
|
// do assertions common to all member builders
|
||||||
mb_common.assertCommon();
|
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
|
* Once a concrete implementation has been defined for a method, a subtype
|
||||||
* cannot make it abstract.
|
* cannot make it abstract.
|
||||||
|
|
Loading…
Reference in New Issue