1
0
Fork 0

Methods can now be properly overridden when visibility is escalated

closure/master
Mike Gerwitz 2011-04-01 06:28:45 -04:00
parent 170eb06af6
commit 61f2f7e22d
2 changed files with 52 additions and 3 deletions

View File

@ -61,7 +61,12 @@ exports.setup = function( dest, properties, methods )
// initialize each of the properties for this instance to // initialize each of the properties for this instance to
// ensure we're not sharing references to prototype values // ensure we're not sharing references to prototype values
doSetup( dest, properties[ 'public' ] ); doSetup( dest, properties[ 'public' ] );
doSetup( dest, properties[ 'protected' ], methods[ 'protected'] );
// Do the same for protected, but only if they do not exist already in
// public. The reason for this is because the property object is laid /atop/
// of the public members, meaning that a parent's protected members will
// take precedence over a subtype's overriding /public/ members. Uh oh.
doSetup( dest, properties[ 'protected' ], methods[ 'protected' ], true );
// then add the private parts // then add the private parts
doSetup( obj, properties[ 'private' ], methods[ 'private' ] ); doSetup( obj, properties[ 'private' ], methods[ 'private' ] );
@ -79,7 +84,7 @@ exports.setup = function( dest, properties, methods )
* *
* @return {undefined} * @return {undefined}
*/ */
function doSetup( dest, properties, methods ) function doSetup( dest, properties, methods, unless_exists )
{ {
var hasOwn = Array.prototype.hasOwnProperty; var hasOwn = Array.prototype.hasOwnProperty;
@ -89,11 +94,20 @@ function doSetup( dest, properties, methods )
for ( method_name in methods ) for ( method_name in methods )
{ {
if ( hasOwn.call( methods, method_name ) ) if ( hasOwn.call( methods, method_name ) )
{
// If requested, do not copy the method over if it already
// exists in the destination object. Don't use hasOwn here;
// unnecessary overhead and we want to traverse any prototype
// chains. We do not check the public object directly, for
// example, because we need a solution that will work if a proxy
// is unsupported by the engine.
if ( !unless_exists || ( dest[ method_name ] === undefined ) )
{ {
dest[ method_name ] = methods[ method_name ]; dest[ method_name ] = methods[ method_name ];
} }
} }
} }
}
// initialize private/protected properties and store in instance data // initialize private/protected properties and store in instance data
for ( prop in properties ) for ( prop in properties )

View File

@ -657,3 +657,38 @@ var common = require( './common' ),
}, Error, "Cannot de-escalate visibility for interface members" ); }, Error, "Cannot de-escalate visibility for interface members" );
} )(); } )();
/**
* Due to the way the property object is laid atop of the public members, we
* need to ensure that protected methods' functionality can /actually/ be
* overridden, since the protected method is higher in the prototype chain and
* therefore will be accessed before the public method.
*
* We don't care about private -> protected, because that's not possible through
* inheritance.
*/
( function testCanOverrideProtectedMethodFunctionalityWithPublic()
{
// get the result of invoking overridden foo()
var result = Class(
{
'protected foo': function()
{
return false;
},
} ).extend(
{
// override and escalate visibility of method foo()
'public foo': function()
{
return true;
},
} )().foo();
// if the override was successful, we'll be able to invoke the overridden
// method
assert.equal( result, true,
"Can properly override protected methods with public"
);
} )();