Miscellaneous performance enhancements
These are the beginning of some smaller performance optimizations brought on by the v8 profiler. This includes removal or movement of over-reaching try/catch blocks and more disciplined argument handling, neither of which can be compiled into machine code (permanently, at least). This also removes some unneeded code, adds some baseline performance test cases, and begins generic performance test output and HTML generation which will be used in the future for more detailed analysis. This is just a starting point; there's more to come, guided by profiling. The trait implementation needs some love and, since its development is not yet complete, that will be optimized in the near future. Further, there are additional optimizations that can be made when ease.js recognizes that certain visibility layers are unneeded, allowing it to create more lightweight classes. Performance enhancements will also introduce the ability to generate a ``compiled'' class, which will generate a prototype that can be immediately run without the overhead of processing keywords, etc. This will also have the benefit of generating code that can be understood by static analysis tools and, consequently, optimizers. All in good time.newmaster
commit
86a4703a1c
|
@ -10,6 +10,7 @@ ChangeLog
|
||||||
|
|
||||||
# autotools- and configure-generated
|
# autotools- and configure-generated
|
||||||
test/runner
|
test/runner
|
||||||
|
test/perf/runner
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
Makefile.in
|
Makefile.in
|
||||||
Makefile
|
Makefile
|
||||||
|
@ -17,6 +18,9 @@ Makefile
|
||||||
configure
|
configure
|
||||||
config.*
|
config.*
|
||||||
|
|
||||||
|
# script output
|
||||||
|
perf.*
|
||||||
|
|
||||||
# should be added using autoreconf -i
|
# should be added using autoreconf -i
|
||||||
INSTALL
|
INSTALL
|
||||||
tools/install-sh
|
tools/install-sh
|
||||||
|
|
11
Makefile.am
11
Makefile.am
|
@ -111,11 +111,14 @@ html-single:
|
||||||
test: check
|
test: check
|
||||||
check: $(src_tests) test-suite
|
check: $(src_tests) test-suite
|
||||||
|
|
||||||
# performance tests
|
# performance tests (order matters here)
|
||||||
perf: @PERF_TESTS@
|
perf-html: perf.log.html
|
||||||
perf-%.js: FORCE
|
perf.%.html: perf.%
|
||||||
|
sort "$<" | $(path_tools)/perf2html -F\| > "$@"
|
||||||
|
perf: perf.log
|
||||||
|
perf.%: FORCE
|
||||||
if HAS_NODE
|
if HAS_NODE
|
||||||
@$(NODE) $@
|
@$(path_test)/perf/runner @PERF_TESTS@ > "$@"
|
||||||
else
|
else
|
||||||
@echo "Node.js must be installed in order to run performance tests"
|
@echo "Node.js must be installed in order to run performance tests"
|
||||||
@exit 1
|
@exit 1
|
||||||
|
|
|
@ -87,7 +87,8 @@ PERF_TESTS=$( find test/perf -name 'perf-*.js' | tr '\n' ' ' )
|
||||||
AC_SUBST(PERF_TESTS)
|
AC_SUBST(PERF_TESTS)
|
||||||
AS_IF([test "$PERF_TESTS"], [AC_MSG_RESULT(ok)], [AC_MSG_WARN(none found)])
|
AS_IF([test "$PERF_TESTS"], [AC_MSG_RESULT(ok)], [AC_MSG_WARN(none found)])
|
||||||
|
|
||||||
AC_CONFIG_FILES(
|
AC_CONFIG_FILES([Makefile doc/Makefile package.json lib/version.js])
|
||||||
[Makefile doc/Makefile package.json lib/version.js test/runner],
|
AC_CONFIG_FILES([test/runner], [chmod +x test/runner])
|
||||||
[chmod +x test/runner])
|
AC_CONFIG_FILES([test/perf/runner], [chmod +x test/perf/runner])
|
||||||
|
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|
|
@ -244,16 +244,14 @@ exports.isInstanceOf = function( type, instance )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
// check prototype chain (will throw an error if type is not a
|
||||||
|
// constructor (function)
|
||||||
|
if ( ( typeof type === 'function' )
|
||||||
|
&& ( instance instanceof type )
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// check prototype chain (will throw an error if type is not a
|
return true;
|
||||||
// constructor (function)
|
|
||||||
if ( instance instanceof type )
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch ( e ) {}
|
|
||||||
|
|
||||||
// if no metadata is available, then our remaining checks cannot be
|
// if no metadata is available, then our remaining checks cannot be
|
||||||
// performed
|
// performed
|
||||||
|
@ -299,9 +297,10 @@ exports.prototype.build = function extend( _, __ )
|
||||||
// ensure we'll be permitted to instantiate abstract classes for the base
|
// ensure we'll be permitted to instantiate abstract classes for the base
|
||||||
this._extending = true;
|
this._extending = true;
|
||||||
|
|
||||||
var args = Array.prototype.slice.call( arguments ),
|
var a = arguments,
|
||||||
props = args.pop() || {},
|
an = a.length,
|
||||||
base = args.pop() || exports.ClassBase,
|
props = ( ( an > 0 ) ? a[ an - 1 ] : 0 ) || {},
|
||||||
|
base = ( ( an > 1 ) ? a[ an - 2 ] : 0 ) || exports.ClassBase,
|
||||||
prototype = this._getBase( base ),
|
prototype = this._getBase( base ),
|
||||||
cname = '',
|
cname = '',
|
||||||
autoa = false,
|
autoa = false,
|
||||||
|
@ -508,7 +507,10 @@ exports.prototype.buildMembers = function buildMembers(
|
||||||
{
|
{
|
||||||
handlers[ name ] = function()
|
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
|
// invoke the custom handler with the original handler as
|
||||||
// its last argument (which the custom handler may choose
|
// its last argument (which the custom handler may choose
|
||||||
|
|
112
lib/class.js
112
lib/class.js
|
@ -64,19 +64,24 @@ var _nullf = function() { return null; }
|
||||||
module.exports = function( namedef, def )
|
module.exports = function( namedef, def )
|
||||||
{
|
{
|
||||||
var type = ( typeof namedef ),
|
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 )
|
switch ( type )
|
||||||
{
|
{
|
||||||
// anonymous class
|
// anonymous class
|
||||||
case 'object':
|
case 'object':
|
||||||
result = createAnonymousClass.apply( null, arguments );
|
result = createAnonymousClass.apply( null, args );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// named class
|
// named class
|
||||||
case 'string':
|
case 'string':
|
||||||
result = createNamedClass.apply( null, arguments );
|
result = createNamedClass.apply( null, args );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -99,10 +104,7 @@ module.exports = function( namedef, def )
|
||||||
*
|
*
|
||||||
* @return {Function} extended class
|
* @return {Function} extended class
|
||||||
*/
|
*/
|
||||||
module.exports.extend = function( baseordfn, dfn )
|
module.exports.extend = extend;
|
||||||
{
|
|
||||||
return extend.apply( this, arguments );
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,11 +140,11 @@ module.exports.implement = function( interfaces )
|
||||||
*/
|
*/
|
||||||
module.exports.use = function( traits )
|
module.exports.use = function( traits )
|
||||||
{
|
{
|
||||||
|
var args = [], i = arguments.length;
|
||||||
|
while( i-- ) args[ i ] = arguments[ i ];
|
||||||
|
|
||||||
// consume traits onto an empty base
|
// consume traits onto an empty base
|
||||||
return createUse(
|
return createUse( _nullf, args );
|
||||||
_nullf,
|
|
||||||
Array.prototype.slice.call( arguments )
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -292,7 +294,10 @@ function createStaging( cname )
|
||||||
return {
|
return {
|
||||||
extend: function()
|
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
|
// extend() takes a maximum of two arguments. If only one
|
||||||
// argument is provided, then it is to be the class definition.
|
// argument is provided, then it is to be the class definition.
|
||||||
|
@ -308,21 +313,24 @@ function createStaging( cname )
|
||||||
|
|
||||||
implement: function()
|
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
|
// implement on empty base, providing the class name to be used once
|
||||||
// extended
|
// extended
|
||||||
return createImplement(
|
return createImplement( null, args, cname );
|
||||||
null,
|
|
||||||
Array.prototype.slice.call( arguments ),
|
|
||||||
cname
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
use: function()
|
use: function()
|
||||||
{
|
{
|
||||||
return createUse(
|
var args = [],
|
||||||
_nullf,
|
i = arguments.length;
|
||||||
Array.prototype.slice.call( arguments )
|
|
||||||
);
|
while ( i-- ) args[ i ] = arguments[ i ];
|
||||||
|
|
||||||
|
return createUse( _nullf, args );
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -349,14 +357,14 @@ function createImplement( base, ifaces, cname )
|
||||||
var partial = {
|
var partial = {
|
||||||
extend: function()
|
extend: function()
|
||||||
{
|
{
|
||||||
var args = Array.prototype.slice.call( arguments ),
|
var an = arguments.length,
|
||||||
def = args.pop(),
|
def = arguments[ an - 1 ],
|
||||||
ext_base = args.pop()
|
ext_base = ( an > 1 ) ? arguments[ an - 2 ] : null
|
||||||
;
|
;
|
||||||
|
|
||||||
// if any arguments remain, then they likely misunderstood what this
|
// if any arguments remain, then they likely misunderstood what this
|
||||||
// method does
|
// method does
|
||||||
if ( args.length > 0 )
|
if ( an > 2 )
|
||||||
{
|
{
|
||||||
throw Error(
|
throw Error(
|
||||||
"Expecting no more than two arguments for extend()"
|
"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)
|
// much more performant (it creates a subtype before mixing in)
|
||||||
use: function()
|
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(
|
return createUse(
|
||||||
function() { return partial.__createBase(); },
|
function() { return partial.__createBase(); },
|
||||||
traits
|
traits
|
||||||
|
@ -468,9 +481,9 @@ function createUse( basef, traits, nonbase )
|
||||||
// given during the extend operation
|
// given during the extend operation
|
||||||
partial.extend = function()
|
partial.extend = function()
|
||||||
{
|
{
|
||||||
var args = Array.prototype.slice.call( arguments ),
|
var an = arguments.length,
|
||||||
dfn = args.pop(),
|
dfn = arguments[ an - 1 ],
|
||||||
ext_base = args.pop(),
|
ext_base = ( an > 1 ) ? arguments[ an - 2 ] : null,
|
||||||
base = basef();
|
base = basef();
|
||||||
|
|
||||||
// extend the mixed class, which ensures that all super references
|
// 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
|
// call simply to mix in another trait
|
||||||
partial.use = function()
|
partial.use = function()
|
||||||
{
|
{
|
||||||
|
var args = [],
|
||||||
|
i = arguments.length;
|
||||||
|
|
||||||
|
while ( i-- ) args[ i ] = arguments[ i ];
|
||||||
|
|
||||||
return createUse(
|
return createUse(
|
||||||
function()
|
function()
|
||||||
{
|
{
|
||||||
return partial.__createBase();
|
return partial.__createBase();
|
||||||
},
|
},
|
||||||
Array.prototype.slice.call( arguments ),
|
args,
|
||||||
nonbase
|
nonbase
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -557,8 +575,14 @@ function createMixedClass( base, traits )
|
||||||
*/
|
*/
|
||||||
function extend( _, __ )
|
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
|
// 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
|
// set up some additional convenience props
|
||||||
setupProps( new_class );
|
setupProps( new_class );
|
||||||
|
@ -584,10 +608,9 @@ function extend( _, __ )
|
||||||
*/
|
*/
|
||||||
var implement = function( baseobj, interfaces )
|
var implement = function( baseobj, interfaces )
|
||||||
{
|
{
|
||||||
var args = Array.prototype.slice.call( arguments ),
|
var an = arguments.length,
|
||||||
dest = {},
|
dest = {},
|
||||||
base = args.pop(),
|
base = arguments[ an - 1 ],
|
||||||
len = args.length,
|
|
||||||
arg = null,
|
arg = null,
|
||||||
|
|
||||||
implemented = [],
|
implemented = [],
|
||||||
|
@ -595,9 +618,9 @@ var implement = function( baseobj, interfaces )
|
||||||
;
|
;
|
||||||
|
|
||||||
// add each of the 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)
|
// copy all interface methods to the class (does not yet deep copy)
|
||||||
util.propParse( arg.prototype, {
|
util.propParse( arg.prototype, {
|
||||||
|
@ -678,10 +701,10 @@ function attachImplement( func )
|
||||||
{
|
{
|
||||||
util.defineSecureProp( func, 'implement', function()
|
util.defineSecureProp( func, 'implement', function()
|
||||||
{
|
{
|
||||||
return createImplement(
|
var args = [], i = arguments.length;
|
||||||
func,
|
while( i-- ) args[ i ] = arguments[ i ];
|
||||||
Array.prototype.slice.call( arguments )
|
|
||||||
);
|
return createImplement( func, args );
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,11 +722,10 @@ function attachUse( func )
|
||||||
{
|
{
|
||||||
util.defineSecureProp( func, 'use', function()
|
util.defineSecureProp( func, 'use', function()
|
||||||
{
|
{
|
||||||
return createUse(
|
var args = [], i = arguments.length;
|
||||||
function() { return func; },
|
while( i-- ) args[ i ] = arguments[ i ];
|
||||||
Array.prototype.slice.call( arguments ),
|
|
||||||
true
|
return createUse( function() { return func; }, args, true );
|
||||||
);
|
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ var Class = require( __dirname + '/class' );
|
||||||
*/
|
*/
|
||||||
module.exports = exports = function()
|
module.exports = exports = function()
|
||||||
{
|
{
|
||||||
markAbstract( arguments );
|
markAbstract( arguments[ arguments.length - 1 ] );
|
||||||
|
|
||||||
// forward everything to Class
|
// forward everything to Class
|
||||||
var result = Class.apply( this, arguments );
|
var result = Class.apply( this, arguments );
|
||||||
|
@ -56,7 +56,7 @@ module.exports = exports = function()
|
||||||
*/
|
*/
|
||||||
exports.extend = function()
|
exports.extend = function()
|
||||||
{
|
{
|
||||||
markAbstract( arguments );
|
markAbstract( arguments[ arguments.length - 1 ] );
|
||||||
return Class.extend.apply( this, arguments );
|
return Class.extend.apply( this, arguments );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,18 +92,12 @@ exports.implement = function()
|
||||||
/**
|
/**
|
||||||
* Causes a definition to be flagged as abstract
|
* Causes a definition to be flagged as abstract
|
||||||
*
|
*
|
||||||
* This function assumes the last argument to be the definition, which is the
|
* @param {*} dfn suspected definition object
|
||||||
* common case, and modifies the object referenced by that argument.
|
|
||||||
*
|
|
||||||
* @param {Arguments} args arguments to parse
|
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @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' )
|
if ( typeof dfn === 'object' )
|
||||||
{
|
{
|
||||||
// mark as abstract
|
// mark as abstract
|
||||||
|
@ -141,7 +135,7 @@ function abstractOverride( obj )
|
||||||
// wrap extend, applying the abstract flag
|
// wrap extend, applying the abstract flag
|
||||||
obj.extend = function()
|
obj.extend = function()
|
||||||
{
|
{
|
||||||
markAbstract( arguments );
|
markAbstract( arguments[ arguments.length - 1 ] );
|
||||||
return extend.apply( this, arguments );
|
return extend.apply( this, arguments );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ var Class = require( __dirname + '/class' );
|
||||||
*/
|
*/
|
||||||
exports = module.exports = function()
|
exports = module.exports = function()
|
||||||
{
|
{
|
||||||
markFinal( arguments );
|
markFinal( arguments[ arguments.length - 1 ] );
|
||||||
|
|
||||||
// forward everything to Class
|
// forward everything to Class
|
||||||
var result = Class.apply( this, arguments );
|
var result = Class.apply( this, arguments );
|
||||||
|
@ -50,7 +50,7 @@ exports = module.exports = function()
|
||||||
*/
|
*/
|
||||||
exports.extend = function()
|
exports.extend = function()
|
||||||
{
|
{
|
||||||
markFinal( arguments );
|
markFinal( arguments[ arguments.length - 1 ] );
|
||||||
return Class.extend.apply( this, arguments );
|
return Class.extend.apply( this, arguments );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,18 +58,12 @@ exports.extend = function()
|
||||||
/**
|
/**
|
||||||
* Causes a definition to be flagged as final
|
* Causes a definition to be flagged as final
|
||||||
*
|
*
|
||||||
* This function assumes the last argument to be the definition, which is the
|
* @param {!Arguments} dfn suspected definition object
|
||||||
* common case, and modifies the object referenced by that argument.
|
|
||||||
*
|
|
||||||
* @param {!Arguments} args arguments to parse
|
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @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' )
|
if ( typeof dfn === 'object' )
|
||||||
{
|
{
|
||||||
// mark as abstract
|
// mark as abstract
|
||||||
|
@ -92,7 +86,7 @@ function finalOverride( obj )
|
||||||
// wrap extend, applying the abstract flag
|
// wrap extend, applying the abstract flag
|
||||||
obj.extend = function()
|
obj.extend = function()
|
||||||
{
|
{
|
||||||
markFinal( arguments );
|
markFinal( arguments[ arguments.length - 1 ] );
|
||||||
return extend.apply( this, arguments );
|
return extend.apply( this, arguments );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,6 +177,23 @@ function createNamedInterface( name, def )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Augment an exception with interface name and then throw
|
||||||
|
*
|
||||||
|
* @param {string} iname interface name or empty string
|
||||||
|
* @param {Error} e exception to augment
|
||||||
|
*/
|
||||||
|
function _ithrow( iname, e )
|
||||||
|
{
|
||||||
|
// alter the message to include our name
|
||||||
|
e.message = "Failed to define interface " +
|
||||||
|
( ( iname ) ? iname : '(anonymous)' ) + ": " + e.message
|
||||||
|
;
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var extend = ( function( extending )
|
var extend = ( function( extending )
|
||||||
{
|
{
|
||||||
return function extend()
|
return function extend()
|
||||||
|
@ -184,9 +201,10 @@ var extend = ( function( extending )
|
||||||
// ensure we'll be permitted to instantiate interfaces for the base
|
// ensure we'll be permitted to instantiate interfaces for the base
|
||||||
extending = true;
|
extending = true;
|
||||||
|
|
||||||
var args = Array.prototype.slice.call( arguments ),
|
var a = arguments,
|
||||||
props = args.pop() || {},
|
an = a.length,
|
||||||
base = args.pop() || Interface,
|
props = ( ( an > 0 ) ? a[ an - 1 ] : 0 ) || {},
|
||||||
|
base = ( ( an > 1 ) ? a[ an - 2 ] : 0 ) || Interface,
|
||||||
prototype = new base(),
|
prototype = new base(),
|
||||||
iname = '',
|
iname = '',
|
||||||
|
|
||||||
|
@ -210,50 +228,43 @@ var extend = ( function( extending )
|
||||||
|
|
||||||
var new_interface = createInterface( iname );
|
var new_interface = createInterface( iname );
|
||||||
|
|
||||||
try
|
util.propParse( props, {
|
||||||
{
|
assumeAbstract: true,
|
||||||
util.propParse( props, {
|
|
||||||
assumeAbstract: true,
|
|
||||||
|
|
||||||
property: function()
|
// override default exceptions from parser errors
|
||||||
|
_throw: function( e )
|
||||||
|
{
|
||||||
|
_ithrow( iname, e );
|
||||||
|
},
|
||||||
|
|
||||||
|
property: function()
|
||||||
|
{
|
||||||
|
// should never get to this point because of assumeAbstract
|
||||||
|
_ithrow( iname, TypeError( "Unexpected internal error" ) );
|
||||||
|
},
|
||||||
|
|
||||||
|
getset: function()
|
||||||
|
{
|
||||||
|
// should never get to this point because of assumeAbstract
|
||||||
|
_ithrow( iname, TypeError( "Unexpected internal error" ) );
|
||||||
|
},
|
||||||
|
|
||||||
|
method: function( name, value, is_abstract, keywords )
|
||||||
|
{
|
||||||
|
// all members must be public
|
||||||
|
if ( keywords[ 'protected' ] || keywords[ 'private' ] )
|
||||||
{
|
{
|
||||||
// should never get to this point because of assumeAbstract
|
_ithrow( iname, TypeError(
|
||||||
throw TypeError( 'Unexpected internal error' );
|
"Member " + name + " must be public"
|
||||||
},
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
getset: function()
|
member_builder.buildMethod(
|
||||||
{
|
members, null, name, value, keywords,
|
||||||
// should never get to this point because of assumeAbstract
|
null, 0, {}, vstate
|
||||||
throw TypeError( 'Unexpected internal error' );
|
);
|
||||||
},
|
},
|
||||||
|
} );
|
||||||
method: function( name, value, is_abstract, keywords )
|
|
||||||
{
|
|
||||||
// all members must be public
|
|
||||||
if ( keywords[ 'protected' ] || keywords[ 'private' ] )
|
|
||||||
{
|
|
||||||
throw TypeError(
|
|
||||||
iname + " member " + name + " must be public"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
member_builder.buildMethod(
|
|
||||||
members, null, name, value, keywords,
|
|
||||||
null, 0, {}, vstate
|
|
||||||
);
|
|
||||||
},
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
catch ( e )
|
|
||||||
{
|
|
||||||
// alter the message to include our name
|
|
||||||
e.message = "Failed to define interface " +
|
|
||||||
( ( iname ) ? iname : '(anonymous)' ) + ": " + e.message
|
|
||||||
;
|
|
||||||
|
|
||||||
// re-throw
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
attachExtend( new_interface );
|
attachExtend( new_interface );
|
||||||
attachStringMethod( new_interface, iname );
|
attachStringMethod( new_interface, iname );
|
||||||
|
|
64
lib/util.js
64
lib/util.js
|
@ -246,6 +246,19 @@ exports.copyTo = function( dest, src, deep )
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw an exception
|
||||||
|
*
|
||||||
|
* Yes, this function has purpose; see where it's used.
|
||||||
|
*
|
||||||
|
* @param {Error} e exception to throw
|
||||||
|
*/
|
||||||
|
function _throw( e )
|
||||||
|
{
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses object properties to determine how they should be interpreted in an
|
* Parses object properties to determine how they should be interpreted in an
|
||||||
* Object Oriented manner
|
* Object Oriented manner
|
||||||
|
@ -268,6 +281,8 @@ exports.propParse = function( data, options, context )
|
||||||
callbackGetSet = options.getset || fvoid,
|
callbackGetSet = options.getset || fvoid,
|
||||||
keywordParser = options.keywordParser || propParseKeywords,
|
keywordParser = options.keywordParser || propParseKeywords,
|
||||||
|
|
||||||
|
throwf = options._throw || _throw,
|
||||||
|
|
||||||
hasOwn = Object.prototype.hasOwnProperty,
|
hasOwn = Object.prototype.hasOwnProperty,
|
||||||
|
|
||||||
parse_data = {},
|
parse_data = {},
|
||||||
|
@ -315,12 +330,12 @@ exports.propParse = function( data, options, context )
|
||||||
|
|
||||||
if ( !( value instanceof Array ) )
|
if ( !( value instanceof Array ) )
|
||||||
{
|
{
|
||||||
throw TypeError(
|
throwf( TypeError(
|
||||||
"Missing parameter list for abstract method: " + name
|
"Missing parameter list for abstract method: " + name
|
||||||
);
|
) );
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyAbstractNames( name, value );
|
verifyAbstractNames( throwf, name, value );
|
||||||
value = exports.createAbstractMethod.apply( this, value );
|
value = exports.createAbstractMethod.apply( this, value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,22 +378,24 @@ exports.propParse = function( data, options, context )
|
||||||
* In the future, we may add additional functionality, so it's important to
|
* In the future, we may add additional functionality, so it's important to
|
||||||
* restrict this as much as possible for the time being.
|
* restrict this as much as possible for the time being.
|
||||||
*
|
*
|
||||||
|
* @param {function(Error)} throwf function to call with error
|
||||||
|
*
|
||||||
* @param {string} name name of abstract member (for error)
|
* @param {string} name name of abstract member (for error)
|
||||||
* @param {Object} params parameter list to check
|
* @param {Object} params parameter list to check
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
function verifyAbstractNames( name, params )
|
function verifyAbstractNames( throwf, name, params )
|
||||||
{
|
{
|
||||||
var i = params.length;
|
var i = params.length;
|
||||||
while ( i-- )
|
while ( i-- )
|
||||||
{
|
{
|
||||||
if ( params[ i ].match( /^[a-z_][a-z0-9_]*$/i ) === null )
|
if ( params[ i ].match( /^[a-z_][a-z0-9_]*$/i ) === null )
|
||||||
{
|
{
|
||||||
throw SyntaxError(
|
throwf( SyntaxError(
|
||||||
"Member " + name + " contains invalid parameter '" +
|
"Member " + name + " contains invalid parameter '" +
|
||||||
params[ i ] + "'"
|
params[ i ] + "'"
|
||||||
);
|
) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,7 +416,10 @@ function verifyAbstractNames( name, params )
|
||||||
*/
|
*/
|
||||||
exports.createAbstractMethod = function( def )
|
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()
|
var method = function()
|
||||||
{
|
{
|
||||||
|
@ -407,7 +427,7 @@ exports.createAbstractMethod = function( def )
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.defineSecureProp( method, 'abstractFlag', true );
|
exports.defineSecureProp( method, 'abstractFlag', true );
|
||||||
exports.defineSecureProp( method, 'definition', definition );
|
exports.defineSecureProp( method, 'definition', dfn );
|
||||||
exports.defineSecureProp( method, '__length', arguments.length );
|
exports.defineSecureProp( method, '__length', arguments.length );
|
||||||
|
|
||||||
return method;
|
return method;
|
||||||
|
@ -562,8 +582,8 @@ exports.defineSecureProp( exports.getPropertyDescriptor, 'canTraverse',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appropriately returns defineSecureProp implementation to avoid check on each
|
* Appropriately returns defineSecureProp implementation to avoid check on
|
||||||
* invocation
|
* each invocation
|
||||||
*
|
*
|
||||||
* @return {function( Object, string, * )}
|
* @return {function( Object, string, * )}
|
||||||
*/
|
*/
|
||||||
|
@ -584,26 +604,14 @@ function getDefineSecureProp()
|
||||||
// uses ECMAScript 5's Object.defineProperty() method
|
// uses ECMAScript 5's Object.defineProperty() method
|
||||||
return function( obj, prop, value )
|
return function( obj, prop, value )
|
||||||
{
|
{
|
||||||
try
|
Object.defineProperty( obj, prop,
|
||||||
{
|
{
|
||||||
Object.defineProperty( obj, prop,
|
value: value,
|
||||||
{
|
|
||||||
value: value,
|
|
||||||
|
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
configurable: false,
|
configurable: false,
|
||||||
});
|
} );
|
||||||
}
|
|
||||||
catch ( e )
|
|
||||||
{
|
|
||||||
// let's not have this happen again, as repeatedly throwing
|
|
||||||
// exceptions will do nothing but slow down the system
|
|
||||||
exports.definePropertyFallback( true );
|
|
||||||
|
|
||||||
// there's an error (ehem, IE8); fall back
|
|
||||||
fallback( obj, prop, value );
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,8 +89,6 @@ exports.report = function( count, desc )
|
||||||
pers = ( total / count ).toFixed( 10 )
|
pers = ( total / count ).toFixed( 10 )
|
||||||
;
|
;
|
||||||
|
|
||||||
console.log( total + "s (x" + count + " = " + pers + "s each)" +
|
console.log( "%s|%s|%s|%s", desc, count, pers, total );
|
||||||
( ( desc ) ? ( ': ' + desc ) : '' )
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -105,3 +105,32 @@ common.test( function()
|
||||||
// run the same test internally
|
// run the same test internally
|
||||||
foo.testInternal();
|
foo.testInternal();
|
||||||
|
|
||||||
|
|
||||||
|
// allows us to compare private method invocation times with method
|
||||||
|
// invocations on a conventional prototype (the increment of `i` is to
|
||||||
|
// ensure that the function is not optimized away)
|
||||||
|
( function()
|
||||||
|
{
|
||||||
|
var p = function() {};
|
||||||
|
p.prototype.foo = function() { i++ };
|
||||||
|
var o = new p();
|
||||||
|
|
||||||
|
common.test( function()
|
||||||
|
{
|
||||||
|
var i = count;
|
||||||
|
while ( i-- ) o.foo();
|
||||||
|
}, count, '[baseline] Invoke method on prototype' );
|
||||||
|
} )();
|
||||||
|
|
||||||
|
|
||||||
|
// compare with plain old function invocation
|
||||||
|
( function()
|
||||||
|
{
|
||||||
|
var f = function() { i++ };
|
||||||
|
common.test( function()
|
||||||
|
{
|
||||||
|
var i = count;
|
||||||
|
while ( i-- ) f();
|
||||||
|
}, count, '[baseline] Invoke function' );
|
||||||
|
} )();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014 Mike Gerwitz
|
||||||
|
#
|
||||||
|
# This file is part of GNU ease.js.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# If you want formatted output, see :/tools/perf2html, or run this command:
|
||||||
|
# $ column -ts\| perf.out
|
||||||
|
# #
|
||||||
|
|
||||||
|
for f in "$@"; do
|
||||||
|
@NODE@ "$f" || exit $?
|
||||||
|
done \
|
||||||
|
| tee >( awk -F\| '
|
||||||
|
# format for display as the tests are running
|
||||||
|
{ printf "%s (x%s = %ss each): %s\n", $4, $2, $3, $1 }
|
||||||
|
' >&2 )
|
|
@ -0,0 +1,97 @@
|
||||||
|
#!/usr/bin/awk -f
|
||||||
|
# Renders performance test output
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014 Mike Gerwitz
|
||||||
|
#
|
||||||
|
# This file is part of GNU ease.js.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU 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 General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# If you want more modest output, consider running this command instead:
|
||||||
|
# $ column -ts\| perf.out
|
||||||
|
# #
|
||||||
|
|
||||||
|
function styleout( i ) {
|
||||||
|
value = $i
|
||||||
|
|
||||||
|
# slow tests should reduce the number of iterations to eat up less time
|
||||||
|
if ( ( i == COL_TOTAL ) && ( value > 0.05 ) ) {
|
||||||
|
class = "slow"
|
||||||
|
if ( value > 0.1 )
|
||||||
|
class = "very " class
|
||||||
|
|
||||||
|
return "<span class=\"" class "\">" value "</span>"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
# column constants
|
||||||
|
COL_DESC = 1
|
||||||
|
COL_COUNT = 2
|
||||||
|
COL_SINGLE = 3
|
||||||
|
COL_TOTAL = 4
|
||||||
|
|
||||||
|
# running time tally
|
||||||
|
runtime = 0.00
|
||||||
|
|
||||||
|
# header
|
||||||
|
print "<!DOCTYPE html>" \
|
||||||
|
"<html>" \
|
||||||
|
"<head>" \
|
||||||
|
"<title>GNU ease.js Performance Test Results</title>" \
|
||||||
|
"<style type=\"text/css\">" \
|
||||||
|
"table th:first-child { text-align: left; }" \
|
||||||
|
"table td { padding: 0.1em 0.5em; }" \
|
||||||
|
"table td:not(:first-child) { text-align: right; }" \
|
||||||
|
".slow { color: #c4a000; font-style: italic; }" \
|
||||||
|
".very.slow { color: red; }" \
|
||||||
|
"</style>" \
|
||||||
|
"</head>" \
|
||||||
|
"<body>" \
|
||||||
|
"<h1>GNU ease.js Performance Test Results</h1>" \
|
||||||
|
"<table>" \
|
||||||
|
"<thead>" \
|
||||||
|
"<tr>" \
|
||||||
|
"<th>Description</th>" \
|
||||||
|
"<th>Iterations</th>" \
|
||||||
|
"<th>Seconds/iter</th>" \
|
||||||
|
"<th>Total (s)</th>" \
|
||||||
|
"</tr>" \
|
||||||
|
"</thead>" \
|
||||||
|
"<tbody>"
|
||||||
|
}
|
||||||
|
|
||||||
|
# format row of output (desc, count, time, total)
|
||||||
|
{
|
||||||
|
runtime += $COL_TOTAL
|
||||||
|
|
||||||
|
printf "<tr>"
|
||||||
|
for ( i = 1; i <= NF; i++ )
|
||||||
|
{
|
||||||
|
printf "<td>%s</td>", styleout( i )
|
||||||
|
}
|
||||||
|
printf "</tr>\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
END {
|
||||||
|
# footer
|
||||||
|
print "</tbody>" \
|
||||||
|
"</table>" \
|
||||||
|
"<p>Total running time: " runtime " seconds</p>" \
|
||||||
|
"</body>" \
|
||||||
|
"</html>"
|
||||||
|
}
|
Loading…
Reference in New Issue