1
0
Fork 0

Began adding protected/private member support

- No longer adding ANY properties to prototype
- protected/private members no longer part of the public access level
closure/master
Mike Gerwitz 2011-01-27 22:35:40 -05:00
parent 8c7ad787c8
commit 38c16048cb
4 changed files with 97 additions and 17 deletions

View File

@ -212,10 +212,9 @@ var extend = ( function( extending )
base = args.pop() || Class, base = args.pop() || Class,
prototype = new base(); prototype = new base();
var properties = {}, var properties = {},
members = member_builder.initMembers( members = member_builder.initMembers( prototype ),
prototype, prototype, prototype prop_init = member_builder.initMembers(),
),
abstract_methods = abstract_methods =
util.clone( getMeta( base.__cid ).abstractMethods ) util.clone( getMeta( base.__cid ).abstractMethods )
@ -236,8 +235,10 @@ var extend = ( function( extending )
{ {
properties[ name ] = value; properties[ name ] = value;
// build a new property, passing in the other members to compare
// against for preventing nonsensical overrides
member_builder.buildProp( member_builder.buildProp(
members, null, name, value, keywords prop_init, null, name, value, keywords, members
); );
}, },
@ -291,7 +292,7 @@ var extend = ( function( extending )
// set up the new class // set up the new class
var new_class = createCtor( abstract_methods ); var new_class = createCtor( abstract_methods );
attachPropInit( prototype, properties ); attachPropInit( prototype, prop_init );
new_class.prototype = prototype; new_class.prototype = prototype;
new_class.constructor = new_class; new_class.constructor = new_class;
@ -470,6 +471,8 @@ function setupProps( func, abstract_methods, class_id )
*/ */
function attachPropInit( prototype, properties ) function attachPropInit( prototype, properties )
{ {
var prop_pub = properties[ 'public' ];
util.defineSecureProp( prototype, '__initProps', function() util.defineSecureProp( prototype, '__initProps', function()
{ {
// first initialize the parent's properties, so that ours will overwrite // first initialize the parent's properties, so that ours will overwrite
@ -482,11 +485,11 @@ function attachPropInit( prototype, properties )
// initialize each of the properties for this instance to // initialize each of the properties for this instance to
// ensure we're not sharing prototype values // ensure we're not sharing prototype values
for ( prop in properties ) for ( prop in prop_pub )
{ {
// initialize the value with a clone to ensure that they do // initialize the value with a clone to ensure that they do
// not share references (and therefore, data) // not share references (and therefore, data)
this[ prop ] = util.clone( properties[ prop ] ); this[ prop ] = util.clone( prop_pub[ prop ] );
} }
}); });
} }

View File

@ -62,11 +62,12 @@ exports.initMembers = function( mpublic, mprotected, mprivate )
* *
* @return {undefined} * @return {undefined}
*/ */
exports.buildMethod = function( members, meta, name, value, keywords ) exports.buildMethod = function( members, meta, name, value, keywords, cmp )
{ {
var prev = scanMembers( members, name ); var prev;
if ( prev ) // search for any previous instances of this member
if ( prev = scanMembers( members, name ) )
{ {
// disallow overriding properties with methods // disallow overriding properties with methods
if ( !( prev instanceof Function ) ) if ( !( prev instanceof Function ) )
@ -124,13 +125,15 @@ exports.buildMethod = function( members, meta, name, value, keywords )
* @param {*} value property value * @param {*} value property value
* *
* @param {Object.<string,boolean>} keywords parsed keywords * @param {Object.<string,boolean>} keywords parsed keywords
* @param {Object=} cmp optional second member object to scan
* *
* @return {undefined} * @return {undefined}
*/ */
exports.buildProp = function( members, meta, name, value, keywords ) exports.buildProp = function( members, meta, name, value, keywords, cmp )
{ {
// disallow overriding methods with properties // disallow overriding methods with properties
if ( scanMembers( members, name ) instanceof Function ) if ( scanMembers( members, name, cmp ) instanceof Function )
{ {
throw new TypeError( throw new TypeError(
"Cannot override method '" + name + "' with property" "Cannot override method '" + name + "' with property"
@ -226,11 +229,12 @@ function getMemberVisibility( members, keywords )
* *
* @param {{public: Object, protected: Object, private: Object}} members * @param {{public: Object, protected: Object, private: Object}} members
* *
* @param {string} name member to locate * @param {string} name member to locate
* @param {Object=} cmp optional second member object to scan
* *
* @return {*} member, if located, otherwise undefined * @return {*} member, if located, otherwise undefined
*/ */
function scanMembers( members, name ) function scanMembers( members, name, cmp )
{ {
var i = visibility.length, var i = visibility.length,
member = null; member = null;
@ -244,6 +248,14 @@ function scanMembers( members, name )
} }
} }
// if a second comparison object was given, try again using it instead of
// the original members object
if ( cmp !== undefined )
{
return scanMembers( cmp, name );
}
// nothing was found
return undefined; return undefined;
} }

View File

@ -63,7 +63,7 @@ for ( var prop in sub_props )
{ {
assert.equal( assert.equal(
sub_props[ prop ], sub_props[ prop ],
SubFoo.prototype[ prop ], SubFoo()[ prop ],
"Subtype contains its own properties: " + prop "Subtype contains its own properties: " + prop
); );
} }
@ -139,7 +139,7 @@ assert.equal(
); );
assert.notEqual( assert.notEqual(
SubOther.prototype.newFoo, SubOther().newFoo,
undefined, undefined,
"Subtype should contain extended members" "Subtype should contain extended members"
); );

View File

@ -0,0 +1,65 @@
/**
* Tests class member visibility (public, private, protected)
*
* 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 test
*/
var common = require( './common' ),
assert = require( 'assert' ),
Class = common.require( 'class' ),
pub = 'foo',
prot = 'bar',
priv = 'baz',
// new anonymous class instance
foo = Class.extend( {
'public pub': pub,
'protected peeps': prot,
'private parts': priv,
})();
( function testPublicMembersAreAccessbileExternally()
{
assert.equal(
foo.pub,
pub,
"Public properties are accessible via public interface"
);
} )();
( function testProtectedAndPrivateMembersAreNotAccessibleExternally()
{
assert.equal(
foo.peeps,
undefined,
"Protected properties are inaccessible via public interface"
);
assert.equal(
foo.parts,
undefined,
"Private properties are inaccessible via public interface"
);
} )();