Class.use now creates its own class
parent
8d81373ef8
commit
75e1470582
|
@ -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,15 +619,18 @@ function validateAbstract( ctor, cname, abstract_methods )
|
|||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if ( abstract_methods.__length > 0 )
|
||||
{
|
||||
if ( abstract_methods.__length > 0 )
|
||||
if ( auto )
|
||||
{
|
||||
throw TypeError(
|
||||
"Class " + ( cname || "(anonymous)" ) + " contains abstract " +
|
||||
"members and must therefore be declared abstract"
|
||||
);
|
||||
ctor.___$$abstract$$ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
throw TypeError(
|
||||
"Class " + ( cname || "(anonymous)" ) + " contains abstract " +
|
||||
"members and must therefore be declared abstract"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
20
lib/Trait.js
20
lib/Trait.js
|
@ -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
|
||||
|
|
63
lib/class.js
63
lib/class.js
|
@ -379,40 +379,67 @@ 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
|
||||
for ( var i = 0, n = traits.length; i < n; i++ )
|
||||
{
|
||||
traits[ i ].__mixin( dfn );
|
||||
}
|
||||
|
||||
var C = extend.call( null, ( base || ext_base ), dfn ),
|
||||
meta = ClassBuilder.getMeta( C );
|
||||
|
||||
// add each trait to the list of implemented types so that the
|
||||
// class is considered to be of type T in traits
|
||||
for ( var i = 0, n = traits.length; i < n; i++ )
|
||||
{
|
||||
meta.implemented.push( traits[ i ] );
|
||||
}
|
||||
|
||||
return C;
|
||||
// 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, tc );
|
||||
}
|
||||
|
||||
// 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
|
||||
// class is considered to be of type T in traits
|
||||
for ( var i = 0, n = traits.length; i < n; i++ )
|
||||
{
|
||||
meta.implemented.push( traits[ i ] );
|
||||
}
|
||||
|
||||
return C;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mimics class inheritance
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue