Subtype mixin support
parent
451ec48a5c
commit
999c10c3bf
33
lib/Trait.js
33
lib/Trait.js
|
@ -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;
|
||||||
|
|
|
@ -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 ) );
|
||||||
|
},
|
||||||
} );
|
} );
|
||||||
|
|
Loading…
Reference in New Issue