diff --git a/lib/class_builder.js b/lib/class_builder.js index 8af2a73..13ead39 100644 --- a/lib/class_builder.js +++ b/lib/class_builder.js @@ -160,7 +160,10 @@ exports.build = function extend() prop_init = member_builder.initMembers(), members = member_builder.initMembers( prototype ), - static_members = member_builder.initMembers(), + static_members = { + methods: member_builder.initMembers(), + props: member_builder.initMembers(), + } abstract_methods = util.clone( exports.getMeta( base ).abstractMethods ) @@ -204,11 +207,11 @@ exports.build = function extend() var new_class = createCtor( cname, abstract_methods, members ); // 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 ); @@ -394,7 +397,11 @@ function buildMembers( ) { var hasOwn = Array.prototype.hasOwnProperty, - defs = {}; + defs = {}, + + smethods = static_members.methods, + sprops = static_members.props + ; util.propParse( props, { each: function( name, value, keywords ) @@ -424,7 +431,7 @@ function buildMembers( 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 // against for preventing nonsensical overrides @@ -435,7 +442,7 @@ function buildMembers( getter: function( name, value, keywords ) { - var dest = ( keywords[ 'static' ] ) ? static_members : members; + var dest = ( keywords[ 'static' ] ) ? smethods : members; member_builder.buildGetter( dest, null, name, value, keywords @@ -444,7 +451,7 @@ function buildMembers( setter: function( name, value, keywords ) { - var dest = ( keywords[ 'static' ] ) ? static_members : members; + var dest = ( keywords[ 'static' ] ) ? smethods : members; member_builder.buildSetter( dest, null, name, value, keywords @@ -453,7 +460,7 @@ function buildMembers( method: function( name, func, is_abstract, keywords ) { - var dest = ( keywords[ 'static' ] ) ? static_members : members; + var dest = ( keywords[ 'static' ] ) ? smethods : members; // constructor check if ( public_methods[ name ] === true ) @@ -560,24 +567,47 @@ function attachPropInit( prototype, properties, members, cid ) /** * Attaches static members to a constructor (class) * - * @param {function()} ctor class - * @param {Object} members static members + * 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 {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} */ -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 var baseinit = base.___$$sinit$$; 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 // share references with their parents) - util.copyTo( ctor, members[ 'public' ], true ); + util.copyTo( ctor, methods[ 'public' ], true ); } diff --git a/test/test-class_builder-static.js b/test/test-class_builder-static.js index 03f44d7..5f1e69c 100644 --- a/test/test-class_builder-static.js +++ b/test/test-class_builder-static.js @@ -79,13 +79,13 @@ var common = require( './common' ), } ); // 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" ); // as long as the above test succeeded, we can then conclude that static // 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" ); @@ -240,10 +240,10 @@ var common = require( './common' ), ; // properties - assert.equal( SubFoo.foo, Foo.foo, + assert.equal( SubFoo.$.foo, Foo.$.foo, "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" ); @@ -256,7 +256,7 @@ var common = require( './common' ), ); // merge - assert.equal( SubFoo.baz, baz, + assert.equal( SubFoo.$.baz, baz, "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 - * should not share references with its parent. + * Static references should be inherited by subtypes. That is, modifying a + * 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 ], Foo = builder.build( @@ -301,14 +302,9 @@ var common = require( './common' ), SubFoo = builder.build( Foo, {} ) ; - // the values should certainly be equal... - assert.deepEqual( SubFoo.bar, Foo.bar, - "Inherited static properties should be equal by value" - ); - - // ...but they should not be the same object - assert.ok( SubFoo.bar !== Foo.bar, - "Inherited static propertis should not be the same object" + // the properties should reference the same object + assert.ok( SubFoo.$.bar === Foo.$.bar, + "Inherited static properties should share references" ); } )();