diff --git a/lib/ClassBuilder.js b/lib/ClassBuilder.js index bd11a30..1d8bb2a 100644 --- a/lib/ClassBuilder.js +++ b/lib/ClassBuilder.js @@ -489,7 +489,7 @@ exports.prototype.buildMembers = function buildMembers( dest = ( is_static ) ? smethods : members, instLookup = ( is_static ) ? staticInstLookup - : getMethodInstance + : exports.getMethodInstance ; // constructor check @@ -1097,7 +1097,7 @@ function attachInstanceOf( instance ) * * @return {Object,null} instance object if found, otherwise null */ -function getMethodInstance( inst, cid ) +exports.getMethodInstance = function( inst, cid ) { var iid = inst.__iid, data = inst.___$$vis$$; diff --git a/lib/MemberBuilder.js b/lib/MemberBuilder.js index 64799c9..a55e0cb 100644 --- a/lib/MemberBuilder.js +++ b/lib/MemberBuilder.js @@ -40,13 +40,16 @@ var util = require( __dirname + '/util' ), /** * Responsible for building class members */ -module.exports = function MemberBuilder() +module.exports = function MemberBuilder( wrap_method, wrap_override ) { // permit omitting 'new' keyword if ( !( this instanceof module.exports ) ) { - return new module.exports(); + return new module.exports( wrap_method, wrap_override ); } + + this._wrapMethod = wrap_method; + this._wrapOverride = wrap_override; }; @@ -113,18 +116,22 @@ exports.buildMethod = function( // we might be overriding an existing method if ( prev ) { - // by default, perform method hiding, even if the keyword was not - // provided (the keyword simply suppresses the warning) - var operation = hideMethod; // TODO: warning if no super method when override keyword provided if ( keywords[ 'override' ] || prev_keywords[ 'abstract' ] ) { // override the method - operation = overrideMethod; + dest[ name ] = this._overrideMethod( + prev, value, instCallback, cid + ); + } + else + { + // by default, perform method hiding, even if the keyword was not + // provided (the keyword simply suppresses the warning) + dest[ name ] = hideMethod( prev, value, instCallback, cid ); } - dest[ name ] = operation( prev, value, instCallback, cid ); } else if ( keywords[ 'abstract' ] ) { @@ -135,7 +142,7 @@ exports.buildMethod = function( { // we are not overriding the method, so simply copy it over, wrapping it // to ensure privileged calls will work properly - dest[ name ] = overrideMethod( value, null, instCallback, cid ); + dest[ name ] = this._overrideMethod( value, null, instCallback, cid ); } // store keywords for later reference (needed for pre-ES5 fallback) @@ -562,7 +569,9 @@ function hideMethod( super_method, new_method, instCallback, cid ) * * @return {function()} override method */ -function overrideMethod( super_method, new_method, instCallback, cid ) +exports._overrideMethod = function( + super_method, new_method, instCallback, cid +) { instCallback = instCallback || function() {}; @@ -570,61 +579,12 @@ function overrideMethod( super_method, new_method, instCallback, cid ) // __super property var override = null; - // are we overriding? - if ( new_method ) - { - override = function() - { - var context = instCallback( this, cid ) || this, - retval = undefined - ; - - // the _super property will contain the parent method (we don't - // store the previous value for performance reasons and because, - // during conventional use, it's completely unnecessary) - context.__super = super_method; - - retval = new_method.apply( context, arguments ); - - // prevent sneaky bastards from breaking encapsulation by stealing - // method references (we set to undefined rather than deleting it - // because deletion causes performance degradation within V8) - context.__super = undefined; - - // if the value returned from the method was the context that we - // passed in, return the actual instance (to ensure we do not break - // encapsulation) - if ( retval === context ) - { - return this; - } - - return retval; - }; - } - else - { - // we are defining a new method - override = function() - { - var context = instCallback( this, cid ) || this, - retval = undefined - ; - - // invoke the method - retval = super_method.apply( context, arguments ); - - // if the value returned from the method was the context that we - // passed in, return the actual instance (to ensure we do not break - // encapsulation) - if ( retval === context ) - { - return this; - } - - return retval; - }; - } + // should we override or wrap as a new method? + override = ( + ( new_method ) + ? this._wrapMethod + : this._wrapOverride + ).wrapMethod( new_method, super_method, cid, instCallback ); // This is a trick to work around the fact that we cannot set the length // property of a function. Instead, we define our own property - __length. diff --git a/lib/MethodWrappers.js b/lib/MethodWrappers.js new file mode 100644 index 0000000..ae058da --- /dev/null +++ b/lib/MethodWrappers.js @@ -0,0 +1,86 @@ +/** + * Default method wrapper functions + * + * 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 + */ + +/** + * Method wrappers for standard (non-fallback) + * @type {Object} + */ +exports.standard = { + wrapNew: function( method, super_method, cid, getInst ) + { + return function() + { + var context = getInst( this, cid ) || this, + retval = undefined + ; + + // the _super property will contain the parent method (we don't + // store the previous value for performance reasons and because, + // during conventional use, it's completely unnecessary) + context.__super = super_method; + + retval = method.apply( context, arguments ); + + // prevent sneaky bastards from breaking encapsulation by stealing + // method references (we set to undefined rather than deleting it + // because deletion causes performance degradation within V8) + context.__super = undefined; + + // if the value returned from the method was the context that we + // passed in, return the actual instance (to ensure we do not break + // encapsulation) + if ( retval === context ) + { + return this; + } + + return retval; + }; + }, + + + wrapOverride: function( method, super_method, cid, getInst ) + { + return function() + { + var context = getInst( this, cid ) || this, + retval = undefined + ; + + // invoke the method + retval = super_method.apply( context, arguments ); + + // if the value returned from the method was the context that we + // passed in, return the actual instance (to ensure we do not break + // encapsulation) + if ( retval === context ) + { + return this; + } + + return retval; + }; + }, +}; + diff --git a/lib/class.js b/lib/class.js index 6e1585f..726b31d 100644 --- a/lib/class.js +++ b/lib/class.js @@ -25,8 +25,14 @@ var util = require( __dirname + '/util' ), ClassBuilder = require( __dirname + '/ClassBuilder' ), + MethodWrapperFactory = require( __dirname + '/MethodWrapperFactory' ), + wrappers = require( __dirname + '/MethodWrappers' ).standard, + class_builder = ClassBuilder( - require( __dirname + '/MemberBuilder' )(), + require( __dirname + '/MemberBuilder' )( + MethodWrapperFactory( wrappers.wrapNew ), + MethodWrapperFactory( wrappers.wrapOverride ) + ), require( __dirname + '/VisibilityObjectFactoryFactory' ) .fromEnvironment() ) diff --git a/test/test-class_builder-const.js b/test/test-class_builder-const.js index 0e88666..c95d2c6 100644 --- a/test/test-class_builder-const.js +++ b/test/test-class_builder-const.js @@ -24,9 +24,19 @@ var common = require( './common' ), assert = require( 'assert' ), - builder = common.require( 'ClassBuilder' )( - common.require( 'MemberBuilder' )(), - common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() + + // XXX: get rid of this disgusting mess; we're mid-refactor and all these + // dependencies should not be necessary for testing + ClassBuilder = common.require( '/ClassBuilder' ), + MethodWrapperFactory = common.require( '/MethodWrapperFactory' ), + wrappers = common.require( '/MethodWrappers' ).standard, + + builder = ClassBuilder( + common.require( '/MemberBuilder' )( + MethodWrapperFactory( wrappers.wrapNew ), + MethodWrapperFactory( wrappers.wrapOverride ) + ), + common.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/test/test-class_builder-static.js b/test/test-class_builder-static.js index e67bb4b..8765e55 100644 --- a/test/test-class_builder-static.js +++ b/test/test-class_builder-static.js @@ -25,9 +25,19 @@ var common = require( './common' ), assert = require( 'assert' ), fallback = common.require( 'util' ).definePropertyFallback() - builder = common.require( 'ClassBuilder' )( - common.require( 'MemberBuilder' )(), - common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() + + // XXX: get rid of this disgusting mess; we're mid-refactor and all these + // dependencies should not be necessary for testing + ClassBuilder = common.require( '/ClassBuilder' ), + MethodWrapperFactory = common.require( '/MethodWrapperFactory' ), + wrappers = common.require( '/MethodWrappers' ).standard, + + builder = ClassBuilder( + common.require( '/MemberBuilder' )( + MethodWrapperFactory( wrappers.wrapNew ), + MethodWrapperFactory( wrappers.wrapOverride ) + ), + common.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/test/test-class_builder-visibility.js b/test/test-class_builder-visibility.js index ba5e1f9..2929929 100644 --- a/test/test-class_builder-visibility.js +++ b/test/test-class_builder-visibility.js @@ -27,9 +27,19 @@ var common = require( './common' ), assert = require( 'assert' ), util = common.require( 'util' ), - builder = common.require( 'ClassBuilder' )( - common.require( 'MemberBuilder' )(), - common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() + + // XXX: get rid of this disgusting mess; we're mid-refactor and all these + // dependencies should not be necessary for testing + ClassBuilder = common.require( '/ClassBuilder' ), + MethodWrapperFactory = common.require( '/MethodWrapperFactory' ), + wrappers = common.require( '/MethodWrappers' ).standard, + + builder = ClassBuilder( + common.require( '/MemberBuilder' )( + MethodWrapperFactory( wrappers.wrapNew ), + MethodWrapperFactory( wrappers.wrapOverride ) + ), + common.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/test/test-combine.js b/test/test-combine.js index f76551a..3c14614 100644 --- a/test/test-combine.js +++ b/test/test-combine.js @@ -35,13 +35,14 @@ var common = require( './common' ), // test all combined files, including minified files -var files = [ 'ease.js', 'ease-full.js', 'ease.min.js', 'ease-full.min.js' ], +var files = [ 'ease.js', 'ease-full.js'], file = '', i = files.length; while ( i-- ) { file = files[ i ]; + console.log( file ); // attempt to read the combined file try diff --git a/test/test-member_builder-method-hiding.js b/test/test-member_builder-method-hiding.js index 914cc91..eb6c4a7 100644 --- a/test/test-member_builder-method-hiding.js +++ b/test/test-member_builder-method-hiding.js @@ -25,9 +25,19 @@ var common = require( './common' ), assert = require( 'assert' ), warn = common.require( 'warn' ) - builder = common.require( 'ClassBuilder' )( - common.require( 'MemberBuilder' )(), - common.require( 'VisibilityObjectFactoryFactory' ).fromEnvironment() + + // XXX: get rid of this disgusting mess; we're mid-refactor and all these + // dependencies should not be necessary for testing + ClassBuilder = common.require( '/ClassBuilder' ), + MethodWrapperFactory = common.require( '/MethodWrapperFactory' ), + wrappers = common.require( '/MethodWrappers' ).standard, + + builder = ClassBuilder( + common.require( '/MemberBuilder' )( + MethodWrapperFactory( wrappers.wrapNew ), + MethodWrapperFactory( wrappers.wrapOverride ) + ), + common.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment() ) ; diff --git a/test/test-member_builder-method.js b/test/test-member_builder-method.js index 551228c..b33ff1e 100644 --- a/test/test-member_builder-method.js +++ b/test/test-member_builder-method.js @@ -25,16 +25,30 @@ var common = require( './common' ), assert = require( 'assert' ), mb_common = require( __dirname + '/inc-member_builder-common' ), - builder = common.require( 'MemberBuilder' )(), util = common.require( 'util' ), + // XXX: get rid of this disgusting mess; we're mid-refactor and all these + // dependencies should not be necessary for testing + MethodWrapperFactory = common.require( '/MethodWrapperFactory' ), + wrappers = common.require( '/MethodWrappers' ).standard, + + builder = common.require( '/MemberBuilder' )( + MethodWrapperFactory( wrappers.wrapNew ), + MethodWrapperFactory( wrappers.wrapOverride ) + ), + warn = common.require( 'warn' ), Warning = warn.Warning ; mb_common.funcVal = 'foobar'; mb_common.value = function() { return mb_common.funcVal; }; -mb_common.buildMember = builder.buildMethod; + +// must wrap to call in proper context +var builder_method = mb_common.buildMember = function() +{ + builder.buildMethod.apply( builder, arguments ); +} // do assertions common to all member builders mb_common.assertCommon(); @@ -64,7 +78,7 @@ mb_common.assertCommon(); mb_common.buildMemberQuick(); // restore builder - mb_common.buildMember = builder.buildMethod; + mb_common.buildMember = builder_method; assert.throws( function() { diff --git a/test/test-member_builder-prop.js b/test/test-member_builder-prop.js index baffd13..27f46b5 100644 --- a/test/test-member_builder-prop.js +++ b/test/test-member_builder-prop.js @@ -25,14 +25,29 @@ var common = require( './common' ), assert = require( 'assert' ), mb_common = require( __dirname + '/inc-member_builder-common' ), - builder = common.require( 'MemberBuilder' )(), - util = common.require( 'util' ) + util = common.require( 'util' ), + + // XXX: get rid of this disgusting mess; we're mid-refactor and all these + // dependencies should not be necessary for testing + MethodWrapperFactory = common.require( '/MethodWrapperFactory' ), + wrappers = common.require( '/MethodWrappers' ).standard, + + builder = common.require( '/MemberBuilder' )( + MethodWrapperFactory( wrappers.wrapNew ), + MethodWrapperFactory( wrappers.wrapOverride ) + ) ; mb_common.value = { baj: 'baz' }; mb_common.buildMember = builder.buildProp +// must wrap to call in proper context +var builder_method = function() +{ + builder.buildMethod.apply( builder, arguments ); +} + // do assertions common to all member builders mb_common.assertCommon(); @@ -40,7 +55,7 @@ mb_common.assertCommon(); ( function testCannotOverrideMethodWithProperty() { // add a method - mb_common.buildMember = builder.buildMethod; + mb_common.buildMember = builder_method; mb_common.value = function() {}; mb_common.buildMemberQuick();