1
0
Fork 0

Subtype mixin support

perfodd
Mike Gerwitz 2014-02-10 00:37:25 -05:00
parent 451ec48a5c
commit 999c10c3bf
2 changed files with 94 additions and 4 deletions

View File

@ -294,7 +294,7 @@ function mixMethods( src, dest, vis, iname )
function addTraitInst( T, dfn ) function addTraitInst( T, dfn )
{ {
var tc = ( dfn.___$$tc$$ = ( dfn.___$$tc$$ || [] ) ), var tc = ( dfn.___$$tc$$ = ( dfn.___$$tc$$ || [] ) ),
iname = '___$to$' + tc.length; iname = '___$to$' + T.__acls.__cid;
// the trait object array will contain two values: the destination field // the trait object array will contain two values: the destination field
// and the trait to instantiate // and the trait to instantiate
@ -307,7 +307,11 @@ function addTraitInst( T, dfn )
// create internal trait ctor if not available // create internal trait ctor if not available
if ( dfn.___$$tctor$$ === undefined ) if ( dfn.___$$tctor$$ === undefined )
{ {
dfn.___$$tctor$$ = tctor; // TODO: let's check for inheritance or something to avoid this weak
// definition (this prevents warnings if there is not a supertype
// that defines the trait ctor)
dfn[ 'weak virtual ___$$tctor$$' ] = function() {};
dfn[ 'virtual override ___$$tctor$$' ] = createTctor( tc );
} }
return iname; return iname;
@ -327,11 +331,10 @@ function addTraitInst( T, dfn )
* *
* @return {undefined} * @return {undefined}
*/ */
function tctor() function tctor( tc )
{ {
// instantiate all traits and assign the object to their // instantiate all traits and assign the object to their
// respective fields // respective fields
var tc = this.___$$tc$$;
for ( var t in tc ) for ( var t in tc )
{ {
var f = tc[ t ][ 0 ], var f = tc[ t ][ 0 ],
@ -345,7 +348,29 @@ function tctor()
// the intimate relationship // the intimate relationship
this[ f ] = C( this.___$$vis$$ ).___$$vis$$; this[ f ] = C( this.___$$vis$$ ).___$$vis$$;
} }
// if we are a subtype, be sure to initialize our parent's traits
this.__super && this.__super();
}; };
/**
* Create trait constructor
*
* This binds the generic trait constructor to a reference to the provided
* trait class list.
*
* @param {Object} tc trait class list
*
* @return {function()} trait constructor
*/
function createTctor( tc )
{
return function()
{
return tctor.call( this, tc );
};
}
module.exports = Trait; module.exports = Trait;

View File

@ -71,4 +71,69 @@ require( 'common' ).testCase(
// o's supertype mixes in T // o's supertype mixes in T
this.assertOk( this.Class.isA( T, o ) ); this.assertOk( this.Class.isA( T, o ) );
}, },
/**
* Subtyping should impose no limits on mixins (except for the obvious
* API compatibility restrictions inherent in OOP).
*/
'Subtype can mix in additional traits': function()
{
var a = false,
b = false;
var Ta = this.Sut(
{
'public ta': function() { a = true; },
} ),
Tb = this.Sut(
{
'public tb': function() { b = true; },
} ),
C = null;
var _self = this;
this.assertDoesNotThrow( function()
{
var sup = _self.Class.use( Ta ).extend( {} );
// mixes in Tb; supertype already mixed in Ta
C = _self.Class.use( Tb ).extend( sup, {} );
} );
this.assertDoesNotThrow( function()
{
// ensures that instantiation does not throw an error and that
// the methods both exist
var o = C();
o.ta();
o.tb();
} );
// ensure both were properly called
this.assertOk( a );
this.assertOk( b );
},
/**
* As a sanity check, ensure that subtyping does not override parent
* type data with respect to traits.
*
* Note that this test makes the preceding test redundant, but the
* separation is useful for debugging any potential regressions.
*/
'Subtype trait types do not overwrite supertype types': function()
{
var Ta = this.Sut( {} ),
Tb = this.Sut( {} ),
C = this.Class.use( Ta ).extend( {} ),
o = this.Class.use( Tb ).extend( C, {} )();
// o's supertype mixes in Ta
this.assertOk( this.Class.isA( Ta, o ) );
// o mixes in Tb
this.assertOk( this.Class.isA( Tb, o ) );
},
} ); } );