1
0
Fork 0

Trait virtual method proxies now set __length metadata

This fixes a bug that causesd virtual definitions with parameters on classes
that a trait is mixed into to fail, and prevented proper param length
validations in the reverse case.

See test case description for a less confusing description.
protolib
Mike Gerwitz 2014-05-02 00:17:53 -04:00
parent 21821cd9e7
commit 8c95932446
2 changed files with 58 additions and 3 deletions

View File

@ -398,11 +398,13 @@ function createVirtProxy( acls, dfn )
? 'public' ? 'public'
: 'protected'; : 'protected';
var plen = acls.___$$methods$$[ vis ][ f ].__length;
// this is the aforementioned proxy method; see the docblock for // this is the aforementioned proxy method; see the docblock for
// more information // more information
dfn[ vis + ' virtual override ' + f ] = ( function() dfn[ vis + ' virtual override ' + f ] = ( function()
{ {
return function() var retf = function()
{ {
var pmo = this.___$$pmo$$, var pmo = this.___$$pmo$$,
o = pmo[ f ]; o = pmo[ f ];
@ -413,16 +415,22 @@ function createVirtProxy( acls, dfn )
? o.apply( pmo, arguments ) ? o.apply( pmo, arguments )
: this.__super.apply( this, arguments ); : this.__super.apply( this, arguments );
}; };
retf.__length = plen;
return retf;
} )( f ); } )( f );
// this guy bypasses the above virtual override check, which is // this guy bypasses the above virtual override check, which is
// necessary in certain cases to prevent infinte recursion // necessary in certain cases to prevent infinte recursion
dfn[ vis + ' virtual __$$' + f ] = ( function( f ) dfn[ vis + ' virtual __$$' + f ] = ( function( f )
{ {
return function() var retf = function()
{ {
return this.___$$parent$$[ f ].apply( this, arguments ); return this.___$$parent$$[ f ].apply( this, arguments );
}; };
retf.__length = plen;
return retf;
} )( f ); } )( f );
} }
} }
@ -581,7 +589,7 @@ function mixMethods( src, dest, vis, iname )
// beacuse we are not proxying to a method of the same name) // beacuse we are not proxying to a method of the same name)
dest[ pname ] = ( function( f ) dest[ pname ] = ( function( f )
{ {
return function() var retf = function()
{ {
var pdest = this[ iname ]; var pdest = this[ iname ];
@ -596,6 +604,9 @@ function mixMethods( src, dest, vis, iname )
? this ? this
: ret; : ret;
}; };
retf.__length = src[ f ].__length;
return retf;
} )( f ); } )( f );
} }
} }

View File

@ -190,6 +190,50 @@ require( 'common' ).testCase(
}, },
/**
* Virtual methods for traits are handled via a series of proxy methods
* that determine, at runtime (as opposed to when the class is created),
* where the call should go. (At least that was the implementation at
* the time this test was written.) This test relies on the proper
* parameter metadata being set on those proxy methods so that the
* necessary length requirements can be validated.
*
* This was a bug in the initial implemenation: the above tests did not
* catch it because the virtual methods had no arguments. The initial
* problem was that, since __length was not defined on the generated
* method that was recognized as the override, it was always zero, which
* always failed if there were any arguments on the virtual method. The
* reverse case was also a problem, but it didn't manifest as an
* error---rather, it did *not* error when it should have.
*
* Note the instantiation in these cases: this is because the trait
* implementation lazily performs the mixin on first use.
*/
'Subtype must meet compatibility requirements of virtual trait method':
function()
{
var _self = this;
var C = this.Class.use(
this.Sut( { 'virtual foo': function( a, b ) {} } )
);
this.assertThrows( function()
{
// does not meet param requirements (note the
// instantiation---traits defer processing until they are used)
C.extend( { 'override foo': function( a ) {} } )();
} );
this.assertDoesNotThrow( function()
{
// does not meet param requirements (note the
// instantiation---traits defer processing until they are used)
C.extend( { 'override foo': function( a, b ) {} } )();
} );
},
/** /**
* This is the same concept as the non-virtual test found in the * This is the same concept as the non-virtual test found in the
* DefinitionTest case: since a trait is mixed into a class, if it * DefinitionTest case: since a trait is mixed into a class, if it