1
0
Fork 0

Began implementing virtual trait methods

These require special treatment with proxying.
perfodd
Mike Gerwitz 2014-02-01 23:15:40 -05:00
parent 40e287bfc3
commit b7a314753a
6 changed files with 126 additions and 13 deletions

View File

@ -135,7 +135,7 @@ exports.buildMethod = function(
); );
// we might be overriding an existing method // we might be overriding an existing method
if ( keywords[ 'proxy' ] ) if ( keywords[ 'proxy' ] && !( prev && keywords.weak ) )
{ {
// TODO: Note that this is not compatible with method hiding, due to its // TODO: Note that this is not compatible with method hiding, due to its
// positioning (see hideMethod() below); address once method hiding is // positioning (see hideMethod() below); address once method hiding is

View File

@ -231,20 +231,32 @@ exports.prototype.validateMethod = function(
); );
} }
var lenprev = prev,
lennow = value; var lenprev = ( prev.__length === undefined )
? prev.length
: prev.__length;
var lennow = ( value.__length === undefined )
? value.length
: value.__length;
if ( keywords[ 'proxy' ] )
{
// otherwise we'd be checking against the length of a string.
lennow = NaN;
}
if ( keywords.weak && !( prev_keywords[ 'abstract' ] ) ) if ( keywords.weak && !( prev_keywords[ 'abstract' ] ) )
{ {
// weak abstract declaration found after its concrete // weak abstract declaration found after its concrete
// definition; check in reverse order // definition; check in reverse order
lenprev = value; var tmp = lenprev;
lennow = prev; lenprev = lennow;
lennow = tmp;
} }
// ensure parameter list is at least the length of its supertype // ensure parameter list is at least the length of its supertype
if ( ( lennow.__length || lennow.length ) if ( lennow < lenprev )
< ( lenprev.__length || lenprev.length )
)
{ {
throw TypeError( throw TypeError(
"Declaration of method '" + name + "' must be compatible " + "Declaration of method '" + name + "' must be compatible " +

View File

@ -127,7 +127,7 @@ exports.standard = {
// 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
// just power through it; see test case for more info) // just power through it; see test case for more info)
ret.__length = Infinity; ret.__length = NaN;
return ret; return ret;
}, },
}; };

View File

@ -189,7 +189,8 @@ function mixMethods( src, dest, vis, iname )
continue; continue;
} }
var keywords = src[ f ].___$$keywords$$; var keywords = src[ f ].___$$keywords$$,
vis = keywords['protected'] ? 'protected' : 'public';
// if abstract, then we are expected to provide the implementation; // if abstract, then we are expected to provide the implementation;
// otherwise, we proxy to the trait's implementation // otherwise, we proxy to the trait's implementation
@ -199,12 +200,12 @@ function mixMethods( src, dest, vis, iname )
// param names, since that is not [yet] important); the // param names, since that is not [yet] important); the
// visibility modified is important to prevent de-escalation // visibility modified is important to prevent de-escalation
// errors on override // errors on override
var vis = keywords['protected'] ? 'protected' : 'public';
dest[ vis + ' weak abstract ' + f ] = src[ f ].definition; dest[ vis + ' weak abstract ' + f ] = src[ f ].definition;
} }
else else
{ {
var pname = vis + ' proxy ' + f; var virt = keywords['virtual'] ? 'weak virtual ' : '',
pname = virt + vis + ' proxy ' + f;
// if we have already set up a proxy for a field of this name, // if we have already set up a proxy for a field of this name,
// then multiple traits have defined the same concrete member // then multiple traits have defined the same concrete member

View File

@ -409,7 +409,7 @@ require( 'common' ).testCase(
{}, null, 0, function() {}, '', {} {}, null, 0, function() {}, '', {}
); );
this.assertEqual( f.__length, Infinity ); this.assertOk( !( 0 < f.__length ) );
}, },
} ); } );

View File

@ -0,0 +1,100 @@
/**
* Tests virtual trait methods
*
* Copyright (C) 2014 Mike Gerwitz
*
* This file is part of GNU ease.js.
*
* ease.js is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
require( 'common' ).testCase(
{
caseSetUp: function()
{
this.Sut = this.require( 'Trait' );
this.Class = this.require( 'class' );
},
/**
* If a trait specifies a virtual method, then the class should expose
* the method as virtual.
*/
'Class inherits virtual trait method': function()
{
var expected = 'foobar';
var T = this.Sut(
{
'virtual foo': function()
{
return expected;
}
} );
var C = this.Class.use( T ).extend( {} );
// ensure that we are actually using the method
this.assertEqual( C().foo(), expected );
// if virtual, we should be able to override it
var expected2 = 'foobaz',
C2;
this.assertDoesNotThrow( function()
{
C2 = C.extend(
{
'override foo': function()
{
return expected2;
}
} );
} );
this.assertEqual( C2().foo(), expected2 );
},
/**
* Virtual trait methods should be treated in a manner similar to
* abstract trait methods---a class should be able to provide its own
* concrete implementation. Note that this differs from the above test
* because we are overriding the method internally at definition time,
* not subclassing.
*/
'Class can override virtual trait method': function()
{
var _self = this;
var T = this.Sut(
{
'virtual foo': function()
{
// we should never execute this (unless we're broken)
_self.fail( true, false,
"Method was not overridden."
);
}
} );
var expected = 'foobar';
var C = this.Class.use( T ).extend(
{
'override foo': function() { return expected; }
} );
this.assertEqual( C().foo(), expected );
},
} );