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
parent
21821cd9e7
commit
8c95932446
17
lib/Trait.js
17
lib/Trait.js
|
@ -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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue