1
0
Fork 0

Merge branch 'master' into doc/master

closure/master
Mike Gerwitz 2011-03-15 00:31:45 -04:00
commit 8175f60cf4
2 changed files with 45 additions and 74 deletions

View File

@ -27,37 +27,6 @@ var util = require( './util' ),
propobj = require( './propobj' ) propobj = require( './propobj' )
; ;
/**
* Stores class metadata internally (ensures data is encapsulated)
*
* The data in this object is hashed a class id.
*
* @type {Object.<number, { implemented: Array.<string> }>}
*/
var class_meta = {};
/**
* Stores class instance visibility object
*
* An entry in this table exists for each instance, with the instance id (iid)
* as the key. For each instance, there is always a base. The base will contain
* a proxy to the public members on the instance itself. The base will also
* contain all protected members.
*
* Atop the base object is a private member object, with the base as its
* prototype. There exists a private member object for the instance itself and
* one for each supertype. This is stored by the class id (cid) as the key. This
* permits the private member object associated with the class of the method
* call to be bound to that method. For example, if a parent method is called,
* that call must be invoked in the context of the parent, so the private
* members of the parent must be made available.
*
* The resulting structure looks something like this:
* class_instance = { iid: { cid: {} } }
*
* @type {Object.<number, Object<number, Object>>}
*/
var class_instance = {};
/* /*
* IE contains a nasty enumeration "bug" (poor implementation) that makes * IE contains a nasty enumeration "bug" (poor implementation) that makes
@ -217,7 +186,7 @@ module.exports.isInstanceOf = function( type, instance )
// if no metadata is available, then our remaining checks cannot be // if no metadata is available, then our remaining checks cannot be
// performed // performed
if ( !instance.__cid || !( meta = getMeta( instance.__cid ) ) ) if ( !instance.__cid || !( meta = getMeta( instance ) ) )
{ {
return false; return false;
} }
@ -450,7 +419,7 @@ var extend = ( function( extending )
members = member_builder.initMembers( prototype ), members = member_builder.initMembers( prototype ),
abstract_methods = abstract_methods =
util.clone( getMeta( base.__cid ).abstractMethods ) util.clone( getMeta( base ).abstractMethods )
|| { __length: 0 } || { __length: 0 }
; ;
@ -543,7 +512,7 @@ var extend = ( function( extending )
} ); } );
// 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;
// set up the new class // set up the new class
var new_class = createCtor( cname, abstract_methods, members ); var new_class = createCtor( cname, abstract_methods, members );
@ -556,15 +525,15 @@ var extend = ( function( extending )
// important: call after setting prototype // important: call after setting prototype
setupProps( new_class, abstract_methods, class_id ); setupProps( new_class, abstract_methods, class_id );
// create internal metadata for the new class
var meta = createMeta( new_class, base );
meta.abstractMethods = abstract_methods;
meta.name = cname;
// lock down the new class (if supported) to ensure that we can't add // lock down the new class (if supported) to ensure that we can't add
// members at runtime // members at runtime
util.freeze( new_class ); util.freeze( new_class );
// create internal metadata for the new class
var meta = createMeta( new_class, base.prototype.__cid );
meta.abstractMethods = abstract_methods;
meta.name = cname;
// we're done with the extension process // we're done with the extension process
extending = false; extending = false;
@ -724,7 +693,7 @@ var implement = function()
// create a new class with the implemented abstract methods // create a new class with the implemented abstract methods
var class_new = module.exports.extend( base, dest ); var class_new = module.exports.extend( base, dest );
getMeta( class_new.__cid ).implemented = implemented; getMeta( class_new ).implemented = implemented;
return class_new; return class_new;
} }
@ -756,6 +725,21 @@ function setupProps( func, abstract_methods, class_id )
* This will be passed to all methods when invoked, permitting them to access * This will be passed to all methods when invoked, permitting them to access
* the private and protected members while keeping them encapsulated. * the private and protected members while keeping them encapsulated.
* *
* For each instance, there is always a base. The base will contain a proxy to
* the public members on the instance itself. The base will also contain all
* protected members.
*
* Atop the base object is a private member object, with the base as its
* prototype. There exists a private member object for the instance itself and
* one for each supertype. This is stored by the class id (cid) as the key. This
* permits the private member object associated with the class of the method
* call to be bound to that method. For example, if a parent method is called,
* that call must be invoked in the context of the parent, so the private
* members of the parent must be made available.
*
* The resulting structure looks something like this:
* class_instance = { iid: { cid: {} } }
*
* @param {number} iid instance id * @param {number} iid instance id
* @param {Object} instance instance to initialize * @param {Object} instance instance to initialize
* *
@ -767,7 +751,7 @@ function initInstance( iid, instance )
prot.prototype = instance; prot.prototype = instance;
// add the visibility objects to the data object for this class instance // add the visibility objects to the data object for this class instance
class_instance[ iid ] = new prot(); instance.___$$vis$$ = new prot();
} }
@ -801,7 +785,7 @@ function attachPropInit( prototype, properties, members, cid )
// first initialize the parent's properties, so that ours will overwrite // first initialize the parent's properties, so that ours will overwrite
// them // them
var parent_init = prototype.parent.__initProps; var parent_init = prototype.___$$parent$$.__initProps;
if ( parent_init instanceof Function ) if ( parent_init instanceof Function )
{ {
// call the parent prop_init, letting it know that it's been // call the parent prop_init, letting it know that it's been
@ -813,12 +797,12 @@ function attachPropInit( prototype, properties, members, cid )
// this will return our property proxy, if supported by our environment, // this will return our property proxy, if supported by our environment,
// otherwise just a normal object with everything merged in // otherwise just a normal object with everything merged in
var inst_props = propobj.createPropProxy( var inst_props = propobj.createPropProxy(
this, class_instance[ iid ], properties[ 'public' ] this, this.___$$vis$$, properties[ 'public' ]
); );
// if we're inheriting, perform a setup that doesn't include everything // if we're inheriting, perform a setup that doesn't include everything
// that we don't want (e.g. private properties) // that we don't want (e.g. private properties)
class_instance[ iid ][ cid ] = propobj.setup( this.___$$vis$$[ cid ] = propobj.setup(
inst_props, properties, members inst_props, properties, members
); );
}); });
@ -953,28 +937,33 @@ function attachInstanceOf( instance )
* Initializes class metadata for the given class * Initializes class metadata for the given class
* *
* @param {Class} func class to initialize metadata for * @param {Class} func class to initialize metadata for
* @param {Class} cparent class parent
* *
* @return {undefined} * @return {undefined}
*/ */
function createMeta( func, parent_id ) function createMeta( func, cparent )
{ {
var id = func.__cid, var id = func.__cid,
parent_meta = ( ( parent_id ) ? getMeta( parent_id ) : undefined ); parent_meta = ( ( cparent.__cid ) ? getMeta( cparent ) : undefined );
// copy the parent prototype's metadata if it exists (inherit metadata) // copy the parent prototype's metadata if it exists (inherit metadata)
if ( parent_meta ) if ( parent_meta )
{ {
class_meta[ id ] = util.clone( parent_meta, true ); func.___$$meta$$ = util.clone( parent_meta, true );
} }
else else
{ {
// create empty // create empty
class_meta[ id ] = { func.___$$meta$$ = {
implemented: [], implemented: [],
}; };
} }
return class_meta[ id ]; // store the metadata in the prototype as well (inconsiderable overhead;
// it's just a reference)
func.prototype.___$$meta$$ = func.___$$meta$$;
return func.___$$meta$$;
} }
@ -984,13 +973,13 @@ function createMeta( func, parent_id )
* Since a reference is returned (rather than a copy), the returned object can * Since a reference is returned (rather than a copy), the returned object can
* be modified to alter the metadata. * be modified to alter the metadata.
* *
* @param {number} id id of class to retrieve metadata for * @param {Class} cls class from which to retrieve metadata
* *
* @return {Object} * @return {Object}
*/ */
function getMeta( id ) function getMeta( cls )
{ {
return class_meta[ id ] || {}; return cls.___$$meta$$ || {};
} }
@ -1007,7 +996,7 @@ function getMeta( id )
* overridden by the subtype to continue to use the private members of the * overridden by the subtype to continue to use the private members of the
* supertype. * supertype.
* *
* @param {function()} method method to look up instance object for * @param {function()} inst instance that the method is being called from
* @param {number} cid class id * @param {number} cid class id
* *
* @return {Object,null} instance object if found, otherwise null * @return {Object,null} instance object if found, otherwise null
@ -1015,7 +1004,7 @@ function getMeta( id )
function getMethodInstance( inst, cid ) function getMethodInstance( inst, cid )
{ {
var iid = inst.__iid, var iid = inst.__iid,
data = class_instance[ iid ]; data = inst.___$$vis$$;
return ( iid && data ) return ( iid && data )
? data[ cid ] ? data[ cid ]

View File

@ -60,11 +60,6 @@ var common = require( './common' ),
{ {
return this.__super( arg ); return this.__super( arg );
}, },
callParentAlt: function()
{
return this.parent.myMethod2.apply( this, arguments );
},
}), }),
foo = new Foo(), foo = new Foo(),
@ -109,19 +104,6 @@ assert.equal(
"Arguments should be passed to super method via _super argument list" "Arguments should be passed to super method via _super argument list"
); );
assert.deepEqual(
SubFoo.prototype.parent,
Foo.prototype,
"Parent property should represent parent prototype"
);
sub_foo.callParentAlt( arg = 'moo' );
assert.equal(
method2Arg,
arg,
"The parent property may also be used to invoke parent methods"
);
assert.throws( function() assert.throws( function()
{ {
Foo.extend( Foo.extend(