diff --git a/lib/MemberBuilder.js b/lib/MemberBuilder.js index 6d80a71..07c8147 100644 --- a/lib/MemberBuilder.js +++ b/lib/MemberBuilder.js @@ -135,7 +135,7 @@ exports.buildMethod = function( ); // 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 // positioning (see hideMethod() below); address once method hiding is diff --git a/lib/MemberBuilderValidator.js b/lib/MemberBuilderValidator.js index c9c2f21..3beec53 100644 --- a/lib/MemberBuilderValidator.js +++ b/lib/MemberBuilderValidator.js @@ -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' ] ) ) { // weak abstract declaration found after its concrete // definition; check in reverse order - lenprev = value; - lennow = prev; + var tmp = lenprev; + lenprev = lennow; + lennow = tmp; } // ensure parameter list is at least the length of its supertype - if ( ( lennow.__length || lennow.length ) - < ( lenprev.__length || lenprev.length ) - ) + if ( lennow < lenprev ) { throw TypeError( "Declaration of method '" + name + "' must be compatible " + diff --git a/lib/MethodWrappers.js b/lib/MethodWrappers.js index 7677d13..086d2ad 100644 --- a/lib/MethodWrappers.js +++ b/lib/MethodWrappers.js @@ -127,7 +127,7 @@ exports.standard = { // implementations of abstract methods with param requirements (we // 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) - ret.__length = Infinity; + ret.__length = NaN; return ret; }, }; diff --git a/lib/Trait.js b/lib/Trait.js index eb48ad7..0ccf47c 100644 --- a/lib/Trait.js +++ b/lib/Trait.js @@ -189,7 +189,8 @@ function mixMethods( src, dest, vis, iname ) 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; // 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 // visibility modified is important to prevent de-escalation // errors on override - var vis = keywords['protected'] ? 'protected' : 'public'; dest[ vis + ' weak abstract ' + f ] = src[ f ].definition; } 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, // then multiple traits have defined the same concrete member diff --git a/test/MethodWrappersTest.js b/test/MethodWrappersTest.js index 28fc6ec..890a330 100644 --- a/test/MethodWrappersTest.js +++ b/test/MethodWrappersTest.js @@ -409,7 +409,7 @@ require( 'common' ).testCase( {}, null, 0, function() {}, '', {} ); - this.assertEqual( f.__length, Infinity ); + this.assertOk( !( 0 < f.__length ) ); }, } ); diff --git a/test/Trait/VirtualTest.js b/test/Trait/VirtualTest.js new file mode 100644 index 0000000..6d61cd2 --- /dev/null +++ b/test/Trait/VirtualTest.js @@ -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 . + */ + +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 ); + }, +} );