diff --git a/Makefile b/Makefile
index 0cd42a0..16da221 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,10 @@ PATH_TOOLS=${CWD}/tools
PATH_COMBINE_OUTPUT=${PATH_BUILD}/ease.js
PATH_COMBINE_OUTPUT_FULL=${PATH_BUILD}/ease-full.js
PATH_BROWSER_TEST=${PATH_TOOLS}/browser-test.html
+PATH_TEST=./test
+PATH_PERF_TEST=${PATH_TEST}/perf
+
+PERF_TESTS := $(shell find "$(PATH_PERF_TEST)" -name 'perf-*.js')
PATH_DOC=${CWD}/doc
PATH_DOC_OUTPUT=${PATH_BUILD}/doc
@@ -17,11 +21,14 @@ PATH_MANUAL_TEXI=${PATH_DOC}/manual.texi
COMBINE=${PATH_TOOLS}/combine
-TESTS_JS := $(shell find "./test" -name 'test-*.js')
-TESTS_SHELL := $(shell find "./test" -name 'test-[^\.]*')
+TESTS := $(shell find "$(PATH_TEST)" \
+ -name 'test-*' \
+ -a ! -name 'test-combine.js'\
+)
+TEST_COMBINE := $(PATH_TEST)/test-combine.js
-.PHONY: test doc
+.PHONY: test test-combine doc
default: combine
@@ -39,11 +46,17 @@ combine: mkbuild
cp "${PATH_BROWSER_TEST}" "${PATH_BUILD}"
# run tests
-test: default $(TESTS_JS) $(TESTS_SHELL)
+test: default $(TESTS) test-combine
+test-combine: default $(TEST_COMBINE)
test-%.js: default
- node $@
+ node $@
test-%: default
- ./$@
+ ./$@
+
+# performance tests
+perf: default $(PERF_TESTS)
+perf-%.js: default
+ @node $@
# generate texinfo documentation (twice to generate TOC), then remove the extra
# files that were generated
diff --git a/TODO b/TODO
index 925d8c8..0c9e606 100644
--- a/TODO
+++ b/TODO
@@ -5,6 +5,9 @@
[ target: 0.1.0 ]
Misc
- Class module is becoming too large; refactor
+ - Disallow member redeclaration in definition
+ - Permit binding on class methods
+ - Provide ability to free class from memory (class data stored in internal vars)
Member Keywords
- Restrictions; throw exceptions when unknown keywords are used
diff --git a/lib/class.js b/lib/class.js
index cfae254..3aae42e 100644
--- a/lib/class.js
+++ b/lib/class.js
@@ -457,6 +457,9 @@ var extend = ( function( extending )
}
}
+ // increment class identifier
+ class_id++;
+
util.propParse( props, {
each: function( name, value, keywords )
{
@@ -498,7 +501,8 @@ var extend = ( function( extending )
method: function( name, func, is_abstract, keywords )
{
member_builder.buildMethod(
- members, null, name, func, keywords, getMethodInstance
+ members, null, name, func, keywords, getMethodInstance,
+ class_id
);
if ( is_abstract )
@@ -531,13 +535,13 @@ var extend = ( function( extending )
// set up the new class
var new_class = createCtor( cname, abstract_methods, members );
- attachPropInit( prototype, prop_init, members );
+ attachPropInit( prototype, prop_init, members, class_id );
new_class.prototype = prototype;
new_class.constructor = new_class;
// important: call after setting prototype
- setupProps( new_class, abstract_methods, ++class_id );
+ setupProps( new_class, abstract_methods, class_id );
// lock down the new class (if supported) to ensure that we can't add
// members at runtime
@@ -761,24 +765,27 @@ function initInstance( iid, instance )
* ensuring that their data is not shared with other instances (this is not a
* problem with primitive data types).
*
- * The __initProps() method will also initialize any parent properties
- * (recursive) to ensure that subtypes do not have a referencing issue, and
- * subtype properties take precedence over those of the parent.
+ * The method will also initialize any parent properties (recursive) to ensure
+ * that subtypes do not have a referencing issue, and subtype properties take
+ * precedence over those of the parent.
*
* @param {Object} prototype prototype to attach method to
* @param {Object} properties properties to initialize
+ * @param {number} cid class id
*
* @param {{public: Object, protected: Object, private: Object}} members
*
* @return {undefined}
*/
-function attachPropInit( prototype, properties, members )
+function attachPropInit( prototype, properties, members, cid )
{
util.defineSecureProp( prototype, '__initProps', function( inherit )
{
- // defaults to false
+ // defaults to false, sid = super identifier
inherit = !!inherit;
+ var iid = this.__iid;
+
// first initialize the parent's properties, so that ours will overwrite
// them
var parent_init = prototype.parent.__initProps;
@@ -790,21 +797,17 @@ function attachPropInit( prototype, properties, members )
parent_init.call( this, true );
}
+ // 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(
- this, class_instance[ this.__iid ], properties[ 'public' ]
+ this, class_instance[ iid ], properties[ 'public' ]
);
- // use whatever was returned by the property proxy (which may not be a
- // proxy, depending on JS engine support)
- class_instance[ this.__iid ] = inst_props;
-
// if we're inheriting, perform a setup that doesn't include everything
// that we don't want (e.g. private properties)
- var setup = ( inherit )
- ? propobj.setupInherited
- : propobj.setup
- ;
- setup( inst_props, properties, members );
+ class_instance[ iid ][ cid ] = propobj.setup(
+ inst_props, properties, members
+ );
});
}
@@ -981,21 +984,28 @@ function getMeta( id )
/**
* Returns the instance object associated with the given method
*
- * The instance object contains the protected and private members. This object
- * can be passed as the context when calling a method in order to give that
- * method access to those members.
+ * The instance object contains the protected members. This object can be passed
+ * as the context when calling a method in order to give that method access to
+ * those members.
+ *
+ * One level above the instance object on the prototype chain is the object
+ * containing the private members. This is swappable, depending on the class id
+ * associated with the provided method call. This allows methods that were not
+ * overridden by the subtype to continue to use the private members of the
+ * supertype.
*
* @param {function()} method method to look up instance object for
+ * @param {number} cid class id
*
* @return {Object,null} instance object if found, otherwise null
*/
-function getMethodInstance( method )
+function getMethodInstance( inst, cid )
{
- var iid = method.__iid,
- data = class_instance[ method.__iid ];
+ var iid = inst.__iid,
+ data = class_instance[ iid ];
return ( iid && data )
- ? data
+ ? data[ cid ]
: null
;
}
diff --git a/lib/member_builder.js b/lib/member_builder.js
index 5daf630..3df3f4e 100644
--- a/lib/member_builder.js
+++ b/lib/member_builder.js
@@ -62,11 +62,12 @@ exports.initMembers = function( mpublic, mprotected, mprivate )
* @param {Object=} instCallback function to call in order to retrieve
* object to bind 'this' keyword to
+ * @param {number} cid class id
*
* @return {undefined}
*/
exports.buildMethod = function(
- members, meta, name, value, keywords, instCallback
+ members, meta, name, value, keywords, instCallback, cid
)
{
var prev;
@@ -109,7 +110,7 @@ exports.buildMethod = function(
if ( prev )
{
// override the method
- dest[ name ] = overrideMethod( prev, value, instCallback );
+ dest[ name ] = overrideMethod( prev, value, instCallback, cid );
}
else if ( keywords[ 'abstract' ] )
{
@@ -120,7 +121,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 );
+ dest[ name ] = overrideMethod( value, null, instCallback, cid );
}
};
@@ -303,10 +304,11 @@ function scanMembers( members, name, cmp )
*
* @param {Object=} instCallback function to call in order to retrieve
* object to bind 'this' keyword to
+ * @param {number} cid class id
*
* @return {function()} override method
*/
-function overrideMethod( super_method, new_method, instCallback )
+function overrideMethod( super_method, new_method, instCallback, cid )
{
instCallback = instCallback || function() {};
@@ -319,7 +321,7 @@ function overrideMethod( super_method, new_method, instCallback )
{
override = function()
{
- var context = instCallback( this ) || this,
+ var context = instCallback( this, cid ) || this,
retval = undefined
;
@@ -344,7 +346,7 @@ function overrideMethod( super_method, new_method, instCallback )
// we are defining a new method
override = function()
{
- var context = instCallback( this ) || this,
+ var context = instCallback( this, cid ) || this,
retval = undefined
;
diff --git a/lib/propobj.js b/lib/propobj.js
index c60d431..7ea372d 100644
--- a/lib/propobj.js
+++ b/lib/propobj.js
@@ -31,43 +31,37 @@ var util = require( './util' ),
/**
- * Sets up properties when inheriting
+ * Sets up properties (non-inheriting)
*
- * This does not include private members.
+ * 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.
*
* @param {Object} dest destination object
* @param {Object} properties properties to copy
* @param {Object=} methods methods to copy
*
- * @return {undefined}
+ * @return {Object} object containing private members and dest as prototype
*/
-exports.setupInherited = function( dest, properties, methods )
+exports.setup = function( dest, properties, methods )
{
+ // this constructor is an extra layer atop of the destination object, which
+ // will contain the private methods
+ var obj_ctor = function() {};
+ obj_ctor.prototype = dest;
+
+ var obj = new obj_ctor();
+
// initialize each of the properties for this instance to
// ensure we're not sharing references to prototype values
doSetup( dest, properties[ 'public' ] );
doSetup( dest, properties[ 'protected' ], methods[ 'protected'] );
-};
-
-
-/**
- * Sets up properties (non-inheriting)
- *
- * This includes all members (including private).
- *
- * @param {Object} dest destination object
- * @param {Object} properties properties to copy
- * @param {Object=} methods methods to copy
- *
- * @return {undefined}
- */
-exports.setup = function( dest, properties, methods )
-{
- // first, set up the public and protected members
- exports.setupInherited( dest, properties, methods );
// then add the private parts
- doSetup( dest, properties[ 'private' ], methods[ 'private' ] );
+ doSetup( obj, properties[ 'private' ], methods[ 'private' ] );
+
+ return obj;
};
diff --git a/test/perf/README b/test/perf/README
new file mode 100644
index 0000000..9c95d99
--- /dev/null
+++ b/test/perf/README
@@ -0,0 +1,8 @@
+This directory contains the performance tests. These tests contain basic
+routines that perform a single action and output the result in seconds, with a
+basic description of what has been done. The timing is done via the native Date
+object to ensure that it can be run both server and client-side.
+
+It is important that each test performs only a single operation to ensure that
+the prior operations have no consequences on the previous, at least when run
+server-side by invoking a separate executable for each.
diff --git a/test/perf/common.js b/test/perf/common.js
new file mode 100644
index 0000000..538d7a7
--- /dev/null
+++ b/test/perf/common.js
@@ -0,0 +1,99 @@
+/**
+ * Common performance testing functionality
+ *
+ * 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 performance
+ */
+
+/**
+ * Stores start time
+ * @type {number}
+ */
+var start = 0;
+
+
+/**
+ * Includes a module from the lib directory
+ *
+ * @param {string} name module name
+ *
+ * @return {Object} module exports
+ */
+exports.require = function( name )
+{
+ return require( '../../lib/' + name );
+};
+
+
+/**
+ * A simple wrapper to perform testing and output the result
+ *
+ * The count is not used to call the function multiple times, because that would
+ * greatly impact the test results. Instead, you should pass the number of times
+ * the test was performed in a loop.
+ *
+ * @param {function()} test performance test to perform
+ * @param {number} count number of times the test was performed
+ * @param {string=} desc test description
+ *
+ * @return {undefined}
+ */
+exports.test = function( test, count, desc )
+{
+ exports.start();
+ test();
+ exports.report( count, desc );
+};
+
+
+/**
+ * Starts the timer
+ *
+ * @return {undefined}
+ */
+exports.start = function()
+{
+ start = ( new Date() ).getTime();
+};
+
+
+/**
+ * Outputs the time elapsed, followed by the description (if available)
+ *
+ * @param {number} count number of times the test was performed
+ * @param {string=} desc test description
+ *
+ * @return {undefined}
+ */
+exports.report = function( count, desc )
+{
+ count = +count;
+ desc = desc || '';
+
+ var end = ( new Date() ).getTime(),
+ total = ( ( end - start ) / 1000 ).toFixed( 3 ),
+ pers = ( total / count ).toFixed( 10 )
+ ;
+
+ console.log( total + "s (x" + count + " = " + pers + "s each)" +
+ ( ( desc ) ? ( ': ' + desc ) : '' )
+ );
+};
+
diff --git a/test/perf/perf-class-define-methods-keyword-private.js b/test/perf/perf-class-define-methods-keyword-private.js
new file mode 100644
index 0000000..3c7ecce
--- /dev/null
+++ b/test/perf/perf-class-define-methods-keyword-private.js
@@ -0,0 +1,47 @@
+/**
+ * Tests amount of time taken to declare 1000 classes with a few members,
+ * private keyword
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {
+ 'private a': function() {},
+ 'private b': function() {},
+ 'private c': function() {},
+ } );
+ }
+
+}, count, 'Declare ' + count + ' anonymous classes with private members' );
diff --git a/test/perf/perf-class-define-methods-keyword-protected.js b/test/perf/perf-class-define-methods-keyword-protected.js
new file mode 100644
index 0000000..611b8f4
--- /dev/null
+++ b/test/perf/perf-class-define-methods-keyword-protected.js
@@ -0,0 +1,47 @@
+/**
+ * Tests amount of time taken to declare 1000 classes with a few members,
+ * protected keyword
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {
+ 'protected a': function() {},
+ 'protected b': function() {},
+ 'protected c': function() {},
+ } );
+ }
+
+}, count, 'Declare ' + count + ' anonymous classes with protected members' );
diff --git a/test/perf/perf-class-define-methods-keyword-public.js b/test/perf/perf-class-define-methods-keyword-public.js
new file mode 100644
index 0000000..8820713
--- /dev/null
+++ b/test/perf/perf-class-define-methods-keyword-public.js
@@ -0,0 +1,47 @@
+/**
+ * Tests amount of time taken to declare 1000 classes with a few members, public
+ * keyword
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {
+ 'public a': function() {},
+ 'public b': function() {},
+ 'public c': function() {},
+ } );
+ }
+
+}, count, 'Declare ' + count + ' anonymous classes with public members' );
diff --git a/test/perf/perf-class-define-methods.js b/test/perf/perf-class-define-methods.js
new file mode 100644
index 0000000..f51a1c6
--- /dev/null
+++ b/test/perf/perf-class-define-methods.js
@@ -0,0 +1,47 @@
+/**
+ * Tests amount of time taken to declare 1000 classes with a few members, no
+ * keywords
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {
+ a: function() {},
+ b: function() {},
+ c: function() {},
+ } );
+ }
+
+}, count, 'Declare ' + count + ' anonymous classes with few methods' );
diff --git a/test/perf/perf-class-define-named.js b/test/perf/perf-class-define-named.js
new file mode 100644
index 0000000..16f52e9
--- /dev/null
+++ b/test/perf/perf-class-define-named.js
@@ -0,0 +1,42 @@
+/**
+ * Tests amount of time taken to declare 1000 classes
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( 'Foo', {} );
+ }
+
+}, count, 'Declare ' + count + ' empty named classes' );
diff --git a/test/perf/perf-class-define-properties-keyword-private.js b/test/perf/perf-class-define-properties-keyword-private.js
new file mode 100644
index 0000000..c3ed765
--- /dev/null
+++ b/test/perf/perf-class-define-properties-keyword-private.js
@@ -0,0 +1,47 @@
+/**
+ * Tests amount of time taken to declare 1000 classes with a few properties,
+ * private keyword
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {
+ 'private a': 'foo',
+ 'private b': 10,
+ 'private c': false,
+ } );
+ }
+
+}, count, 'Declare ' + count + ' anonymous classes with private properties' );
diff --git a/test/perf/perf-class-define-properties-keyword-protected.js b/test/perf/perf-class-define-properties-keyword-protected.js
new file mode 100644
index 0000000..07f19a4
--- /dev/null
+++ b/test/perf/perf-class-define-properties-keyword-protected.js
@@ -0,0 +1,47 @@
+/**
+ * Tests amount of time taken to declare 1000 classes with a few properties,
+ * protected keyword
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {
+ 'protected a': 'foo',
+ 'protected b': 10,
+ 'protected c': false,
+ } );
+ }
+
+}, count, 'Declare ' + count + ' anonymous classes with protected properties' );
diff --git a/test/perf/perf-class-define-properties-keyword-public.js b/test/perf/perf-class-define-properties-keyword-public.js
new file mode 100644
index 0000000..569ee5e
--- /dev/null
+++ b/test/perf/perf-class-define-properties-keyword-public.js
@@ -0,0 +1,47 @@
+/**
+ * Tests amount of time taken to declare 1000 classes with a few properties,
+ * public keyword
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {
+ 'public a': 'foo',
+ 'public b': 10,
+ 'public c': false,
+ } );
+ }
+
+}, count, 'Declare ' + count + ' anonymous classes with public properties' );
diff --git a/test/perf/perf-class-define-properties.js b/test/perf/perf-class-define-properties.js
new file mode 100644
index 0000000..97046d1
--- /dev/null
+++ b/test/perf/perf-class-define-properties.js
@@ -0,0 +1,47 @@
+/**
+ * Tests amount of time taken to declare 1000 classes with few properties, no
+ * keywords
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {
+ a: 'foo',
+ b: 10,
+ c: false,
+ } );
+ }
+
+}, count, 'Declare ' + count + ' anonymous classes with few properties' );
diff --git a/test/perf/perf-class-define.js b/test/perf/perf-class-define.js
new file mode 100644
index 0000000..ad448db
--- /dev/null
+++ b/test/perf/perf-class-define.js
@@ -0,0 +1,42 @@
+/**
+ * Tests amount of time taken to declare 1000 classes
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 1000
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Class( {} );
+ }
+
+}, count, 'Declare ' + count + ' empty anonymous classes' );
diff --git a/test/perf/perf-class-get-property.js b/test/perf/perf-class-get-property.js
new file mode 100644
index 0000000..4e3a0ed
--- /dev/null
+++ b/test/perf/perf-class-get-property.js
@@ -0,0 +1,101 @@
+/**
+ * Tests amount of time taken to declare read properties internally and
+ * externally
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' ),
+
+ // we need many tests for a measurable result
+ count = 500000
+
+ // instance of anonymous class
+ foo = Class( {
+ 'public pub_bar': 'foo',
+ 'protected prot_bar': 'bar',
+ 'private priv_bar': 'baz',
+
+ 'public testInternal': function()
+ {
+ var _self = this;
+
+ common.test( function()
+ {
+ var i = count,
+ val = null
+ ;
+
+ while ( i-- )
+ {
+ val = _self.pub_bar;
+ }
+ }, count, 'Read public properties internally' );
+
+
+ common.test( function()
+ {
+ var i = count,
+ val = null
+ ;
+
+ while ( i-- )
+ {
+ val = _self.prot_bar;
+ }
+ }, count, 'Read protected properties internally' );
+
+
+ common.test( function()
+ {
+ var i = count,
+ val = null
+ ;
+
+ while ( i-- )
+ {
+ val = _self.priv_bar;
+ }
+ }, count, 'Read private properties internally' );
+ },
+ } )()
+;
+
+
+common.test( function()
+{
+ var i = count,
+ val = null
+ ;
+
+ while ( i-- )
+ {
+ val = foo.pub_bar;
+ }
+
+}, count, 'Read public properties externally' );
+
+
+// run the same test internally
+foo.testInternal();
+
diff --git a/test/perf/perf-class-inst-anon-empty.js b/test/perf/perf-class-inst-anon-empty.js
new file mode 100644
index 0000000..d737f95
--- /dev/null
+++ b/test/perf/perf-class-inst-anon-empty.js
@@ -0,0 +1,43 @@
+/**
+ * Tests amount of time taken to instantiate anonymous classes
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 5000,
+ Foo = Class( {} )
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ Foo();
+ }
+
+}, count, 'Instantiate ' + count + ' empty anonymous classes' );
diff --git a/test/perf/perf-class-inst-named-empty.js b/test/perf/perf-class-inst-named-empty.js
new file mode 100644
index 0000000..1149909
--- /dev/null
+++ b/test/perf/perf-class-inst-named-empty.js
@@ -0,0 +1,44 @@
+/**
+ * Tests amount of time taken to instantiate named classes
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' )
+
+ count = 5000,
+ Foo = Class( 'Foo', {} )
+;
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ // to be extra confident that V8 or another compiler won't realize this
+ // is useless and optimize it out
+ Foo();
+ }
+
+}, count, 'Instantiate ' + count + ' empty named classes' );
diff --git a/test/perf/perf-class-invoke-method.js b/test/perf/perf-class-invoke-method.js
new file mode 100644
index 0000000..2a97355
--- /dev/null
+++ b/test/perf/perf-class-invoke-method.js
@@ -0,0 +1,111 @@
+/**
+ * Tests amount of time taken to invoke Class methods
+ *
+ * The expected results are as follows:
+ * - Method invocations are expected to be slower than invoking a method on a
+ * conventional constructor instance. This is because of the method wrapper
+ * used by ease.js.
+ * - Public methods externally should be invoked very quickly. They are part
+ * of the class's prototype and therefore easily accessible.
+ * - Public methods /internally/ are likely to be invoked slightly more
+ * slowly. This is because it takes one extra step down the prototype chain
+ * to access them. The difference should be minute.
+ * - Protected and private methods internally should be accessed fairly
+ * quickly since, like public methods externally, they are first on the
+ * prototype chain.
+ * - Protected members will be accessed more slowly than private members,
+ * because they are one step lower on the prototype chain. Future versions
+ * will remove this performance hit if the Class contains no private
+ * members.
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' ),
+
+ count = 500000,
+
+ // used to ensure v8 doesn't optimize functions away
+ i = 0,
+
+ // instance of anonymous class
+ foo = Class( {
+ 'public pub': function() { i++; },
+ 'protected prot': function() { i++; },
+ 'private priv': function() { i++; },
+
+ 'public testInternal': function()
+ {
+ var _self = this;
+
+ common.test( function()
+ {
+ var i = count;
+
+ while ( i-- )
+ {
+ _self.pub();
+ }
+ }, count, 'Invoke public methods internally' );
+
+
+ common.test( function()
+ {
+ var i = count;
+
+ while ( i-- )
+ {
+ _self.prot();
+ }
+ }, count, 'Invoke protected methods internally' );
+
+
+ common.test( function()
+ {
+ var i = count;
+
+ while ( i-- )
+ {
+ _self.priv();
+ }
+ }, count, 'Invoke private methods internally' );
+ },
+ } )()
+;
+
+
+common.test( function()
+{
+ var i = count;
+
+ while ( i-- )
+ {
+ foo.pub();
+ }
+
+}, count, 'Invoke public methods externally' );
+
+
+// run the same test internally
+foo.testInternal();
+
diff --git a/test/perf/perf-class-require.js b/test/perf/perf-class-require.js
new file mode 100644
index 0000000..c5bd893
--- /dev/null
+++ b/test/perf/perf-class-require.js
@@ -0,0 +1,33 @@
+/**
+ * Tests amount of time spent on requiring class module
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' );
+
+// we run this test once because require() will cache the object in memory
+common.test( function()
+{
+ common.require( 'class' );
+}, 1, 'Require class module' );
+
diff --git a/test/perf/perf-class-set-property.js b/test/perf/perf-class-set-property.js
new file mode 100644
index 0000000..0221d5a
--- /dev/null
+++ b/test/perf/perf-class-set-property.js
@@ -0,0 +1,101 @@
+/**
+ * Tests amount of time taken to declare read properties internally and
+ * externally
+ *
+ * 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 performance
+ */
+
+
+var common = require( __dirname + '/common.js' ),
+ Class = common.require( 'class' ),
+
+ // we need many tests for a measurable result
+ count = 500000
+
+ // instance of anonymous class
+ foo = Class( {
+ 'public pub_bar': 'foo',
+ 'protected prot_bar': 'bar',
+ 'private priv_bar': 'baz',
+
+ 'public testInternal': function()
+ {
+ var _self = this;
+
+ common.test( function()
+ {
+ var i = count,
+ val = null
+ ;
+
+ while ( i-- )
+ {
+ _self.pub_bar = 'foo';
+ }
+ }, count, 'Write public properties internally' );
+
+
+ common.test( function()
+ {
+ var i = count,
+ val = null
+ ;
+
+ while ( i-- )
+ {
+ _self.prot_bar = 'foo';
+ }
+ }, count, 'Write protected properties internally' );
+
+
+ common.test( function()
+ {
+ var i = count,
+ val = null
+ ;
+
+ while ( i-- )
+ {
+ _self.priv_bar = 'foo';
+ }
+ }, count, 'Write private properties internally' );
+ },
+ } )()
+;
+
+
+common.test( function()
+{
+ var i = count,
+ val = null
+ ;
+
+ while ( i-- )
+ {
+ foo.pub_bar = 'foo';
+ }
+
+}, count, 'Write public properties externally' );
+
+
+// run the same test internally
+foo.testInternal();
+
diff --git a/test/test-class-visibility.js b/test/test-class-visibility.js
index 61bf5c9..5c82bf2 100644
--- a/test/test-class-visibility.js
+++ b/test/test-class-visibility.js
@@ -73,6 +73,24 @@ var common = require( './common' ),
{
// override me
},
+
+
+ 'public getPrivProp': function()
+ {
+ return this.parts;
+ },
+
+
+ 'public invokePriv': function()
+ {
+ return this._priv();
+ },
+
+
+ 'private _priv': function()
+ {
+ return priv;
+ },
}),
// instance of Foo
@@ -80,13 +98,28 @@ var common = require( './common' ),
// subtype
SubFoo = Foo.extend({
+ 'private _pfoo': 'baz',
+
'public getSelfOverride': function()
{
// return this from overridden method
return this;
},
+
+
+ /**
+ * We have to override this so that 'this' is not bound to the supertype
+ */
+ 'public getProp': function( name )
+ {
+ // return property, allowing us to break encapsulation for
+ // protected/private properties (for testing purposes)
+ return this[ name ];
+ },
}),
- sub_foo = SubFoo()
+ sub_foo = SubFoo(),
+
+ sub_sub_foo = SubFoo.extend( {} )()
;
@@ -362,3 +395,50 @@ var common = require( './common' ),
);
} )();
+
+/**
+ * This one's a particularly nasty bug that snuck up on me. Private members
+ * should not be accessible to subtypes; that's a given. However, they need to
+ * be accessible to the parent methods. For example, let's say class Foo
+ * contains public method bar(), which invokes private method _baz(). This is
+ * perfectly legal. Then SubFoo extends Foo, but does not override method bar().
+ * Invoking method bar() should still be able to invoke private method _baz(),
+ * because, from the perspective of the parent class, that operation is
+ * perfectly legal.
+ *
+ * The resolution of this bug required a slight system redesign. The short-term
+ * fix was to declare any needed private members are protected, so that they
+ * were accessible by the subtype.
+ */
+( function testParentMethodsCanAccessPrivateMembersOfParent()
+{
+ // properties
+ assert.equal(
+ sub_foo.getPrivProp(),
+ priv,
+ "Parent methods should have access to the private properties of the " +
+ "parent"
+ );
+
+ // methods
+ assert.equal(
+ sub_foo.invokePriv(),
+ priv,
+ "Parent methods should have access to the private methods of the parent"
+ );
+
+ // should apply to super-supertypes too
+ assert.equal(
+ sub_sub_foo.getPrivProp(),
+ priv,
+ "Parent methods should have access to the private properties of the " +
+ "parent (2)"
+ );
+ assert.equal(
+ sub_sub_foo.invokePriv(),
+ priv,
+ "Parent methods should have access to the private methods of the " +
+ "parent (2)"
+ );
+} )();
+