1
0
Fork 0

Class.use now creates its own class

perfodd
Mike Gerwitz 2014-02-16 23:23:11 -05:00
parent 8d81373ef8
commit 75e1470582
3 changed files with 82 additions and 35 deletions

View File

@ -298,6 +298,7 @@ exports.prototype.build = function extend( _, __ )
base = args.pop() || exports.ClassBase, base = args.pop() || exports.ClassBase,
prototype = this._getBase( base ), prototype = this._getBase( base ),
cname = '', cname = '',
autoa = false,
prop_init = this._memberBuilder.initMembers(), prop_init = this._memberBuilder.initMembers(),
members = this._memberBuilder.initMembers( prototype ), members = this._memberBuilder.initMembers( prototype ),
@ -331,6 +332,12 @@ exports.prototype.build = function extend( _, __ )
delete props.__name; delete props.__name;
} }
// gobble up auto-abstract flag if present
if ( ( autoa = props.___$$auto$abstract$$ ) !== undefined )
{
delete props.___$$auto$abstract$$;
}
// IE has problems with toString() // IE has problems with toString()
if ( enum_bug ) if ( enum_bug )
{ {
@ -401,8 +408,7 @@ exports.prototype.build = function extend( _, __ )
new_class.___$$sinit$$ = staticInit; new_class.___$$sinit$$ = staticInit;
attachFlags( new_class, props ); attachFlags( new_class, props );
validateAbstract( new_class, cname, abstract_methods, autoa );
validateAbstract( new_class, cname, abstract_methods );
// We reduce the overall cost of this definition by defining it on the // We reduce the overall cost of this definition by defining it on the
// prototype rather than during instantiation. While this does increase the // prototype rather than during instantiation. While this does increase the
@ -588,13 +594,20 @@ exports.prototype.buildMembers = function buildMembers(
/** /**
* Validates abstract class requirements * Validates abstract class requirements
* *
* We permit an `auto' flag for internal use only that will cause the
* abstract flag to be automatically set if the class should be marked as
* abstract, instead of throwing an error; this should be used sparingly and
* never exposed via a public API (for explicit use), as it goes against the
* self-documentation philosophy.
*
* @param {function()} ctor class * @param {function()} ctor class
* @param {string} cname class name * @param {string} cname class name
* @param {{__length}} abstract_methods object containing abstract methods * @param {{__length}} abstract_methods object containing abstract methods
* @param {boolean} auto automatically flag as abstract
* *
* @return {undefined} * @return {undefined}
*/ */
function validateAbstract( ctor, cname, abstract_methods ) function validateAbstract( ctor, cname, abstract_methods, auto )
{ {
if ( ctor.___$$abstract$$ ) if ( ctor.___$$abstract$$ )
{ {
@ -606,17 +619,20 @@ function validateAbstract( ctor, cname, abstract_methods )
); );
} }
} }
else else if ( abstract_methods.__length > 0 )
{ {
if ( abstract_methods.__length > 0 ) if ( auto )
{ {
ctor.___$$abstract$$ = true;
return;
}
throw TypeError( throw TypeError(
"Class " + ( cname || "(anonymous)" ) + " contains abstract " + "Class " + ( cname || "(anonymous)" ) + " contains abstract " +
"members and must therefore be declared abstract" "members and must therefore be declared abstract"
); );
} }
} }
}
/** /**

View File

@ -109,9 +109,9 @@ Trait.extend = function( dfn )
} }
// invoked to trigger mixin // invoked to trigger mixin
TraitType.__mixin = function( dfn ) TraitType.__mixin = function( dfn, tc )
{ {
mixin( TraitType, dfn ); mixin( TraitType, dfn, tc );
}; };
return TraitType; return TraitType;
@ -238,21 +238,25 @@ function createVirtProxy( acls, dfn )
/** /**
* Mix trait into the given definition * Mix trait into the given definition
* *
* The original object DFN is modified; it is not cloned. * The original object DFN is modified; it is not cloned. TC should be
* initialized to an empty array; it is used to store context data for
* mixing in traits and will be encapsulated within a ctor closure (and thus
* will remain in memory).
* *
* @param {Trait} trait trait to mix in * @param {Trait} trait trait to mix in
* @param {Object} dfn definition object to merge into * @param {Object} dfn definition object to merge into
* @param {Array} tc trait class context
* *
* @return {Object} dfn * @return {Object} dfn
*/ */
function mixin( trait, dfn ) function mixin( trait, dfn, tc )
{ {
// the abstract class hidden within the trait // the abstract class hidden within the trait
var acls = trait.__acls, var acls = trait.__acls,
methods = acls.___$$methods$$; methods = acls.___$$methods$$;
// retrieve the private member name that will contain this trait object // retrieve the private member name that will contain this trait object
var iname = addTraitInst( trait, dfn ); var iname = addTraitInst( trait, dfn, tc );
mixMethods( methods['public'], dfn, 'public', iname ); mixMethods( methods['public'], dfn, 'public', iname );
mixMethods( methods['protected'], dfn, 'protected', iname ); mixMethods( methods['protected'], dfn, 'protected', iname );
@ -334,13 +338,13 @@ function mixMethods( src, dest, vis, iname )
* *
* @param {Class} T trait * @param {Class} T trait
* @param {Object} dfn definition object of class being mixed into * @param {Object} dfn definition object of class being mixed into
* @param {Array} tc trait class object
* *
* @return {string} private member into which C instance shall be stored * @return {string} private member into which C instance shall be stored
*/ */
function addTraitInst( T, dfn ) function addTraitInst( T, dfn, tc )
{ {
var tc = ( dfn.___$$tc$$ = ( dfn.___$$tc$$ || [] ) ), var iname = '___$to$' + T.__acls.__cid;
iname = '___$to$' + T.__acls.__cid;
// the trait object array will contain two values: the destination field // the trait object array will contain two values: the destination field
// and the trait to instantiate // and the trait to instantiate

View File

@ -379,24 +379,54 @@ function createImplement( base, ifaces, cname )
function createUse( base, traits ) function createUse( base, traits )
{ {
// invoking the partially applied class will immediately complete its
// definition and instantiate it with the provided constructor arguments
var partial = function() var partial = function()
{ {
return partial.extend( {} ).apply( null, arguments ); return createMixedClass( base, traits )
.apply( null, arguments );
}; };
// otherwise, its definition is deferred until additional context is
// given during the extend operation
partial.extend = function() partial.extend = function()
{ {
var args = Array.prototype.slice.call( arguments ), var args = Array.prototype.slice.call( arguments ),
dfn = args.pop(), dfn = args.pop(),
ext_base = args.pop(); ext_base = args.pop();
// "mix" each trait into the provided definition object // extend the mixed class, which ensures that all super references
for ( var i = 0, n = traits.length; i < n; i++ ) // are properly resolved
{ return extend.call( null,
traits[ i ].__mixin( dfn ); createMixedClass( ( base || ext_base ), traits ),
dfn
);
};
return partial;
} }
var C = extend.call( null, ( base || ext_base ), dfn ),
function createMixedClass( base, traits )
{
// generated definition for our [abstract] class that will mix in each
// of the provided traits; it will automatically be marked as abstract
// if needed
var dfn = { ___$$auto$abstract$$: true };
// this object is used as a class-specific context for storing trait
// data; it will be encapsulated within a ctor closure and will not be
// attached to any class
var tc = [];
// "mix" each trait into the class definition object
for ( var i = 0, n = traits.length; i < n; i++ )
{
traits[ i ].__mixin( dfn, tc );
}
// create the mixed class from the above generated definition
var C = extend.call( null, base, dfn ),
meta = ClassBuilder.getMeta( C ); meta = ClassBuilder.getMeta( C );
// add each trait to the list of implemented types so that the // add each trait to the list of implemented types so that the
@ -407,9 +437,6 @@ function createUse( base, traits )
} }
return C; return C;
};
return partial;
} }