From 316a7dd703c3adad727e437450cdea8817ff95a9 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Tue, 11 Mar 2014 06:36:45 -0400 Subject: [PATCH] Refactored Traits to use propParse hooks --- lib/ClassBuilder.js | 39 +++++++++++++++++++++++++--- lib/Trait.js | 63 +++++++++++++++++++++++++++++++-------------- 2 files changed, 80 insertions(+), 22 deletions(-) diff --git a/lib/ClassBuilder.js b/lib/ClassBuilder.js index b351dd6..5d29a29 100644 --- a/lib/ClassBuilder.js +++ b/lib/ClassBuilder.js @@ -487,14 +487,47 @@ exports.prototype.buildMembers = function buildMembers( virtual_members: memberdest['virtual'], }; - util.propParse( props, { + // default member handlers for parser + var handlers = { each: _parseEach, property: _parseProp, getset: _parseGetSet, method: _parseMethod, - }, context ); + }; - // process accumulated member state + // a custom parser may be provided to hook the below property parser; + // this can be done to save time on post-processing, or alter the + // default behavior of the parser + if ( props.___$$parser$$ ) + { + // this isn't something that we actually want to parse + var parser = props.___$$parser$$; + delete props.___$$parser$$; + + function hjoin( name, orig ) + { + handlers[ name ] = function() + { + var args = Array.prototype.slice.call( arguments ); + + // invoke the custom handler with the original handler as + // its last argument (which the custom handler may choose + // not to invoke at all) + args.push( orig ); + parser[ name ].apply( context, args ); + }; + } + + // this avoids a performance penalty unless the above property is + // set + parser.each && hjoin( 'each', handlers.each ); + parser.property && hjoin( 'property', handlers.property ); + parser.getset && hjoin( 'getset', handlers.getset ); + parser.method && hjoin( 'method', handlers.method ); + } + + // parse members and process accumulated member state + util.propParse( props, handlers, context ); this._memberBuilder.end( context.state ); } diff --git a/lib/Trait.js b/lib/Trait.js index 31de351..c2bb77b 100644 --- a/lib/Trait.js +++ b/lib/Trait.js @@ -86,6 +86,12 @@ Trait.extend = function( dfn ) // just in case DFN does not contain any abstract members itself dfn[ 'abstract protected ___$$trait$$' ] = []; + // augment the parser to handle our own oddities + dfn.___$$parser$$ = { + each: _parseMember, + property: _parseProps, + }; + // give the abstract trait class a distinctive name for debugging dfn.__name = '#AbstractTrait#'; @@ -112,16 +118,6 @@ Trait.extend = function( dfn ) return ''+name; }; - // traits are not permitted to define constructors - if ( tclass.___$$methods$$['public'].__construct !== undefined ) - { - throw Error( "Traits may not define __construct" ); - } - - // traits have property restrictions - validateProps( tclass.___$$props$$['public'] ); - validateProps( tclass.___$$props$$['protected'] ); - // invoked to trigger mixin TraitType.__mixin = function( dfn, tc, base ) { @@ -138,30 +134,59 @@ Trait.extend = function( dfn ) }; +/** + * Verifies trait member restrictions + * + * @param {string} name property name + * @param {*} value property value + * @param {Object} keywords property keywords + * @param {Function} h original handler that we replaced + * + * @return {undefined} + */ +function _parseMember( name, value, keywords, h ) +{ + // traits are not permitted to define constructors + if ( name === '__construct' ) + { + throw Error( "Traits may not define __construct" ); + } + + // apply original handler + h.apply( this, arguments ); +} + + /** * Throws error if non-internal property is found within PROPS * * For details and rationale, see the Trait/PropertyTest case. * - * @param {Object} props properties to prohibit + * @param {string} name property name + * @param {*} value property value + * @param {Object} keywords property keywords + * @param {Function} h original handler that we replaced * * @return {undefined} */ -function validateProps( props ) +function _parseProps( name, value, keywords, h ) { - for ( var f in props ) + // ignore internal properties + if ( name.substr( 0, 3 ) === '___' ) { - // ignore internal properties - if ( f.substr( 0, 3 ) === '___' ) - { - continue; - } + return; + } + if ( !( keywords['private'] ) ) + { throw Error( - "Cannot define property `" + f + "'; only private " + + "Cannot define property `" + name + "'; only private " + "properties are permitted within Trait definitions" ); } + + // apply original handler + h.apply( this, arguments ); }