Initial refactoring of class_builder module into ClassBuilder ctor (#25)
parent
a401c31996
commit
7a579ab2aa
|
@ -27,42 +27,26 @@
|
||||||
* class tests in tact for a higher-level test.
|
* class tests in tact for a higher-level test.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var util = require( __dirname + '/util' ),
|
var util = require( __dirname + '/util' ),
|
||||||
warn = require( __dirname + '/warn' ),
|
warn = require( __dirname + '/warn' ),
|
||||||
member_builder = require( __dirname + '/member_builder' ),
|
propobj = require( __dirname + '/propobj' ),
|
||||||
propobj = require( __dirname + '/propobj' ),
|
|
||||||
|
|
||||||
Warning = warn.Warning,
|
Warning = warn.Warning,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class id counter, to be increment on each new definition
|
* IE contains a nasty enumeration "bug" (poor implementation) that makes
|
||||||
* @type {number}
|
* toString unenumerable. This means that, if you do obj.toString = foo,
|
||||||
*/
|
* toString will NOT show up in `for` or hasOwnProperty(). This is a problem.
|
||||||
class_id = 0,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance id counter, to be incremented on each new instance
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
instance_id = 0,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set to TRUE when class is in the process of being extended to ensure that
|
|
||||||
* a constructor can be instantiated (to use as the prototype) without
|
|
||||||
* invoking the class construction logic
|
|
||||||
*
|
*
|
||||||
* @type {boolean}
|
* This test will determine if this poor implementation exists.
|
||||||
*/
|
*/
|
||||||
extending = false,
|
enum_bug = (
|
||||||
|
Object.prototype.propertyIsEnumerable.call(
|
||||||
/**
|
{ toString: function() {} },
|
||||||
* A flag to let the system know that we are currently attempting to access
|
'toString'
|
||||||
* a static property from within a method. This means that the caller should
|
) === false
|
||||||
* be given access to additional levels of visibility.
|
)
|
||||||
*
|
? true
|
||||||
* @type {boolean}
|
: false,
|
||||||
*/
|
|
||||||
sprop_internal = false,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hash of reserved members
|
* Hash of reserved members
|
||||||
|
@ -88,26 +72,62 @@ var util = require( __dirname + '/util' ),
|
||||||
'__construct': true,
|
'__construct': true,
|
||||||
'toString': true,
|
'toString': true,
|
||||||
'__toString': true,
|
'__toString': true,
|
||||||
}
|
};
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IE contains a nasty enumeration "bug" (poor implementation) that makes
|
* Initializes class builder with given member builder
|
||||||
* toString unenumerable. This means that, if you do obj.toString = foo,
|
|
||||||
* toString will NOT show up in `for` or hasOwnProperty(). This is a problem.
|
|
||||||
*
|
*
|
||||||
* This test will determine if this poor implementation exists.
|
* The 'new' keyword is not required when instantiating this constructor.
|
||||||
|
*
|
||||||
|
* @param {Object} member_builder member builder
|
||||||
*/
|
*/
|
||||||
var enum_bug = (
|
module.exports = exports =
|
||||||
Object.prototype.propertyIsEnumerable.call(
|
function ClassBuilder( member_builder )
|
||||||
{ toString: function() {} },
|
{
|
||||||
'toString'
|
// allow ommitting the 'new' keyword
|
||||||
) === false
|
if ( !( this instanceof exports ) )
|
||||||
)
|
{
|
||||||
? true
|
return new exports( member_builder );
|
||||||
: false
|
}
|
||||||
;
|
|
||||||
|
/**
|
||||||
|
* Used for building class members
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
this._memberBuilder = member_builder;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class id counter, to be increment on each new definition
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this._classId = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance id counter, to be incremented on each new instance
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this._instanceId = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to TRUE when class is in the process of being extended to ensure that
|
||||||
|
* a constructor can be instantiated (to use as the prototype) without
|
||||||
|
* invoking the class construction logic
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this._extending = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag to let the system know that we are currently attempting to access
|
||||||
|
* a static property from within a method. This means that the caller should
|
||||||
|
* be given access to additional levels of visibility.
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this._spropInternal = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,6 +137,7 @@ var enum_bug = (
|
||||||
*/
|
*/
|
||||||
exports.ClassBase = function Class() {};
|
exports.ClassBase = function Class() {};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default static property method
|
* Default static property method
|
||||||
*
|
*
|
||||||
|
@ -168,6 +189,73 @@ exports.getForcedPublicMethods = function()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns reference to metadata for the requested class
|
||||||
|
*
|
||||||
|
* Since a reference is returned (rather than a copy), the returned object can
|
||||||
|
* be modified to alter the metadata.
|
||||||
|
*
|
||||||
|
* @param {Class} cls class from which to retrieve metadata
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
exports.getMeta = function( cls )
|
||||||
|
{
|
||||||
|
return cls.___$$meta$$ || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the class is an instance of the given type
|
||||||
|
*
|
||||||
|
* The given type can be a class, interface, trait or any other type of object.
|
||||||
|
* It may be used in place of the 'instanceof' operator and contains additional
|
||||||
|
* enhancements that the operator is unable to provide due to prototypal
|
||||||
|
* restrictions.
|
||||||
|
*
|
||||||
|
* @param {Object} type expected type
|
||||||
|
* @param {Object} instance instance to check
|
||||||
|
*
|
||||||
|
* @return {boolean} true if instance is an instance of type, otherwise false
|
||||||
|
*/
|
||||||
|
exports.isInstanceOf = function( type, instance )
|
||||||
|
{
|
||||||
|
var meta, implemented, i;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// check prototype chain (will throw an error if type is not a
|
||||||
|
// constructor (function)
|
||||||
|
if ( instance instanceof type )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( e ) {}
|
||||||
|
|
||||||
|
// if no metadata is available, then our remaining checks cannot be
|
||||||
|
// performed
|
||||||
|
if ( !instance.__cid || !( meta = exports.getMeta( instance ) ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
implemented = meta.implemented;
|
||||||
|
i = implemented.length;
|
||||||
|
|
||||||
|
// check implemented interfaces
|
||||||
|
while ( i-- )
|
||||||
|
{
|
||||||
|
if ( implemented[ i ] === type )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mimics class inheritance
|
* Mimics class inheritance
|
||||||
*
|
*
|
||||||
|
@ -180,10 +268,10 @@ exports.getForcedPublicMethods = function()
|
||||||
*
|
*
|
||||||
* @return {Object} extended class
|
* @return {Object} extended class
|
||||||
*/
|
*/
|
||||||
exports.build = function extend()
|
exports.prototype.build = function extend()
|
||||||
{
|
{
|
||||||
// ensure we'll be permitted to instantiate abstract classes for the base
|
// ensure we'll be permitted to instantiate abstract classes for the base
|
||||||
extending = true;
|
this._extending = true;
|
||||||
|
|
||||||
var args = Array.prototype.slice.call( arguments ),
|
var args = Array.prototype.slice.call( arguments ),
|
||||||
props = args.pop() || {},
|
props = args.pop() || {},
|
||||||
|
@ -191,11 +279,11 @@ exports.build = function extend()
|
||||||
prototype = new base(),
|
prototype = new base(),
|
||||||
cname = '',
|
cname = '',
|
||||||
|
|
||||||
prop_init = member_builder.initMembers(),
|
prop_init = this._memberBuilder.initMembers(),
|
||||||
members = member_builder.initMembers( prototype ),
|
members = this._memberBuilder.initMembers( prototype ),
|
||||||
static_members = {
|
static_members = {
|
||||||
methods: member_builder.initMembers(),
|
methods: this._memberBuilder.initMembers(),
|
||||||
props: member_builder.initMembers(),
|
props: this._memberBuilder.initMembers(),
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract_methods =
|
abstract_methods =
|
||||||
|
@ -235,14 +323,14 @@ exports.build = function extend()
|
||||||
}
|
}
|
||||||
|
|
||||||
// increment class identifier
|
// increment class identifier
|
||||||
class_id++;
|
this._classId++;
|
||||||
|
|
||||||
// build the various class components (xxx: this is temporary; needs
|
// build the various class components (xxx: this is temporary; needs
|
||||||
// refactoring)
|
// refactoring)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
buildMembers( props,
|
this.buildMembers( props,
|
||||||
class_id,
|
this._classId,
|
||||||
base,
|
base,
|
||||||
prop_init,
|
prop_init,
|
||||||
abstract_methods,
|
abstract_methods,
|
||||||
|
@ -271,7 +359,7 @@ exports.build = function extend()
|
||||||
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 = this.createCtor( cname, abstract_methods, members );
|
||||||
|
|
||||||
// closure to hold static initialization to be used later by subtypes
|
// closure to hold static initialization to be used later by subtypes
|
||||||
initStaticVisibilityObj( new_class, static_members );
|
initStaticVisibilityObj( new_class, static_members );
|
||||||
|
@ -281,7 +369,7 @@ exports.build = function extend()
|
||||||
}
|
}
|
||||||
staticInit( new_class, false );
|
staticInit( new_class, false );
|
||||||
|
|
||||||
attachPropInit( prototype, prop_init, members, new_class, class_id );
|
attachPropInit( prototype, prop_init, members, new_class, this._classId );
|
||||||
|
|
||||||
new_class.prototype = prototype;
|
new_class.prototype = prototype;
|
||||||
new_class.constructor = new_class;
|
new_class.constructor = new_class;
|
||||||
|
@ -307,15 +395,128 @@ exports.build = function extend()
|
||||||
meta.name = cname;
|
meta.name = cname;
|
||||||
|
|
||||||
attachAbstract( new_class, abstract_methods );
|
attachAbstract( new_class, abstract_methods );
|
||||||
attachId( new_class, class_id );
|
attachId( new_class, this._classId );
|
||||||
|
|
||||||
// we're done with the extension process
|
// we're done with the extension process
|
||||||
extending = false;
|
this._extending = false;
|
||||||
|
|
||||||
return new_class;
|
return new_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
exports.prototype.buildMembers = function buildMembers(
|
||||||
|
props, class_id, base, prop_init, abstract_methods, members,
|
||||||
|
static_members, staticInstLookup
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var hasOwn = Array.prototype.hasOwnProperty,
|
||||||
|
defs = {},
|
||||||
|
|
||||||
|
smethods = static_members.methods,
|
||||||
|
sprops = static_members.props,
|
||||||
|
|
||||||
|
_self = this
|
||||||
|
;
|
||||||
|
|
||||||
|
util.propParse( props, {
|
||||||
|
each: function( name, value, keywords )
|
||||||
|
{
|
||||||
|
// disallow use of our internal __initProps() method
|
||||||
|
if ( reserved_members[ name ] === true )
|
||||||
|
{
|
||||||
|
throw Error(
|
||||||
|
( ( cname ) ? cname + '::' : '' ) +
|
||||||
|
( name + " is reserved" )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a member was defined multiple times in the same class
|
||||||
|
// declaration, throw an error
|
||||||
|
if ( hasOwn.call( defs, name ) )
|
||||||
|
{
|
||||||
|
throw Error(
|
||||||
|
"Cannot redefine method '" + name + "' in same declaration"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of the definitions (only during class declaration)
|
||||||
|
// to catch duplicates
|
||||||
|
defs[ name ] = 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
property: function( name, value, keywords )
|
||||||
|
{
|
||||||
|
var dest = ( keywordStatic( keywords ) ) ? sprops : prop_init;
|
||||||
|
|
||||||
|
// build a new property, passing in the other members to compare
|
||||||
|
// against for preventing nonsensical overrides
|
||||||
|
_self._memberBuilder.buildProp(
|
||||||
|
dest, null, name, value, keywords, base
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getter: function( name, value, keywords )
|
||||||
|
{
|
||||||
|
var dest = ( keywordStatic( keywords ) ) ? smethods : members;
|
||||||
|
|
||||||
|
_self._memberBuilder.buildGetter(
|
||||||
|
dest, null, name, value, keywords, base
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
setter: function( name, value, keywords )
|
||||||
|
{
|
||||||
|
var dest = ( keywordStatic( keywords ) ) ? smethods : members;
|
||||||
|
|
||||||
|
_self._memberBuilder.buildSetter(
|
||||||
|
dest, null, name, value, keywords, base
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
method: function( name, func, is_abstract, keywords )
|
||||||
|
{
|
||||||
|
var is_static = keywordStatic( keywords ),
|
||||||
|
dest = ( is_static ) ? smethods : members,
|
||||||
|
instLookup = ( is_static )
|
||||||
|
? staticInstLookup
|
||||||
|
: getMethodInstance
|
||||||
|
;
|
||||||
|
|
||||||
|
// constructor check
|
||||||
|
if ( public_methods[ name ] === true )
|
||||||
|
{
|
||||||
|
if ( keywords[ 'protected' ] || keywords[ 'private' ] )
|
||||||
|
{
|
||||||
|
throw TypeError(
|
||||||
|
name + " must be public"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_self._memberBuilder.buildMethod(
|
||||||
|
dest, null, name, func, keywords, instLookup,
|
||||||
|
class_id, base
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( is_abstract )
|
||||||
|
{
|
||||||
|
abstract_methods[ name ] = true;
|
||||||
|
abstract_methods.__length++;
|
||||||
|
}
|
||||||
|
else if ( ( hasOwn.call( abstract_methods, name ) )
|
||||||
|
&& ( is_abstract === false )
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// if this was a concrete method, then it should no longer
|
||||||
|
// be marked as abstract
|
||||||
|
delete abstract_methods[ name ];
|
||||||
|
abstract_methods.__length--;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates abstract class requirements
|
* Validates abstract class requirements
|
||||||
*
|
*
|
||||||
|
@ -362,17 +563,17 @@ function validateAbstract( ctor, cname, abstract_methods )
|
||||||
*
|
*
|
||||||
* @return {Function} constructor
|
* @return {Function} constructor
|
||||||
*/
|
*/
|
||||||
function createCtor( cname, abstract_methods, members )
|
exports.prototype.createCtor = function( cname, abstract_methods, members )
|
||||||
{
|
{
|
||||||
// concrete class
|
// concrete class
|
||||||
if ( abstract_methods.__length === 0 )
|
if ( abstract_methods.__length === 0 )
|
||||||
{
|
{
|
||||||
return createConcreteCtor( cname, members );
|
return this.createConcreteCtor( cname, members );
|
||||||
}
|
}
|
||||||
// abstract class
|
// abstract class
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return createAbstractCtor( cname );
|
return this.createAbstractCtor( cname );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,9 +589,10 @@ function createCtor( cname, abstract_methods, members )
|
||||||
*
|
*
|
||||||
* @return {function()} constructor
|
* @return {function()} constructor
|
||||||
*/
|
*/
|
||||||
function createConcreteCtor( cname, members )
|
exports.prototype.createConcreteCtor = function( cname, members )
|
||||||
{
|
{
|
||||||
var args = null;
|
var args = null,
|
||||||
|
_self = this;
|
||||||
|
|
||||||
// constructor function to be returned (the name is set to ClassInstance
|
// constructor function to be returned (the name is set to ClassInstance
|
||||||
// because some debuggers (e.g. v8) will show the name of this function for
|
// because some debuggers (e.g. v8) will show the name of this function for
|
||||||
|
@ -411,13 +613,13 @@ function createConcreteCtor( cname, members )
|
||||||
// If we're extending, we don't actually want to invoke any class
|
// If we're extending, we don't actually want to invoke any class
|
||||||
// construction logic. The above is sufficient to use this class in a
|
// construction logic. The above is sufficient to use this class in a
|
||||||
// prototype, so stop here.
|
// prototype, so stop here.
|
||||||
if ( extending )
|
if ( _self._extending )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate and store unique instance id
|
// generate and store unique instance id
|
||||||
attachInstanceId( this, ++instance_id );
|
attachInstanceId( this, ++_self._instanceId );
|
||||||
|
|
||||||
// call the constructor, if one was provided
|
// call the constructor, if one was provided
|
||||||
if ( this.__construct instanceof Function )
|
if ( this.__construct instanceof Function )
|
||||||
|
@ -477,11 +679,13 @@ function createConcreteCtor( cname, members )
|
||||||
*
|
*
|
||||||
* @return {function()} constructor
|
* @return {function()} constructor
|
||||||
*/
|
*/
|
||||||
function createAbstractCtor( cname )
|
exports.prototype.createAbstractCtor = function( cname )
|
||||||
{
|
{
|
||||||
|
var _self = this;
|
||||||
|
|
||||||
var __abstract_self = function()
|
var __abstract_self = function()
|
||||||
{
|
{
|
||||||
if ( !extending )
|
if ( !_self._extending )
|
||||||
{
|
{
|
||||||
throw Error(
|
throw Error(
|
||||||
"Abstract class " + ( cname || '(anonymous)' ) +
|
"Abstract class " + ( cname || '(anonymous)' ) +
|
||||||
|
@ -505,117 +709,6 @@ function createAbstractCtor( cname )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function buildMembers(
|
|
||||||
props, class_id, base, prop_init, abstract_methods, members,
|
|
||||||
static_members, staticInstLookup
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var hasOwn = Array.prototype.hasOwnProperty,
|
|
||||||
defs = {},
|
|
||||||
|
|
||||||
smethods = static_members.methods,
|
|
||||||
sprops = static_members.props
|
|
||||||
;
|
|
||||||
|
|
||||||
util.propParse( props, {
|
|
||||||
each: function( name, value, keywords )
|
|
||||||
{
|
|
||||||
// disallow use of our internal __initProps() method
|
|
||||||
if ( reserved_members[ name ] === true )
|
|
||||||
{
|
|
||||||
throw Error(
|
|
||||||
( ( cname ) ? cname + '::' : '' ) +
|
|
||||||
( name + " is reserved" )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if a member was defined multiple times in the same class
|
|
||||||
// declaration, throw an error
|
|
||||||
if ( hasOwn.call( defs, name ) )
|
|
||||||
{
|
|
||||||
throw Error(
|
|
||||||
"Cannot redefine method '" + name + "' in same declaration"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep track of the definitions (only during class declaration)
|
|
||||||
// to catch duplicates
|
|
||||||
defs[ name ] = 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
property: function( name, value, keywords )
|
|
||||||
{
|
|
||||||
var dest = ( keywordStatic( keywords ) ) ? sprops : prop_init;
|
|
||||||
|
|
||||||
// build a new property, passing in the other members to compare
|
|
||||||
// against for preventing nonsensical overrides
|
|
||||||
member_builder.buildProp(
|
|
||||||
dest, null, name, value, keywords, base
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
getter: function( name, value, keywords )
|
|
||||||
{
|
|
||||||
var dest = ( keywordStatic( keywords ) ) ? smethods : members;
|
|
||||||
|
|
||||||
member_builder.buildGetter(
|
|
||||||
dest, null, name, value, keywords, base
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
setter: function( name, value, keywords )
|
|
||||||
{
|
|
||||||
var dest = ( keywordStatic( keywords ) ) ? smethods : members;
|
|
||||||
|
|
||||||
member_builder.buildSetter(
|
|
||||||
dest, null, name, value, keywords, base
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
method: function( name, func, is_abstract, keywords )
|
|
||||||
{
|
|
||||||
var is_static = keywordStatic( keywords ),
|
|
||||||
dest = ( is_static ) ? smethods : members,
|
|
||||||
instLookup = ( is_static )
|
|
||||||
? staticInstLookup
|
|
||||||
: getMethodInstance
|
|
||||||
;
|
|
||||||
|
|
||||||
// constructor check
|
|
||||||
if ( public_methods[ name ] === true )
|
|
||||||
{
|
|
||||||
if ( keywords[ 'protected' ] || keywords[ 'private' ] )
|
|
||||||
{
|
|
||||||
throw TypeError(
|
|
||||||
name + " must be public"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
member_builder.buildMethod(
|
|
||||||
dest, null, name, func, keywords, instLookup,
|
|
||||||
class_id, base
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( is_abstract )
|
|
||||||
{
|
|
||||||
abstract_methods[ name ] = true;
|
|
||||||
abstract_methods.__length++;
|
|
||||||
}
|
|
||||||
else if ( ( hasOwn.call( abstract_methods, name ) )
|
|
||||||
&& ( is_abstract === false )
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// if this was a concrete method, then it should no longer
|
|
||||||
// be marked as abstract
|
|
||||||
delete abstract_methods[ name ];
|
|
||||||
abstract_methods.__length--;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the given keywords should result in a static member
|
* Determines if the given keywords should result in a static member
|
||||||
*
|
*
|
||||||
|
@ -714,6 +807,8 @@ function attachPropInit( prototype, properties, members, ctor, cid )
|
||||||
*/
|
*/
|
||||||
function initStaticVisibilityObj( ctor )
|
function initStaticVisibilityObj( ctor )
|
||||||
{
|
{
|
||||||
|
var _self = this;
|
||||||
|
|
||||||
// the object will simply be another layer in the prototype chain to
|
// the object will simply be another layer in the prototype chain to
|
||||||
// prevent protected/private members from being mixed in with the public
|
// prevent protected/private members from being mixed in with the public
|
||||||
var sobj = function() {};
|
var sobj = function() {};
|
||||||
|
@ -731,9 +826,9 @@ function initStaticVisibilityObj( ctor )
|
||||||
// internal flag cannot be modified by conventional means.
|
// internal flag cannot be modified by conventional means.
|
||||||
sobji.$ = function()
|
sobji.$ = function()
|
||||||
{
|
{
|
||||||
sprop_internal = true;
|
_self._spropInternal = true;
|
||||||
var val = ctor.$.apply( ctor, arguments );
|
var val = ctor.$.apply( ctor, arguments );
|
||||||
sprop_internal = false;
|
_self._spropInternal = false;
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
|
@ -761,7 +856,9 @@ function initStaticVisibilityObj( ctor )
|
||||||
function attachStatic( ctor, members, base, inheriting )
|
function attachStatic( ctor, members, base, inheriting )
|
||||||
{
|
{
|
||||||
var methods = members.methods,
|
var methods = members.methods,
|
||||||
props = members.props;
|
props = members.props,
|
||||||
|
_self = this
|
||||||
|
;
|
||||||
|
|
||||||
// "Inherit" the parent's static methods by running the parent's static
|
// "Inherit" the parent's static methods by running the parent's static
|
||||||
// initialization method. It is important that we do this before anything,
|
// initialization method. It is important that we do this before anything,
|
||||||
|
@ -805,7 +902,7 @@ function attachStatic( ctor, members, base, inheriting )
|
||||||
// to check other levels of visibility. `found` will contain the
|
// to check other levels of visibility. `found` will contain the
|
||||||
// visibility level the property was found in, or false.
|
// visibility level the property was found in, or false.
|
||||||
found = has.call( props[ 'public' ], prop ) && 'public';
|
found = has.call( props[ 'public' ], prop ) && 'public';
|
||||||
if ( !found && sprop_internal )
|
if ( !found && _self._spropInternal )
|
||||||
{
|
{
|
||||||
// Check for protected/private. We only check for private
|
// Check for protected/private. We only check for private
|
||||||
// properties if we are not currently checking the properties of
|
// properties if we are not currently checking the properties of
|
||||||
|
@ -898,22 +995,6 @@ function createMeta( func, cparent )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns reference to metadata for the requested class
|
|
||||||
*
|
|
||||||
* Since a reference is returned (rather than a copy), the returned object can
|
|
||||||
* be modified to alter the metadata.
|
|
||||||
*
|
|
||||||
* @param {Class} cls class from which to retrieve metadata
|
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
exports.getMeta = function( cls )
|
|
||||||
{
|
|
||||||
return cls.___$$meta$$ || {};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches an instance identifier to a class instance
|
* Attaches an instance identifier to a class instance
|
||||||
*
|
*
|
||||||
|
@ -1014,57 +1095,6 @@ function getMethodInstance( inst, cid )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if the class is an instance of the given type
|
|
||||||
*
|
|
||||||
* The given type can be a class, interface, trait or any other type of object.
|
|
||||||
* It may be used in place of the 'instanceof' operator and contains additional
|
|
||||||
* enhancements that the operator is unable to provide due to prototypal
|
|
||||||
* restrictions.
|
|
||||||
*
|
|
||||||
* @param {Object} type expected type
|
|
||||||
* @param {Object} instance instance to check
|
|
||||||
*
|
|
||||||
* @return {boolean} true if instance is an instance of type, otherwise false
|
|
||||||
*/
|
|
||||||
exports.isInstanceOf = function( type, instance )
|
|
||||||
{
|
|
||||||
var meta, implemented, i;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// check prototype chain (will throw an error if type is not a
|
|
||||||
// constructor (function)
|
|
||||||
if ( instance instanceof type )
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch ( e ) {}
|
|
||||||
|
|
||||||
// if no metadata is available, then our remaining checks cannot be
|
|
||||||
// performed
|
|
||||||
if ( !instance.__cid || !( meta = exports.getMeta( instance ) ) )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
implemented = meta.implemented;
|
|
||||||
i = implemented.length;
|
|
||||||
|
|
||||||
// check implemented interfaces
|
|
||||||
while ( i-- )
|
|
||||||
{
|
|
||||||
if ( implemented[ i ] === type )
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches isAbstract() method to the class
|
* Attaches isAbstract() method to the class
|
||||||
*
|
*
|
18
lib/class.js
18
lib/class.js
|
@ -22,8 +22,12 @@
|
||||||
* @package core
|
* @package core
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var util = require( __dirname + '/util' ),
|
var util = require( __dirname + '/util' ),
|
||||||
class_builder = require( __dirname + '/class_builder' )
|
ClassBuilder = require( __dirname + '/ClassBuilder' ),
|
||||||
|
|
||||||
|
class_builder = ClassBuilder(
|
||||||
|
require( __dirname + '/member_builder' )
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,7 +115,7 @@ module.exports.isClass = function( obj )
|
||||||
{
|
{
|
||||||
obj = obj || {};
|
obj = obj || {};
|
||||||
|
|
||||||
return ( obj.prototype instanceof class_builder.ClassBase )
|
return ( obj.prototype instanceof ClassBuilder.ClassBase )
|
||||||
? true
|
? true
|
||||||
: false
|
: false
|
||||||
;
|
;
|
||||||
|
@ -131,7 +135,7 @@ module.exports.isClassInstance = function( obj )
|
||||||
{
|
{
|
||||||
obj = obj || {};
|
obj = obj || {};
|
||||||
|
|
||||||
return ( obj instanceof class_builder.ClassBase )
|
return ( obj instanceof ClassBuilder.ClassBase )
|
||||||
? true
|
? true
|
||||||
: false;
|
: false;
|
||||||
};
|
};
|
||||||
|
@ -150,7 +154,7 @@ module.exports.isClassInstance = function( obj )
|
||||||
*
|
*
|
||||||
* @return {boolean} true if instance is an instance of type, otherwise false
|
* @return {boolean} true if instance is an instance of type, otherwise false
|
||||||
*/
|
*/
|
||||||
module.exports.isInstanceOf = class_builder.isInstanceOf;
|
module.exports.isInstanceOf = ClassBuilder.isInstanceOf;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -353,7 +357,7 @@ function createImplement( base, ifaces, cname )
|
||||||
function extend()
|
function extend()
|
||||||
{
|
{
|
||||||
// set up the new class
|
// set up the new class
|
||||||
var new_class = class_builder.build.apply( null, arguments );
|
var new_class = class_builder.build.apply( class_builder, arguments );
|
||||||
|
|
||||||
// set up some additional convenience props
|
// set up some additional convenience props
|
||||||
setupProps( new_class );
|
setupProps( new_class );
|
||||||
|
@ -413,7 +417,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 );
|
||||||
class_builder.getMeta( class_new ).implemented = implemented;
|
ClassBuilder.getMeta( class_new ).implemented = implemented;
|
||||||
|
|
||||||
return class_new;
|
return class_new;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
|
|
||||||
var common = require( './common' ),
|
var common = require( './common' ),
|
||||||
assert = require( 'assert' ),
|
assert = require( 'assert' ),
|
||||||
builder = common.require( 'class_builder' )
|
builder = common.require( 'ClassBuilder' )(
|
||||||
|
common.require( 'member_builder' )
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
|
|
||||||
var common = require( './common' ),
|
var common = require( './common' ),
|
||||||
assert = require( 'assert' ),
|
assert = require( 'assert' ),
|
||||||
builder = common.require( 'class_builder' ),
|
builder = common.require( 'ClassBuilder' )(
|
||||||
|
common.require( 'member_builder' )
|
||||||
|
),
|
||||||
|
|
||||||
Class = common.require( 'class' )
|
Class = common.require( 'class' )
|
||||||
FinalClass = common.require( 'class_final' )
|
FinalClass = common.require( 'class_final' )
|
||||||
|
|
|
@ -24,7 +24,11 @@
|
||||||
|
|
||||||
var common = require( './common' ),
|
var common = require( './common' ),
|
||||||
assert = require( 'assert' ),
|
assert = require( 'assert' ),
|
||||||
builder = common.require( 'class_builder' )
|
|
||||||
|
ClassBuilder = common.require( 'ClassBuilder' ),
|
||||||
|
builder = ClassBuilder(
|
||||||
|
common.require( 'member_builder' )
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,7 +39,7 @@ var common = require( './common' ),
|
||||||
*/
|
*/
|
||||||
( function testCanRetrieveListOfReservedMembers()
|
( function testCanRetrieveListOfReservedMembers()
|
||||||
{
|
{
|
||||||
var reserved = builder.getReservedMembers();
|
var reserved = ClassBuilder.getReservedMembers();
|
||||||
|
|
||||||
assert.ok( reserved instanceof Object,
|
assert.ok( reserved instanceof Object,
|
||||||
"Can retrieve hash of reserved members"
|
"Can retrieve hash of reserved members"
|
||||||
|
@ -60,10 +64,10 @@ var common = require( './common' ),
|
||||||
var val = 'foo';
|
var val = 'foo';
|
||||||
|
|
||||||
// attempt to add to list
|
// attempt to add to list
|
||||||
builder.getReservedMembers().foo = val;
|
ClassBuilder.getReservedMembers().foo = val;
|
||||||
|
|
||||||
assert.notEqual(
|
assert.notEqual(
|
||||||
builder.getReservedMembers().foo,
|
ClassBuilder.getReservedMembers().foo,
|
||||||
val,
|
val,
|
||||||
"Cannot alter internal list of reserved members"
|
"Cannot alter internal list of reserved members"
|
||||||
);
|
);
|
||||||
|
@ -76,7 +80,7 @@ var common = require( './common' ),
|
||||||
*/
|
*/
|
||||||
( function testAllReservedMembersAreActuallyReserved()
|
( function testAllReservedMembersAreActuallyReserved()
|
||||||
{
|
{
|
||||||
var reserved = builder.getReservedMembers(),
|
var reserved = ClassBuilder.getReservedMembers(),
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
// test each of the reserved members
|
// test each of the reserved members
|
||||||
|
@ -126,7 +130,7 @@ var common = require( './common' ),
|
||||||
*/
|
*/
|
||||||
( function testCanRetrieveListOfForcedPublicMethods()
|
( function testCanRetrieveListOfForcedPublicMethods()
|
||||||
{
|
{
|
||||||
var pub = builder.getForcedPublicMethods(),
|
var pub = ClassBuilder.getForcedPublicMethods(),
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
assert.ok( pub instanceof Object,
|
assert.ok( pub instanceof Object,
|
||||||
|
@ -153,10 +157,10 @@ var common = require( './common' ),
|
||||||
var val = 'foo';
|
var val = 'foo';
|
||||||
|
|
||||||
// attempt to add to list
|
// attempt to add to list
|
||||||
builder.getForcedPublicMethods().foo = val;
|
ClassBuilder.getForcedPublicMethods().foo = val;
|
||||||
|
|
||||||
assert.notEqual(
|
assert.notEqual(
|
||||||
builder.getForcedPublicMethods().foo,
|
ClassBuilder.getForcedPublicMethods().foo,
|
||||||
val,
|
val,
|
||||||
"Cannot alter internal list of forced-public methods"
|
"Cannot alter internal list of forced-public methods"
|
||||||
);
|
);
|
||||||
|
@ -169,7 +173,7 @@ var common = require( './common' ),
|
||||||
*/
|
*/
|
||||||
( function testAllForcedPublicMethodsAreForcedToPublic()
|
( function testAllForcedPublicMethodsAreForcedToPublic()
|
||||||
{
|
{
|
||||||
var pub = builder.getForcedPublicMethods();
|
var pub = ClassBuilder.getForcedPublicMethods();
|
||||||
|
|
||||||
// test each of the reserved members
|
// test each of the reserved members
|
||||||
for ( name in pub )
|
for ( name in pub )
|
||||||
|
|
|
@ -22,10 +22,12 @@
|
||||||
* @package test
|
* @package test
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var common = require( './common' ),
|
var common = require( './common' ),
|
||||||
assert = require( 'assert' ),
|
assert = require( 'assert' ),
|
||||||
builder = common.require( 'class_builder' ),
|
fallback = common.require( 'util' ).definePropertyFallback()
|
||||||
fallback = common.require( 'util' ).definePropertyFallback()
|
builder = common.require( 'ClassBuilder' )(
|
||||||
|
common.require( 'member_builder' )
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,9 @@
|
||||||
var common = require( './common' ),
|
var common = require( './common' ),
|
||||||
assert = require( 'assert' ),
|
assert = require( 'assert' ),
|
||||||
util = common.require( 'util' ),
|
util = common.require( 'util' ),
|
||||||
builder = common.require( 'class_builder' )
|
builder = common.require( 'ClassBuilder' )(
|
||||||
|
common.require( 'member_builder' )
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,10 @@
|
||||||
|
|
||||||
var common = require( './common' ),
|
var common = require( './common' ),
|
||||||
assert = require( 'assert' ),
|
assert = require( 'assert' ),
|
||||||
builder = common.require( 'class_builder' ),
|
|
||||||
warn = common.require( 'warn' )
|
warn = common.require( 'warn' )
|
||||||
|
builder = common.require( 'ClassBuilder' )(
|
||||||
|
common.require( 'member_builder' )
|
||||||
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ TPL_VAR='/**{CONTENT}**/'
|
||||||
RMTRAIL="$PATH_TOOLS/rmtrail"
|
RMTRAIL="$PATH_TOOLS/rmtrail"
|
||||||
|
|
||||||
# order matters
|
# order matters
|
||||||
CAT_MODULES="warn prop_parser util propobj member_builder class_builder"
|
CAT_MODULES="warn prop_parser util propobj member_builder ClassBuilder"
|
||||||
CAT_MODULES="$CAT_MODULES class class_final class_abstract interface"
|
CAT_MODULES="$CAT_MODULES class class_final class_abstract interface"
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
Loading…
Reference in New Issue