From fd95f38c874b136b95d967fed019c12449d08758 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sat, 13 Aug 2011 23:58:08 -0400 Subject: [PATCH] Integrated VisibilityObjectFactory and removed old propobj (#25) - Note that the excessive gluing is temporary --- lib/ClassBuilder.js | 66 +++--- lib/class.js | 4 +- lib/propobj.js | 221 ------------------ test/test-class-visibility.js | 8 +- test/test-class_builder-const.js | 3 +- test/test-class_builder-final.js | 3 +- .../test-class_builder-member-restrictions.js | 3 +- test/test-class_builder-static.js | 3 +- test/test-class_builder-visibility.js | 3 +- test/test-member_builder-method-hiding.js | 3 +- tools/combine | 2 +- 11 files changed, 60 insertions(+), 259 deletions(-) delete mode 100644 lib/propobj.js diff --git a/lib/ClassBuilder.js b/lib/ClassBuilder.js index b4e9ab8..bd11a30 100644 --- a/lib/ClassBuilder.js +++ b/lib/ClassBuilder.js @@ -29,7 +29,6 @@ var util = require( __dirname + '/util' ), warn = require( __dirname + '/warn' ), - propobj = require( __dirname + '/propobj' ), Warning = warn.Warning, /** @@ -80,15 +79,18 @@ var util = require( __dirname + '/util' ), * * The 'new' keyword is not required when instantiating this constructor. * - * @param {Object} member_builder member builder + * @param {Object} member_builder member builder + * + * @param {VisibilityObjectFacotry} visibility_factory visibility object + * generator */ module.exports = exports = -function ClassBuilder( member_builder ) +function ClassBuilder( member_builder, visibility_factory ) { // allow ommitting the 'new' keyword if ( !( this instanceof exports ) ) { - return new exports( member_builder ); + return new exports( member_builder, visibility_factory ); } /** @@ -97,6 +99,12 @@ function ClassBuilder( member_builder ) */ this._memberBuilder = member_builder; + /** + * Generates visibility object + * @type {VisibilityObjectFactory} + */ + this._visFactory = visibility_factory; + /** * Class id counter, to be increment on each new definition @@ -369,7 +377,9 @@ exports.prototype.build = function extend() } staticInit( new_class, false ); - attachPropInit( prototype, prop_init, members, new_class, this._classId ); + this._attachPropInit( + prototype, prop_init, members, new_class, this._classId + ); new_class.prototype = prototype; new_class.constructor = new_class; @@ -709,24 +719,6 @@ exports.prototype.createAbstractCtor = function( cname ) } -/** - * Determines if the given keywords should result in a static member - * - * A member will be considered static if the static or const keywords are given. - * - * @param {Object} keywords keywords to scan - * - * @return {bool} true if to be static, otherwise false - */ -function keywordStatic( keywords ) -{ - return ( keywords[ 'static' ] || keywords[ 'const' ] ) - ? true - : false - ; -} - - /** * Attaches __initProps() method to the class prototype * @@ -748,8 +740,12 @@ function keywordStatic( keywords ) * * @return {undefined} */ -function attachPropInit( prototype, properties, members, ctor, cid ) +exports.prototype._attachPropInit = function( + prototype, properties, members, ctor, cid +) { + var _self = this; + util.defineSecureProp( prototype, '__initProps', function( inherit ) { // defaults to false @@ -770,7 +766,7 @@ function attachPropInit( prototype, properties, members, ctor, cid ) // this will return our property proxy, if supported by our environment, // otherwise just a normal object with everything merged in - var inst_props = propobj.createPropProxy( + var inst_props = _self._visFactory.createPropProxy( this, this.___$$vis$$, properties[ 'public' ] ); @@ -779,7 +775,7 @@ function attachPropInit( prototype, properties, members, ctor, cid ) // chain and is returned. This is stored in a property referenced by the // class id, so that the private members can be swapped on each method // request, depending on calling context. - var vis = this.___$$vis$$[ cid ] = propobj.setup( + var vis = this.___$$vis$$[ cid ] = _self._visFactory.setup( inst_props, properties, members ); @@ -798,6 +794,24 @@ function attachPropInit( prototype, properties, members, ctor, cid ) } +/** + * Determines if the given keywords should result in a static member + * + * A member will be considered static if the static or const keywords are given. + * + * @param {Object} keywords keywords to scan + * + * @return {bool} true if to be static, otherwise false + */ +function keywordStatic( keywords ) +{ + return ( keywords[ 'static' ] || keywords[ 'const' ] ) + ? true + : false + ; +} + + /** * Creates and populates the static visibility object * diff --git a/lib/class.js b/lib/class.js index 88391f8..0c1a118 100644 --- a/lib/class.js +++ b/lib/class.js @@ -26,7 +26,9 @@ var util = require( __dirname + '/util' ), ClassBuilder = require( __dirname + '/ClassBuilder' ), class_builder = ClassBuilder( - require( __dirname + '/member_builder' ) + require( __dirname + '/member_builder' ), + require( __dirname + '/VisibilityObjectFactoryFactory' ) + .fromEnvironment() ) ; diff --git a/lib/propobj.js b/lib/propobj.js deleted file mode 100644 index 0585ca1..0000000 --- a/lib/propobj.js +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Contains property object generator - * - * Copyright (C) 2010 Mike Gerwitz - * - * This file is part of ease.js. - * - * ease.js is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - * - * @author Mike Gerwitz - * @package core - */ - -var util = require( __dirname + '/util' ), - - // whether or not we support defining properties through - // Object.defineProperty() - defprop = !( util.definePropertyFallback() ); -; - - -/** - * Sets up properties (non-inheriting) - * - * This includes all members (including private). Private members will be set up - * in a separate object, so that they can be easily removed from the mix. That - * object will include the destination object in the prototype, so that the - * access should be transparent. This object is returned. - * - * Properties are expected in the following format. Note that keywords are - * ignored: - * { public: { prop: [ value, { keyword: true } ] } } - * - * @param {Object} dest destination object - * @param {Object} properties properties to copy - * @param {Object=} methods methods to copy - * - * @return {Object} object containing private members and dest as prototype - */ -exports.setup = function( dest, properties, methods ) -{ - var obj = dest; - - // this constructor is an extra layer atop of the destination object, which - // will contain the private methods - if ( defprop ) - { - var obj_ctor = function() {}; - obj_ctor.prototype = dest; - - obj = new obj_ctor(); - - // all private protected proxies need to be proxied from the private - // object (which will be passed as the context) to the object containing - // protected values - exports.createPropProxy( dest, obj, properties[ 'protected' ] ); - } - - // initialize each of the properties for this instance to - // ensure we're not sharing references to prototype values - doSetup( dest, properties[ 'public' ] ); - - // Do the same for protected, but only if they do not exist already in - // public. The reason for this is because the property object is laid /atop/ - // of the public members, meaning that a parent's protected members will - // take precedence over a subtype's overriding /public/ members. Uh oh. - doSetup( dest, - properties[ 'protected' ], - methods[ 'protected' ], - 'public' - ); - - // then add the private parts - doSetup( obj, properties[ 'private' ], methods[ 'private' ] ); - - return obj; -}; - - -/** - * Set up destination object by copying over properties and methods - * - * @param {Object} dest destination object - * @param {Object} properties properties to copy - * @param {Object=} methods methods to copy - * @param {boolean} unless_keyword do not set if keyword is set on existing - * method - * - * @return {undefined} - */ -function doSetup( dest, properties, methods, unless_keyword ) -{ - var hasOwn = Array.prototype.hasOwnProperty, - pre = null; - - // copy over the methods - if ( methods !== undefined ) - { - for ( method_name in methods ) - { - if ( hasOwn.call( methods, method_name ) ) - { - pre = dest[ method_name ]; - - // If requested, do not copy the method over if it already - // exists in the destination object. Don't use hasOwn here; - // unnecessary overhead and we want to traverse any prototype - // chains. We do not check the public object directly, for - // example, because we need a solution that will work if a proxy - // is unsupported by the engine. - // - // Also note that we need to allow overriding if it exists in - // the protected object (we can override protected with - // protected). This is the *last* check to ensure a performance - // hit is incured *only* if we're overriding protected with - // protected. - if ( !unless_keyword - || ( pre === undefined ) - || !( pre.___$$keywords$$[ unless_keyword ] ) - ) - { - dest[ method_name ] = methods[ method_name ]; - } - } - } - } - - // initialize private/protected properties and store in instance data - for ( prop in properties ) - { - if ( hasOwn.call( properties, prop ) ) - { - dest[ prop ] = util.clone( properties[ prop ][ 0 ] ); - } - } -} - - -/** - * Creates a proxy for all given properties to the given base - * - * The proxy uses getters/setters to forward all calls to the base. The - * destination object will be used as the proxy. All properties within props - * will be used proxied. - * - * To summarize: for each property in props, all gets and sets will be forwarded - * to base. - * - * @param {Object} base object to proxy to - * @param {Object} dest object to treat as proxy (set getters/setters on) - * @param {Object} props properties to proxy - * - * @return {Object} returns dest - */ -exports.createPropProxy = function( base, dest, props ) -{ - var hasOwn = Object.prototype.hasOwnProperty; - - if ( !defprop ) - { - return base; - } - - for ( prop in props ) - { - if ( !( hasOwn.call( props, prop ) ) ) - { - continue; - } - - ( function( prop ) - { - // just in case it's already defined, so we don't throw an error - dest[ prop ] = undefined; - - // public properties, when set internally, must forward to the - // actual variable - Object.defineProperty( dest, prop, { - set: function( val ) - { - base[ prop ] = val; - }, - - get: function() - { - return base[ prop ]; - }, - - enumerable: true, - } ); - } ).call( null, prop ); - } - - return dest; -}; - - -/** - * Returns whether property proxying is supported - * - * Proxying is done via getters and setters. If the JS engine doesn't support - * them (pre-ES5), then the proxy will not work. - * - * @return {boolean} true if supported, otherwise false - */ -exports.supportsPropProxy = function() -{ - return defprop; -}; - diff --git a/test/test-class-visibility.js b/test/test-class-visibility.js index 2eb8ad9..e26ef5c 100644 --- a/test/test-class-visibility.js +++ b/test/test-class-visibility.js @@ -26,7 +26,7 @@ var common = require( './common' ), assert = require( 'assert' ), Class = common.require( 'class' ), Interface = common.require( 'interface' ), - propobj = common.require( 'propobj' ), + util = common.require( 'util' ), pub = 'foo', prot = 'bar', @@ -221,7 +221,7 @@ var common = require( './common' ), { // browsers that do not support the property proxy will not support // encapsulating properties - if ( !( propobj.supportsPropProxy() ) ) + if ( util.definePropertyFallback() ) { return; } @@ -322,7 +322,7 @@ var common = require( './common' ), { // browsers that do not support the property proxy will not support // encapsulating properties - if ( !( propobj.supportsPropProxy() ) ) + if ( util.definePropertyFallback() ) { return; } @@ -482,7 +482,7 @@ var common = require( './common' ), { // browsers that do not support the property proxy will not support // encapsulating properties - if ( !( propobj.supportsPropProxy() ) ) + if ( util.definePropertyFallback() ) { return; } diff --git a/test/test-class_builder-const.js b/test/test-class_builder-const.js index 57565d1..c61ad15 100644 --- a/test/test-class_builder-const.js +++ b/test/test-class_builder-const.js @@ -25,7 +25,8 @@ var common = require( './common' ), assert = require( 'assert' ), builder = common.require( 'ClassBuilder' )( - common.require( 'member_builder' ) + common.require( 'member_builder' ), + common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/test/test-class_builder-final.js b/test/test-class_builder-final.js index a7d1dfb..b504074 100644 --- a/test/test-class_builder-final.js +++ b/test/test-class_builder-final.js @@ -25,7 +25,8 @@ var common = require( './common' ), assert = require( 'assert' ), builder = common.require( 'ClassBuilder' )( - common.require( 'member_builder' ) + common.require( 'member_builder' ), + common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() ), Class = common.require( 'class' ) diff --git a/test/test-class_builder-member-restrictions.js b/test/test-class_builder-member-restrictions.js index 96baca9..1b501be 100644 --- a/test/test-class_builder-member-restrictions.js +++ b/test/test-class_builder-member-restrictions.js @@ -27,7 +27,8 @@ var common = require( './common' ), ClassBuilder = common.require( 'ClassBuilder' ), builder = ClassBuilder( - common.require( 'member_builder' ) + common.require( 'member_builder' ), + common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/test/test-class_builder-static.js b/test/test-class_builder-static.js index 3c23cfe..13e2bd9 100644 --- a/test/test-class_builder-static.js +++ b/test/test-class_builder-static.js @@ -26,7 +26,8 @@ var common = require( './common' ), assert = require( 'assert' ), fallback = common.require( 'util' ).definePropertyFallback() builder = common.require( 'ClassBuilder' )( - common.require( 'member_builder' ) + common.require( 'member_builder' ), + common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/test/test-class_builder-visibility.js b/test/test-class_builder-visibility.js index d7800fc..c3dd3da 100644 --- a/test/test-class_builder-visibility.js +++ b/test/test-class_builder-visibility.js @@ -28,7 +28,8 @@ var common = require( './common' ), assert = require( 'assert' ), util = common.require( 'util' ), builder = common.require( 'ClassBuilder' )( - common.require( 'member_builder' ) + common.require( 'member_builder' ), + common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/test/test-member_builder-method-hiding.js b/test/test-member_builder-method-hiding.js index 0eca016..5871099 100644 --- a/test/test-member_builder-method-hiding.js +++ b/test/test-member_builder-method-hiding.js @@ -26,7 +26,8 @@ var common = require( './common' ), assert = require( 'assert' ), warn = common.require( 'warn' ) builder = common.require( 'ClassBuilder' )( - common.require( 'member_builder' ) + common.require( 'member_builder' ), + common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/tools/combine b/tools/combine index 887b628..e975c0d 100755 --- a/tools/combine +++ b/tools/combine @@ -29,7 +29,7 @@ TPL_VAR='/**{CONTENT}**/' RMTRAIL="$PATH_TOOLS/rmtrail" # order matters -CAT_MODS="warn prop_parser util propobj VisibilityObjectFactory" +CAT_MODS="warn prop_parser util VisibilityObjectFactory" CAT_MODS="$CAT_MODS FallbackVisibilityObjectFactory member_builder" CAT_MODS="$CAT_MODS ClassBuilder VisibilityObjectFactoryFactory" CAT_MODS="$CAT_MODS class class_final class_abstract interface"