1
0
Fork 0

Override keyword is now required to override a virtual method (#19)

closure/master
Mike Gerwitz 2011-08-04 00:32:10 -04:00
parent bd4e18acc6
commit 2569dacf15
7 changed files with 73 additions and 41 deletions

View File

@ -76,20 +76,31 @@ exports.buildMethod = function(
{ {
// TODO: We can improve performance by not scanning each one individually // TODO: We can improve performance by not scanning each one individually
// every time this method is called // every time this method is called
var prev_data = scanMembers( members, name, base ), var prev_data = scanMembers( members, name, base ),
prev = ( prev_data ) ? prev_data.member : null, prev = ( prev_data ) ? prev_data.member : null,
dest = getMemberVisibility( members, keywords ); prev_keywords = ( prev && prev.___$$keywords$$ ),
dest = getMemberVisibility( members, keywords );
; ;
// ensure that the declaration is valid (keywords make sense, argument // ensure that the declaration is valid (keywords make sense, argument
// length, etc) // length, etc)
validateMethod( keywords, prev_data, value, name ); validateMethod( keywords, prev_data, prev_keywords, value, name );
// we might be overriding an existing method // we might be overriding an existing method
if ( prev ) if ( prev )
{ {
// override the method // by default, perform method hiding, even if the keyword was not
dest[ name ] = overrideMethod( prev, value, instCallback, cid ); // provided (the keyword simply suppresses the warning)
var operation = hideMethod;
// TODO: warning if no super method when override keyword provided
if ( keywords[ 'override' ] || prev_keywords[ 'abstract' ] )
{
// override the method
operation = overrideMethod;
}
dest[ name ] = operation( prev, value, instCallback, cid );
} }
else if ( keywords[ 'abstract' ] ) else if ( keywords[ 'abstract' ] )
{ {
@ -114,17 +125,14 @@ exports.buildMethod = function(
* *
* @param {Object.<string,boolean>} keywords parsed keywords * @param {Object.<string,boolean>} keywords parsed keywords
* *
* @param {Object} prev_data data of member being overridden, if available * @param {Object} prev_data data of member being overridden
* @param {*} value property value * @param {Object} prev_keywords keywords of member being overridden
* @param {string} name property name * @param {*} value property value
* @param {string} name property name
*/ */
function validateMethod( keywords, prev_data, value, name ) function validateMethod( keywords, prev_data, prev_keywords, value, name )
{ {
var prev = ( prev_data ) ? prev_data.member : null, var prev = ( prev_data ) ? prev_data.member : null;
prev_keywords = ( prev && prev.___$$keywords$$ )
? prev.___$$keywords$$
: {}
;
if ( keywords[ 'abstract' ] ) if ( keywords[ 'abstract' ] )
{ {
@ -210,10 +218,13 @@ function validateMethod( keywords, prev_data, value, name )
// IMPORTANT: do this last, to ensure we throw errors before warnings // IMPORTANT: do this last, to ensure we throw errors before warnings
if ( !( keywords[ 'new' ] || keywords[ 'override' ] ) ) if ( !( keywords[ 'new' ] || keywords[ 'override' ] ) )
{ {
throw Warning( Error( if ( !( prev_keywords[ 'abstract' ] ) )
"Hiding method '" + name + "'; " + {
"use 'new' if intended, or 'override' to override instead" throw Warning( Error(
) ); "Hiding method '" + name + "'; " +
"use 'new' if intended, or 'override' to override instead"
) );
}
} }
} }
} }
@ -492,6 +503,17 @@ function scanMembers( members, name, base )
} }
/**
* Hide a method with a "new" method
*/
function hideMethod( super_method, new_method, instCallback, cid )
{
// TODO: This function is currently unimplemented. It exists at present to
// provide a placeholder and ensure that the override keyword is required to
// override a parent method.
}
/** /**
* Generates a method override function * Generates a method override function
* *

View File

@ -34,6 +34,7 @@ var _keywords = {
'abstract': true, 'abstract': true,
'const': true, 'const': true,
'virtual': true, 'virtual': true,
'override': true,
}; };

View File

@ -122,6 +122,8 @@ var common = require( './common' ),
ConcreteFoo = Class.extend( SubAbstractFoo, ConcreteFoo = Class.extend( SubAbstractFoo,
{ {
// we should NOT need the override keyword for concrete
// implementations of abstract super methods
'foo': function() {}, 'foo': function() {},
} ) } )
; ;

View File

@ -171,8 +171,8 @@ var Type = Interface.extend( {
// concrete implementation so that we can instantiate it // concrete implementation so that we can instantiate it
var ConcreteFoo = Foo.extend( var ConcreteFoo = Foo.extend(
{ {
foo: function() {}, 'override foo': function() {},
foo2: function() {}, 'override foo2': function() {},
}), }),
concrete_inst = ConcreteFoo() concrete_inst = ConcreteFoo()

View File

@ -51,12 +51,12 @@ var common = require( './common' ),
SubFoo = Foo.extend( SubFoo = Foo.extend(
{ {
myMethod: function() 'override myMethod': function()
{ {
return this; return this;
}, },
myMethod2: function( arg ) 'override myMethod2': function( arg )
{ {
return this.__super( arg ); return this.__super( arg );
}, },

View File

@ -110,7 +110,7 @@ var common = require( './common' ),
SubFoo = Foo.extend({ SubFoo = Foo.extend({
'private _pfoo': 'baz', 'private _pfoo': 'baz',
'public getSelfOverride': function() 'override public getSelfOverride': function()
{ {
// return this from overridden method // return this from overridden method
return this; return this;
@ -120,7 +120,7 @@ var common = require( './common' ),
/** /**
* We have to override this so that 'this' is not bound to the supertype * We have to override this so that 'this' is not bound to the supertype
*/ */
'public getProp': function( name ) 'override public getProp': function( name )
{ {
// return property, allowing us to break encapsulation for // return property, allowing us to break encapsulation for
// protected/private properties (for testing purposes) // protected/private properties (for testing purposes)
@ -519,7 +519,7 @@ var common = require( './common' ),
'virtual protected baz': function() {}, 'virtual protected baz': function() {},
} ).extend( { } ).extend( {
'public foo': 'bar', 'public foo': 'bar',
'public baz': function() {}, 'override public baz': function() {},
} ); } );
}, Error, "Can escalate visibility of subtype members" ); }, Error, "Can escalate visibility of subtype members" );
@ -532,7 +532,7 @@ var common = require( './common' ),
'virtual protected baz': function() {}, 'virtual protected baz': function() {},
} ).extend( { } ).extend( {
'protected foo': 'bar', 'protected foo': 'bar',
'protected baz': function() {}, 'override protected baz': function() {},
} ); } );
}, Error, "Can retain level of visibility for subtype members" ); }, Error, "Can retain level of visibility for subtype members" );
} )(); } )();
@ -628,7 +628,7 @@ var common = require( './common' ),
} ).extend( } ).extend(
{ {
// we override to public just so we can call it externally // we override to public just so we can call it externally
'public foo': function() 'override public foo': function()
{ {
return this.__super(); return this.__super();
}, },
@ -679,7 +679,7 @@ var common = require( './common' ),
} ).extend( } ).extend(
{ {
// override and escalate visibility of method foo() // override and escalate visibility of method foo()
'public foo': function() 'override public foo': function()
{ {
return true; return true;
}, },
@ -743,7 +743,7 @@ var common = require( './common' ),
} ).extend( } ).extend(
{ {
// provide concrete implementation // provide concrete implementation
'protected foo': function() 'override protected foo': function()
{ {
return val; return val;
}, },

View File

@ -88,7 +88,7 @@ mb_common.assertCommon();
// attempt to override it // attempt to override it
assert.doesNotThrow( function() assert.doesNotThrow( function()
{ {
mb_common.buildMemberQuick( {}, true ); mb_common.buildMemberQuick( { 'override': true }, true );
}, Error, "Should be able to override virtual methods" ); }, Error, "Should be able to override virtual methods" );
} )(); } )();
@ -106,7 +106,7 @@ mb_common.assertCommon();
mb_common.buildMemberQuick( { 'virtual': true } ); mb_common.buildMemberQuick( { 'virtual': true } );
// override it (non-virtual) // override it (non-virtual)
mb_common.buildMemberQuick( {}, true ); mb_common.buildMemberQuick( { 'override': true }, true );
// attempt to override again (should fail) // attempt to override again (should fail)
try try
@ -133,12 +133,12 @@ mb_common.assertCommon();
mb_common.buildMemberQuick( { 'virtual': true } ); mb_common.buildMemberQuick( { 'virtual': true } );
// override it (virtual) // override it (virtual)
mb_common.buildMemberQuick( { 'virtual': true }, true ); mb_common.buildMemberQuick( { 'virtual': true, 'override': true }, true );
// attempt to override again // attempt to override again
assert.doesNotThrow( function() assert.doesNotThrow( function()
{ {
mb_common.buildMemberQuick( {}, true ); mb_common.buildMemberQuick( { 'override': true }, true );
}, Error, "Can override an override if declared virtual" ); }, Error, "Can override an override if declared virtual" );
} )(); } )();
@ -156,7 +156,7 @@ mb_common.assertCommon();
// we should be able to override it // we should be able to override it
assert.doesNotThrow( function() assert.doesNotThrow( function()
{ {
mb_common.buildMemberQuick( {}, true ); mb_common.buildMemberQuick( { 'override': true }, true );
}, Error, "Can overrde abstract methods" ); }, Error, "Can overrde abstract methods" );
} )(); } )();
@ -200,19 +200,19 @@ mb_common.assertCommon();
assert.doesNotThrow( function() assert.doesNotThrow( function()
{ {
mb_common.buildMemberQuick( { 'virtual': true }, true ); mb_common.buildMemberQuick( { 'override': true }, true );
}, TypeError, "Method can have equal number of parameters" ); }, TypeError, "Method can have equal number of parameters" );
assert.doesNotThrow( function() assert.doesNotThrow( function()
{ {
mb_common.value = function( one, two, three ) {}; mb_common.value = function( one, two, three ) {};
mb_common.buildMemberQuick( { 'virtual': true }, true ); mb_common.buildMemberQuick( { 'override': true }, true );
}, TypeError, "Method can have greater number of parameters" ); }, TypeError, "Method can have greater number of parameters" );
assert.throws( function() assert.throws( function()
{ {
mb_common.value = function( one ) {}; mb_common.value = function( one ) {};
mb_common.buildMemberQuick( {}, true ); mb_common.buildMemberQuick( { 'override': true }, true );
}, TypeError, "Method cannot have lesser number of parameters" ); }, TypeError, "Method cannot have lesser number of parameters" );
} )(); } )();
@ -255,7 +255,7 @@ mb_common.assertCommon();
); );
}; };
mb_common.buildMemberQuick( {}, true ); mb_common.buildMemberQuick( { 'override': true }, true );
// invoke the method // invoke the method
mb_common.members[ 'public' ][ mb_common.name ](); mb_common.members[ 'public' ][ mb_common.name ]();
@ -298,7 +298,14 @@ mb_common.assertCommon();
members['public'].foo.___$$keywords$$ = { 'virtual': true }; members['public'].foo.___$$keywords$$ = { 'virtual': true };
// override // override
builder.buildMethod( members, {}, 'foo', newfunc, {}, instCallback ); builder.buildMethod(
members,
{},
'foo',
newfunc,
{ 'override': true },
instCallback
);
// call the overriding method // call the overriding method
members[ 'public' ].foo(); members[ 'public' ].foo();
@ -393,7 +400,7 @@ mb_common.assertCommon();
exports.meta, exports.meta,
'func', 'func',
func2, func2,
{}, { 'override': true },
instCallback instCallback
); );