Initial trait virtual member proxy implementation
This has some flaws that should be addressed, but those will be detailed in later commits; this works for now.perfodd
parent
3c7cd0e57a
commit
66cab74cc1
|
@ -123,6 +123,9 @@ exports.standard = {
|
|||
: retval;
|
||||
};
|
||||
|
||||
// TODO: need a test for this; yes, we want to store a reference
|
||||
ret.___$$proxy_to$$ = proxy_to;
|
||||
|
||||
// ensures that proxies can be used to provide concrete
|
||||
// implementations of abstract methods with param requirements (we
|
||||
// have no idea what we'll be proxying to at runtime, so we need to
|
||||
|
|
53
lib/Trait.js
53
lib/Trait.js
|
@ -132,10 +132,63 @@ function createConcrete( acls )
|
|||
dfn[ vis + ' proxy ' + f ] = '___$$pmo$$';
|
||||
}
|
||||
|
||||
// virtual methods need to be handled with care to ensure that we invoke
|
||||
// any overrides
|
||||
createVirtProxy( acls, dfn );
|
||||
|
||||
return acls.extend( dfn );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create virtual method proxies for all virtual members
|
||||
*
|
||||
* Virtual methods are a bit of hassle with traits: we are in a situation
|
||||
* where we do not know at the time that the trait is created whether or not
|
||||
* the virtual method has been overridden, since the class that the trait is
|
||||
* mixed into may do the overriding. Therefore, we must check if an override
|
||||
* has occured *when the method is invoked*; there is room for optimization
|
||||
* there (by making such a determination at the time of mixin), but we'll
|
||||
* leave that for later.
|
||||
*
|
||||
* @param {AbstractClass} acls abstract trait class
|
||||
* @param {Object} dfn destination definition object
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
function createVirtProxy( acls, dfn )
|
||||
{
|
||||
var vmembers = ClassBuilder.getMeta( acls ).virtualMembers;
|
||||
|
||||
// f = `field'
|
||||
for ( var f in vmembers )
|
||||
{
|
||||
var vis = ( acls.___$$methods$$['public'][ f ] !== undefined )
|
||||
? 'public'
|
||||
: 'protected';
|
||||
|
||||
dfn[ vis + ' virtual override ' + f ] = ( function()
|
||||
{
|
||||
// this is the aforementioned proxy method; see the docblock for
|
||||
// more information
|
||||
return function()
|
||||
{
|
||||
var pmo = this.___$$pmo$$,
|
||||
o = pmo[ f ],
|
||||
op = o.___$$proxy_to$$;
|
||||
|
||||
// XXX: a better way to do this would be nice, since this
|
||||
// does a substring check on every call; avoids infinite
|
||||
// recursion from proxying to self
|
||||
return ( o && !( op && op.substr( 0, 7 ) === '___$to$' ) )
|
||||
? pmo[ f ].apply( pmo, arguments )
|
||||
: this.__super.apply( this, arguments );
|
||||
}
|
||||
} )( f );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mix trait into the given definition
|
||||
*
|
||||
|
|
|
@ -97,4 +97,41 @@ require( 'common' ).testCase(
|
|||
|
||||
this.assertEqual( C().foo(), expected );
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* If C uses T and overrides T.Ma, and there is some method T.Mb that
|
||||
* invokes T.Ma, then T.Mb should instead invoke C.Ma.
|
||||
*/
|
||||
'Class-overridden virtual trait method is accessible by trait':
|
||||
function()
|
||||
{
|
||||
var _self = this;
|
||||
|
||||
var T = this.Sut(
|
||||
{
|
||||
'public doFoo': function()
|
||||
{
|
||||
// should call overridden, not the one below
|
||||
this.foo();
|
||||
},
|
||||
|
||||
// to be overridden
|
||||
'virtual protected foo': function()
|
||||
{
|
||||
_self.fail( true, false, "Method not overridden." );
|
||||
},
|
||||
} );
|
||||
|
||||
var called = false;
|
||||
|
||||
var C = this.Class.use( T ).extend(
|
||||
{
|
||||
// should be called by T.doFoo
|
||||
'override protected foo': function() { called = true },
|
||||
} );
|
||||
|
||||
C().doFoo();
|
||||
this.assertOk( called );
|
||||
},
|
||||
} );
|
||||
|
|
Loading…
Reference in New Issue