1
0
Fork 0

Public static properties references are now shared with subtypes

closure/master
Mike Gerwitz 2011-04-10 22:32:46 -04:00
parent aead20290c
commit 4a90b7b809
2 changed files with 57 additions and 31 deletions

View File

@ -160,7 +160,10 @@ exports.build = function extend()
prop_init = member_builder.initMembers(), prop_init = member_builder.initMembers(),
members = member_builder.initMembers( prototype ), members = member_builder.initMembers( prototype ),
static_members = member_builder.initMembers(), static_members = {
methods: member_builder.initMembers(),
props: member_builder.initMembers(),
}
abstract_methods = abstract_methods =
util.clone( exports.getMeta( base ).abstractMethods ) util.clone( exports.getMeta( base ).abstractMethods )
@ -204,11 +207,11 @@ exports.build = function extend()
var new_class = createCtor( cname, abstract_methods, members ); var new_class = createCtor( cname, abstract_methods, members );
// closure to hold static initialization to be used later by subtypes // closure to hold static initialization to be used later by subtypes
var staticInit = function( ctor ) var staticInit = function( ctor, inheriting )
{ {
attachStatic( ctor, static_members, base ); attachStatic( ctor, static_members, base, inheriting );
} }
staticInit( new_class ); staticInit( new_class, false );
attachPropInit( prototype, prop_init, members, class_id ); attachPropInit( prototype, prop_init, members, class_id );
@ -394,7 +397,11 @@ function buildMembers(
) )
{ {
var hasOwn = Array.prototype.hasOwnProperty, var hasOwn = Array.prototype.hasOwnProperty,
defs = {}; defs = {},
smethods = static_members.methods,
sprops = static_members.props
;
util.propParse( props, { util.propParse( props, {
each: function( name, value, keywords ) each: function( name, value, keywords )
@ -424,7 +431,7 @@ function buildMembers(
property: function( name, value, keywords ) property: function( name, value, keywords )
{ {
var dest = ( keywords[ 'static' ] ) ? static_members : prop_init; var dest = ( keywords[ 'static' ] ) ? sprops : prop_init;
// build a new property, passing in the other members to compare // build a new property, passing in the other members to compare
// against for preventing nonsensical overrides // against for preventing nonsensical overrides
@ -435,7 +442,7 @@ function buildMembers(
getter: function( name, value, keywords ) getter: function( name, value, keywords )
{ {
var dest = ( keywords[ 'static' ] ) ? static_members : members; var dest = ( keywords[ 'static' ] ) ? smethods : members;
member_builder.buildGetter( member_builder.buildGetter(
dest, null, name, value, keywords dest, null, name, value, keywords
@ -444,7 +451,7 @@ function buildMembers(
setter: function( name, value, keywords ) setter: function( name, value, keywords )
{ {
var dest = ( keywords[ 'static' ] ) ? static_members : members; var dest = ( keywords[ 'static' ] ) ? smethods : members;
member_builder.buildSetter( member_builder.buildSetter(
dest, null, name, value, keywords dest, null, name, value, keywords
@ -453,7 +460,7 @@ function buildMembers(
method: function( name, func, is_abstract, keywords ) method: function( name, func, is_abstract, keywords )
{ {
var dest = ( keywords[ 'static' ] ) ? static_members : members; var dest = ( keywords[ 'static' ] ) ? smethods : members;
// constructor check // constructor check
if ( public_methods[ name ] === true ) if ( public_methods[ name ] === true )
@ -560,24 +567,47 @@ function attachPropInit( prototype, properties, members, cid )
/** /**
* Attaches static members to a constructor (class) * Attaches static members to a constructor (class)
* *
* Static methods will be assigned to the constructor itself. Properties, on the
* other hand, will be assigned to ctor.$. The reason for this is because JS
* engines pre-ES5 support no means of sharing references to primitives. Static
* properties of subtypes should share references to the static properties of
* their parents.
*
* @param {function()} ctor class * @param {function()} ctor class
* @param {Object} members static members * @param {Object} members static members
* @param {function()} base base class inheriting from
* @param {boolean} inheriting true if inheriting static members,
* otherwise false (setting own static
* members)
* *
* @return {undefined} * @return {undefined}
*/ */
function attachStatic( ctor, members, base ) function attachStatic( ctor, members, base, inheriting )
{ {
// "inherit" the parent's static members by running the parent's static var methods = members.methods,
props = members.props;
// "inherit" the parent's static methods by running the parent's static
// initialization method // initialization method
var baseinit = base.___$$sinit$$; var baseinit = base.___$$sinit$$;
if ( baseinit ) if ( baseinit )
{ {
baseinit( ctor ); baseinit( ctor, true );
}
// initialize static property if not yet defined
if ( !inheriting )
{
// "inherit" properties from the supertype, if available
ctor.$ = base.$ || {};
// add our own properties
util.copyTo( ctor.$, props[ 'public' ], true );
} }
// copy over public static members (deep copy; we don't want subtypes to // copy over public static members (deep copy; we don't want subtypes to
// share references with their parents) // share references with their parents)
util.copyTo( ctor, members[ 'public' ], true ); util.copyTo( ctor, methods[ 'public' ], true );
} }

View File

@ -79,13 +79,13 @@ var common = require( './common' ),
} ); } );
// properties should be accessible via class definition // properties should be accessible via class definition
assert.equal( Foo.foo, val, assert.equal( Foo.$.foo, val,
"Public static properties should be accessible via class definition" "Public static properties should be accessible via class definition"
); );
// as long as the above test succeeded, we can then conclude that static // as long as the above test succeeded, we can then conclude that static
// members are public by default if the following succeeds // members are public by default if the following succeeds
assert.equal( Foo.bar, val2, assert.equal( Foo.$.bar, val2,
"Static properties are public by default" "Static properties are public by default"
); );
@ -240,10 +240,10 @@ var common = require( './common' ),
; ;
// properties // properties
assert.equal( SubFoo.foo, Foo.foo, assert.equal( SubFoo.$.foo, Foo.$.foo,
"Public static properties are inherited by subtypes" "Public static properties are inherited by subtypes"
); );
assert.equal( SubSubFoo.foo, Foo.foo, assert.equal( SubSubFoo.$.foo, Foo.$.foo,
"Public static properties are inherited by sub-subtypes" "Public static properties are inherited by sub-subtypes"
); );
@ -256,7 +256,7 @@ var common = require( './common' ),
); );
// merge // merge
assert.equal( SubFoo.baz, baz, assert.equal( SubFoo.$.baz, baz,
"Subtypes contain both inherited static members as well as their own" "Subtypes contain both inherited static members as well as their own"
); );
@ -288,10 +288,11 @@ var common = require( './common' ),
/** /**
* Each class should have its own set of static values. That is, a subtype * Static references should be inherited by subtypes. That is, modifying a
* should not share references with its parent. * static property of a supertype should modify the same static property of the
* subtype, so long as the subtype has not defined a property of the same name.
*/ */
( function testInheritedPublicStaticPropertiesAreClones() ( function testPublicStaticPropertyReferencesAreInheritedBySubtypes()
{ {
var val = [ 1, 2, 3 ], var val = [ 1, 2, 3 ],
Foo = builder.build( Foo = builder.build(
@ -301,14 +302,9 @@ var common = require( './common' ),
SubFoo = builder.build( Foo, {} ) SubFoo = builder.build( Foo, {} )
; ;
// the values should certainly be equal... // the properties should reference the same object
assert.deepEqual( SubFoo.bar, Foo.bar, assert.ok( SubFoo.$.bar === Foo.$.bar,
"Inherited static properties should be equal by value" "Inherited static properties should share references"
);
// ...but they should not be the same object
assert.ok( SubFoo.bar !== Foo.bar,
"Inherited static propertis should not be the same object"
); );
} )(); } )();