1
0
Fork 0

Refactored new and override method wrappers into separate prototypes

- Note that, since we're mid-refactor, this is a bit of a mess
closure/master
Mike Gerwitz 2011-08-31 00:24:19 -04:00
parent af653aaff7
commit bc636637cc
11 changed files with 208 additions and 86 deletions

View File

@ -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$$;

View File

@ -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.

View File

@ -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 <http://www.gnu.org/licenses/>.
*
* @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;
};
},
};

View File

@ -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()
)

View File

@ -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()
)
;

View File

@ -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()
)
;

View File

@ -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()
)
;

View File

@ -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

View File

@ -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()
)
;

View File

@ -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()
{

View File

@ -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();