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,
|
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"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
20
lib/Trait.js
20
lib/Trait.js
|
@ -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
|
||||||
|
|
45
lib/class.js
45
lib/class.js
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue