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;
|
: 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
|
// ensures that proxies can be used to provide concrete
|
||||||
// implementations of abstract methods with param requirements (we
|
// implementations of abstract methods with param requirements (we
|
||||||
// have no idea what we'll be proxying to at runtime, so we need to
|
// 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$$';
|
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 );
|
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
|
* Mix trait into the given definition
|
||||||
*
|
*
|
||||||
|
|
|
@ -97,4 +97,41 @@ require( 'common' ).testCase(
|
||||||
|
|
||||||
this.assertEqual( C().foo(), expected );
|
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