1
0
Fork 0

Preparing to remove abstractMethods from public prototype; now uses hash for performance since it no longer needs to be referenced cleanly externally

- It will be later referenced via reflection
closure/master
Mike Gerwitz 2011-01-25 00:13:47 -05:00
parent 069ef6717f
commit 881edc0cc6
3 changed files with 21 additions and 77 deletions

View File

@ -217,16 +217,10 @@ var extend = ( function( extending )
members = member_builder.initMembers( members = member_builder.initMembers(
prototype, prototype, prototype prototype, prototype, prototype
), ),
abstract_methods = ( base.abstractMethods || [] ).slice();
// it's much faster to lookup a hash than it is to iterate through an abstract_methods = util.clone( base.abstractMethods )
// entire array each time we need to find an existing abstract method || { __length: 0 }
var abstract_map = {}; ;
for ( var i = 0, len = abstract_methods.length; i < len; i++ )
{
var method = abstract_methods[ i ];
abstract_map[ method ] = i;
}
util.propParse( props, { util.propParse( props, {
each: function( name, value, keywords ) each: function( name, value, keywords )
@ -269,15 +263,17 @@ var extend = ( function( extending )
if ( is_abstract ) if ( is_abstract )
{ {
abstract_methods.push( name ); abstract_methods[ name ] = true;
abstract_methods.__length++;
} }
else if ( ( abstract_map[ name ] !== undefined ) else if ( ( abstract_methods[ name ] )
&& ( is_abstract === false ) && ( is_abstract === false )
) )
{ {
// if this was a concrete method, then it should no longer // if this was a concrete method, then it should no longer
// be marked as abstract // be marked as abstract
delete abstract_methods[ abstract_map[ name ] ]; delete abstract_methods[ name ];
abstract_methods.__length--;
} }
}, },
@ -289,8 +285,6 @@ var extend = ( function( extending )
}, },
} ); } );
abstract_methods = util.arrayShrink( abstract_methods );
// reference to the parent prototype (for more experienced users) // reference to the parent prototype (for more experienced users)
prototype.parent = base.prototype; prototype.parent = base.prototype;
@ -332,7 +326,7 @@ var extend = ( function( extending )
function createCtor( abstract_methods ) function createCtor( abstract_methods )
{ {
// concrete class // concrete class
if ( abstract_methods.length === 0 ) if ( abstract_methods.__length === 0 )
{ {
var args = null; var args = null;
@ -507,7 +501,7 @@ function attachPropInit( prototype, properties )
*/ */
function attachAbstract( func, methods ) function attachAbstract( func, methods )
{ {
var is_abstract = ( methods.length > 0 ) ? true: false; var is_abstract = ( methods.__length > 0 ) ? true: false;
/** /**
* Returns whether the class contains abstract methods (and is therefore * Returns whether the class contains abstract methods (and is therefore

View File

@ -72,11 +72,6 @@ assert.ok(
"All classes should have an isAbstract() method" "All classes should have an isAbstract() method"
); );
assert.ok(
( Foo.abstractMethods instanceof Array ),
"All classes should provide a list of their abstract methods as an array"
);
assert.equal( assert.equal(
Foo.isAbstract(), Foo.isAbstract(),
false, false,
@ -96,28 +91,6 @@ assert.equal(
"concrete implementation for all abstract methods" "concrete implementation for all abstract methods"
); );
assert.ok(
( ( AbstractFoo.abstractMethods[ 0 ] == 'method' )
&& ( AbstractFoo.abstractMethods[ 1 ] == 'second' )
),
"Abstract classes should provide a list of their abstract methods' names"
);
assert.ok(
( ( SubAbstractFoo.abstractMethods[ 0 ] == 'method' )
&& ( SubAbstractFoo.abstractMethods[ 1 ] == undefined )
),
"Abstract subclasses should not have their concrete methods within the " +
"the abstract method list if the concrete method was abstract in " +
"its supertype"
);
assert.equal(
ConcreteFoo.abstractMethods.length,
0,
"Concrete classes should not have any abstract methods listed"
);
assert.equal( assert.equal(
ConcreteFoo.isAbstract(), ConcreteFoo.isAbstract(),
false, false,

View File

@ -112,6 +112,11 @@ var Type = Interface.extend( {
} )(); } )();
/**
* Since interfaces can contain only abstract methods, it stands to reason that
* any class implementing an interface without providing any concrete methods
* should be abstract by default.
*/
( function testClassesImplementingInterfacesAreConsideredAbstractByDefault() ( function testClassesImplementingInterfacesAreConsideredAbstractByDefault()
{ {
assert.equal( assert.equal(
@ -123,40 +128,6 @@ var Type = Interface.extend( {
} ) (); } ) ();
( function testAbstractMethodListUpdatedWhenInterfaceImplemented()
{
// no base
assert.equal(
Foo.abstractMethods.length,
2,
"Abstract methods list is updated when interface is implemented " +
"(empty base)"
);
// PlainFoo base
assert.equal(
PlainFoo2.abstractMethods.length,
2,
"Abstract methods list is updated when interface is implemented " +
"(concrete base)"
);
} )();
( function testProperAbstractMethodsAreCopiedFromInterface()
{
assert.ok(
( ( Foo.abstractMethods[ 0 ] == 'foo' )
&& ( Foo.abstractMethods[ 1 ] == 'foo2' )
)
&& ( ( PlainFoo2.abstractMethods[ 0 ] == 'foo' )
&& ( PlainFoo2.abstractMethods[ 1 ] == 'foo2' )
),
"Abstract methods list contains names of implemented methods"
);
} )();
( function testInstancesOfClassesAreInstancesOfTheirImplementedInterfaces() ( function testInstancesOfClassesAreInstancesOfTheirImplementedInterfaces()
{ {
// concrete implementation so that we can instantiate it // concrete implementation so that we can instantiate it
@ -175,5 +146,11 @@ var Type = Interface.extend( {
"Instances of classes implementing interfaces are considered to be " + "Instances of classes implementing interfaces are considered to be " +
"instances of the implemented interfaces" "instances of the implemented interfaces"
); );
assert.equal(
ConcreteFoo.isAbstract(),
false,
"Concrete implementations are not considered to be abstract"
);
} )(); } )();