From 77efd515deef1671b69a170516e07b9565dd4b7a Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sat, 29 Mar 2014 00:40:36 -0400 Subject: [PATCH] Various argument handling optimizations Permits more aggressive v8 optimization. --- lib/ClassBuilder.js | 12 +++-- lib/class.js | 112 +++++++++++++++++++++++++----------------- lib/class_abstract.js | 18 +++---- lib/class_final.js | 16 ++---- lib/interface.js | 7 +-- lib/util.js | 7 ++- 6 files changed, 95 insertions(+), 77 deletions(-) diff --git a/lib/ClassBuilder.js b/lib/ClassBuilder.js index 1642beb..369d664 100644 --- a/lib/ClassBuilder.js +++ b/lib/ClassBuilder.js @@ -299,9 +299,10 @@ exports.prototype.build = function extend( _, __ ) // ensure we'll be permitted to instantiate abstract classes for the base this._extending = true; - var args = Array.prototype.slice.call( arguments ), - props = args.pop() || {}, - base = args.pop() || exports.ClassBase, + var a = arguments, + an = a.length, + props = ( ( an > 0 ) ? a[ an - 1 ] : 0 ) || {}, + base = ( ( an > 1 ) ? a[ an - 2 ] : 0 ) || exports.ClassBase, prototype = this._getBase( base ), cname = '', autoa = false, @@ -508,7 +509,10 @@ exports.prototype.buildMembers = function buildMembers( { handlers[ name ] = function() { - var args = Array.prototype.slice.call( arguments ); + var args = [], + i = arguments.length; + + while ( i-- ) args[ i ] = arguments[ i ]; // invoke the custom handler with the original handler as // its last argument (which the custom handler may choose diff --git a/lib/class.js b/lib/class.js index 8a5e23d..17b76d9 100644 --- a/lib/class.js +++ b/lib/class.js @@ -64,19 +64,24 @@ var _nullf = function() { return null; } module.exports = function( namedef, def ) { var type = ( typeof namedef ), - result = null + result = null, + args = [], + i = arguments.length ; + // passing arguments object prohibits optimizations in v8 + while ( i-- ) args[ i ] = arguments[ i ]; + switch ( type ) { // anonymous class case 'object': - result = createAnonymousClass.apply( null, arguments ); + result = createAnonymousClass.apply( null, args ); break; // named class case 'string': - result = createNamedClass.apply( null, arguments ); + result = createNamedClass.apply( null, args ); break; default: @@ -99,10 +104,7 @@ module.exports = function( namedef, def ) * * @return {Function} extended class */ -module.exports.extend = function( baseordfn, dfn ) -{ - return extend.apply( this, arguments ); -}; +module.exports.extend = extend; /** @@ -138,11 +140,11 @@ module.exports.implement = function( interfaces ) */ module.exports.use = function( traits ) { + var args = [], i = arguments.length; + while( i-- ) args[ i ] = arguments[ i ]; + // consume traits onto an empty base - return createUse( - _nullf, - Array.prototype.slice.call( arguments ) - ); + return createUse( _nullf, args ); }; @@ -292,7 +294,10 @@ function createStaging( cname ) return { extend: function() { - var args = Array.prototype.slice.apply( arguments ); + var args = [], + i = arguments.length; + + while ( i-- ) args[ i ] = arguments[ i ]; // extend() takes a maximum of two arguments. If only one // argument is provided, then it is to be the class definition. @@ -308,21 +313,24 @@ function createStaging( cname ) implement: function() { + var args = [], + i = arguments.length; + + while ( i-- ) args[ i ] = arguments[ i ]; + // implement on empty base, providing the class name to be used once // extended - return createImplement( - null, - Array.prototype.slice.call( arguments ), - cname - ); + return createImplement( null, args, cname ); }, use: function() { - return createUse( - _nullf, - Array.prototype.slice.call( arguments ) - ); + var args = [], + i = arguments.length; + + while ( i-- ) args[ i ] = arguments[ i ]; + + return createUse( _nullf, args ); }, }; } @@ -349,14 +357,14 @@ function createImplement( base, ifaces, cname ) var partial = { extend: function() { - var args = Array.prototype.slice.call( arguments ), - def = args.pop(), - ext_base = args.pop() + var an = arguments.length, + def = arguments[ an - 1 ], + ext_base = ( an > 1 ) ? arguments[ an - 2 ] : null ; // if any arguments remain, then they likely misunderstood what this // method does - if ( args.length > 0 ) + if ( an > 2 ) { throw Error( "Expecting no more than two arguments for extend()" @@ -394,7 +402,12 @@ function createImplement( base, ifaces, cname ) // much more performant (it creates a subtype before mixing in) use: function() { - var traits = Array.prototype.slice.call( arguments ); + var traits = [], + i = arguments.length; + + // passing arguments object prohibits optimizations in v8 + while ( i-- ) traits[ i ] = arguments[ i ]; + return createUse( function() { return partial.__createBase(); }, traits @@ -468,9 +481,9 @@ function createUse( basef, traits, nonbase ) // given during the extend operation partial.extend = function() { - var args = Array.prototype.slice.call( arguments ), - dfn = args.pop(), - ext_base = args.pop(), + var an = arguments.length, + dfn = arguments[ an - 1 ], + ext_base = ( an > 1 ) ? arguments[ an - 2 ] : null, base = basef(); // extend the mixed class, which ensures that all super references @@ -485,12 +498,17 @@ function createUse( basef, traits, nonbase ) // call simply to mix in another trait partial.use = function() { + var args = [], + i = arguments.length; + + while ( i-- ) args[ i ] = arguments[ i ]; + return createUse( function() { return partial.__createBase(); }, - Array.prototype.slice.call( arguments ), + args, nonbase ); }; @@ -557,8 +575,14 @@ function createMixedClass( base, traits ) */ function extend( _, __ ) { + var args = [], + i = arguments.length; + + // passing arguments object prohibits optimizations in v8 + while ( i-- ) args[ i ] = arguments[ i ]; + // set up the new class - var new_class = class_builder.build.apply( class_builder, arguments ); + var new_class = class_builder.build.apply( class_builder, args ); // set up some additional convenience props setupProps( new_class ); @@ -584,10 +608,9 @@ function extend( _, __ ) */ var implement = function( baseobj, interfaces ) { - var args = Array.prototype.slice.call( arguments ), + var an = arguments.length, dest = {}, - base = args.pop(), - len = args.length, + base = arguments[ an - 1 ], arg = null, implemented = [], @@ -595,9 +618,9 @@ var implement = function( baseobj, interfaces ) ; // add each of the interfaces - for ( var i = 0; i < len; i++ ) + for ( var i = 0; i < ( an - 1 ); i++ ) { - arg = args[ i ]; + arg = arguments[ i ]; // copy all interface methods to the class (does not yet deep copy) util.propParse( arg.prototype, { @@ -678,10 +701,10 @@ function attachImplement( func ) { util.defineSecureProp( func, 'implement', function() { - return createImplement( - func, - Array.prototype.slice.call( arguments ) - ); + var args = [], i = arguments.length; + while( i-- ) args[ i ] = arguments[ i ]; + + return createImplement( func, args ); }); } @@ -699,11 +722,10 @@ function attachUse( func ) { util.defineSecureProp( func, 'use', function() { - return createUse( - function() { return func; }, - Array.prototype.slice.call( arguments ), - true - ); + var args = [], i = arguments.length; + while( i-- ) args[ i ] = arguments[ i ]; + + return createUse( function() { return func; }, args, true ); } ); } diff --git a/lib/class_abstract.js b/lib/class_abstract.js index f01ae6a..1128f1e 100644 --- a/lib/class_abstract.js +++ b/lib/class_abstract.js @@ -5,7 +5,7 @@ * flag to allow abstract methods within a class, forcing users to clearly * state that a class is abstract. * - * Copyright (C) 2010, 2011, 2013, 2014 Mike Gerwitz + * Copyright (C) 2010, 2011, 2013 Mike Gerwitz * * This file is part of GNU ease.js. * @@ -33,7 +33,7 @@ var Class = require( __dirname + '/class' ); */ module.exports = exports = function() { - markAbstract( arguments ); + markAbstract( arguments[ arguments.length - 1 ] ); // forward everything to Class var result = Class.apply( this, arguments ); @@ -56,7 +56,7 @@ module.exports = exports = function() */ exports.extend = function() { - markAbstract( arguments ); + markAbstract( arguments[ arguments.length - 1 ] ); return Class.extend.apply( this, arguments ); }; @@ -92,18 +92,12 @@ exports.implement = function() /** * Causes a definition to be flagged as abstract * - * This function assumes the last argument to be the definition, which is the - * common case, and modifies the object referenced by that argument. - * - * @param {Arguments} args arguments to parse + * @param {*} dfn suspected definition object * * @return {undefined} */ -function markAbstract( args ) +function markAbstract( dfn ) { - // the last argument _should_ be the definition - var dfn = args[ args.length - 1 ]; - if ( typeof dfn === 'object' ) { // mark as abstract @@ -141,7 +135,7 @@ function abstractOverride( obj ) // wrap extend, applying the abstract flag obj.extend = function() { - markAbstract( arguments ); + markAbstract( arguments[ arguments.length - 1 ] ); return extend.apply( this, arguments ); }; diff --git a/lib/class_final.js b/lib/class_final.js index 4449bb5..130afc8 100644 --- a/lib/class_final.js +++ b/lib/class_final.js @@ -29,7 +29,7 @@ var Class = require( __dirname + '/class' ); */ exports = module.exports = function() { - markFinal( arguments ); + markFinal( arguments[ arguments.length - 1 ] ); // forward everything to Class var result = Class.apply( this, arguments ); @@ -50,7 +50,7 @@ exports = module.exports = function() */ exports.extend = function() { - markFinal( arguments ); + markFinal( arguments[ arguments.length - 1 ] ); return Class.extend.apply( this, arguments ); }; @@ -58,18 +58,12 @@ exports.extend = function() /** * Causes a definition to be flagged as final * - * This function assumes the last argument to be the definition, which is the - * common case, and modifies the object referenced by that argument. - * - * @param {!Arguments} args arguments to parse + * @param {!Arguments} dfn suspected definition object * * @return {undefined} */ -function markFinal( args ) +function markFinal( dfn ) { - // the last argument _should_ be the definition - var dfn = args[ args.length - 1 ]; - if ( typeof dfn === 'object' ) { // mark as abstract @@ -92,7 +86,7 @@ function finalOverride( obj ) // wrap extend, applying the abstract flag obj.extend = function() { - markFinal( arguments ); + markFinal( arguments[ arguments.length - 1 ] ); return extend.apply( this, arguments ); }; } diff --git a/lib/interface.js b/lib/interface.js index 52162be..a4b8983 100644 --- a/lib/interface.js +++ b/lib/interface.js @@ -184,9 +184,10 @@ var extend = ( function( extending ) // ensure we'll be permitted to instantiate interfaces for the base extending = true; - var args = Array.prototype.slice.call( arguments ), - props = args.pop() || {}, - base = args.pop() || Interface, + var a = arguments, + an = a.length, + props = ( ( an > 0 ) ? a[ an - 1 ] : 0 ) || {}, + base = ( ( an > 1 ) ? a[ an - 2 ] : 0 ) || Interface, prototype = new base(), iname = '', diff --git a/lib/util.js b/lib/util.js index d138b17..8ca7c56 100644 --- a/lib/util.js +++ b/lib/util.js @@ -399,7 +399,10 @@ function verifyAbstractNames( name, params ) */ exports.createAbstractMethod = function( def ) { - var definition = Array.prototype.slice.call( arguments ); + var dfn = [], + i = arguments.length; + + while ( i-- ) dfn[ i ] = arguments[ i ]; var method = function() { @@ -407,7 +410,7 @@ exports.createAbstractMethod = function( def ) }; exports.defineSecureProp( method, 'abstractFlag', true ); - exports.defineSecureProp( method, 'definition', definition ); + exports.defineSecureProp( method, 'definition', dfn ); exports.defineSecureProp( method, '__length', arguments.length ); return method;