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,
prototype = this._getBase( base ),
cname = '',
autoa = false,
prop_init = this._memberBuilder.initMembers(),
members = this._memberBuilder.initMembers( prototype ),
@ -331,6 +332,12 @@ exports.prototype.build = function extend( _, __ )
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()
if ( enum_bug )
{
@ -401,8 +408,7 @@ exports.prototype.build = function extend( _, __ )
new_class.___$$sinit$$ = staticInit;
attachFlags( new_class, props );
validateAbstract( new_class, cname, abstract_methods );
validateAbstract( new_class, cname, abstract_methods, autoa );
// We reduce the overall cost of this definition by defining it on the
// prototype rather than during instantiation. While this does increase the
@ -588,13 +594,20 @@ exports.prototype.buildMembers = function buildMembers(
/**
* 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 {string} cname class name
* @param {{__length}} abstract_methods object containing abstract methods
* @param {boolean} auto automatically flag as abstract
*
* @return {undefined}
*/
function validateAbstract( ctor, cname, abstract_methods )
function validateAbstract( ctor, cname, abstract_methods, auto )
{
if ( ctor.___$$abstract$$ )
{
@ -606,16 +619,19 @@ 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(
"Class " + ( cname || "(anonymous)" ) + " contains abstract " +
"members and must therefore be declared abstract"
);
}
}
}

View File

@ -109,9 +109,9 @@ Trait.extend = function( dfn )
}
// invoked to trigger mixin
TraitType.__mixin = function( dfn )
TraitType.__mixin = function( dfn, tc )
{
mixin( TraitType, dfn );
mixin( TraitType, dfn, tc );
};
return TraitType;
@ -238,21 +238,25 @@ function createVirtProxy( acls, dfn )
/**
* 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 {Object} dfn definition object to merge into
* @param {Array} tc trait class context
*
* @return {Object} dfn
*/
function mixin( trait, dfn )
function mixin( trait, dfn, tc )
{
// the abstract class hidden within the trait
var acls = trait.__acls,
methods = acls.___$$methods$$;
// 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['protected'], dfn, 'protected', iname );
@ -334,13 +338,13 @@ function mixMethods( src, dest, vis, iname )
*
* @param {Class} T trait
* @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
*/
function addTraitInst( T, dfn )
function addTraitInst( T, dfn, tc )
{
var tc = ( dfn.___$$tc$$ = ( dfn.___$$tc$$ || [] ) ),
iname = '___$to$' + T.__acls.__cid;
var iname = '___$to$' + T.__acls.__cid;
// the trait object array will contain two values: the destination field
// and the trait to instantiate

View File

@ -379,24 +379,54 @@ function createImplement( base, ifaces, cname )
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()
{
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()
{
var args = Array.prototype.slice.call( arguments ),
dfn = args.pop(),
ext_base = args.pop();
// "mix" each trait into the provided definition object
// extend the mixed class, which ensures that all super references
// are properly resolved
return extend.call( null,
createMixedClass( ( base || ext_base ), traits ),
dfn
);
};
return partial;
}
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 );
traits[ i ].__mixin( dfn, tc );
}
var C = extend.call( null, ( base || ext_base ), dfn ),
// create the mixed class from the above generated definition
var C = extend.call( null, base, dfn ),
meta = ClassBuilder.getMeta( C );
// add each trait to the list of implemented types so that the
@ -407,9 +437,6 @@ function createUse( base, traits )
}
return C;
};
return partial;
}