From 38c16048cb437d93da136203699feb5673f570fc Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Thu, 27 Jan 2011 22:35:40 -0500 Subject: [PATCH] Began adding protected/private member support - No longer adding ANY properties to prototype - protected/private members no longer part of the public access level --- lib/class.js | 19 +++++----- lib/member_builder.js | 26 ++++++++++---- test/test-class-extend.js | 4 +-- test/test-class-visibility.js | 65 +++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 test/test-class-visibility.js diff --git a/lib/class.js b/lib/class.js index f1ba4e9..aabbba8 100644 --- a/lib/class.js +++ b/lib/class.js @@ -212,10 +212,9 @@ var extend = ( function( extending ) base = args.pop() || Class, prototype = new base(); - var properties = {}, - members = member_builder.initMembers( - prototype, prototype, prototype - ), + var properties = {}, + members = member_builder.initMembers( prototype ), + prop_init = member_builder.initMembers(), abstract_methods = util.clone( getMeta( base.__cid ).abstractMethods ) @@ -236,8 +235,10 @@ var extend = ( function( extending ) { properties[ name ] = value; + // build a new property, passing in the other members to compare + // against for preventing nonsensical overrides 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 var new_class = createCtor( abstract_methods ); - attachPropInit( prototype, properties ); + attachPropInit( prototype, prop_init ); new_class.prototype = prototype; new_class.constructor = new_class; @@ -470,6 +471,8 @@ function setupProps( func, abstract_methods, class_id ) */ function attachPropInit( prototype, properties ) { + var prop_pub = properties[ 'public' ]; + util.defineSecureProp( prototype, '__initProps', function() { // 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 // 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 // not share references (and therefore, data) - this[ prop ] = util.clone( properties[ prop ] ); + this[ prop ] = util.clone( prop_pub[ prop ] ); } }); } diff --git a/lib/member_builder.js b/lib/member_builder.js index ac56890..8d22af9 100644 --- a/lib/member_builder.js +++ b/lib/member_builder.js @@ -62,11 +62,12 @@ exports.initMembers = function( mpublic, mprotected, mprivate ) * * @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 if ( !( prev instanceof Function ) ) @@ -124,13 +125,15 @@ exports.buildMethod = function( members, meta, name, value, keywords ) * @param {*} value property value * * @param {Object.} keywords parsed keywords + + * @param {Object=} cmp optional second member object to scan * * @return {undefined} */ -exports.buildProp = function( members, meta, name, value, keywords ) +exports.buildProp = function( members, meta, name, value, keywords, cmp ) { // disallow overriding methods with properties - if ( scanMembers( members, name ) instanceof Function ) + if ( scanMembers( members, name, cmp ) instanceof Function ) { throw new TypeError( "Cannot override method '" + name + "' with property" @@ -226,11 +229,12 @@ function getMemberVisibility( members, keywords ) * * @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 */ -function scanMembers( members, name ) +function scanMembers( members, name, cmp ) { var i = visibility.length, 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; } diff --git a/test/test-class-extend.js b/test/test-class-extend.js index 31c9e67..38c1f35 100644 --- a/test/test-class-extend.js +++ b/test/test-class-extend.js @@ -63,7 +63,7 @@ for ( var prop in sub_props ) { assert.equal( sub_props[ prop ], - SubFoo.prototype[ prop ], + SubFoo()[ prop ], "Subtype contains its own properties: " + prop ); } @@ -139,7 +139,7 @@ assert.equal( ); assert.notEqual( - SubOther.prototype.newFoo, + SubOther().newFoo, undefined, "Subtype should contain extended members" ); diff --git a/test/test-class-visibility.js b/test/test-class-visibility.js new file mode 100644 index 0000000..2cb6963 --- /dev/null +++ b/test/test-class-visibility.js @@ -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 . + * + * @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" + ); +} )(); +