Merge branch 'master' into doc/master
commit
8175f60cf4
97
lib/class.js
97
lib/class.js
|
@ -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 ]
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue