From fd7e0cbef7a2fae80aeb71da9431128152c7fdf4 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sun, 20 Apr 2014 02:28:38 -0400 Subject: [PATCH] Support for implicit private members Members with underscore prefixes are now implicitly private, which follows common convention. See test case comments for rationale. --- lib/prop_parser.js | 55 ++++++++++++++++++++++-------- test/PropParserKeywordsTest.js | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/lib/prop_parser.js b/lib/prop_parser.js index 6b9489f..a1e9a23 100644 --- a/lib/prop_parser.js +++ b/lib/prop_parser.js @@ -24,16 +24,26 @@ * @type {Object.} */ var _keywords = { - 'public': true, - 'protected': true, - 'private': true, - 'static': true, - 'abstract': true, - 'const': true, - 'virtual': true, - 'override': true, - 'proxy': true, - 'weak': true, + 'public': 1, + 'protected': 1<<1, + 'private': 1<<2, + 'static': 1<<3, + 'abstract': 1<<4, + 'const': 1<<5, + 'virtual': 1<<6, + 'override': 1<<7, + 'proxy': 1<<8, + 'weak': 1<<9, +}; + +/** + * Keyword masks for conveniently checking the keyword bitfield + * @type {Object.} + */ +var _kmasks = { + amods: _keywords[ 'public' ] + | _keywords[ 'protected' ] + | _keywords[ 'private' ], }; @@ -48,6 +58,7 @@ exports.parseKeywords = function ( prop ) { var name = prop, keywords = [], + bitwords = 0x00, keyword_obj = {}; prop = ''+( prop ); @@ -58,27 +69,41 @@ exports.parseKeywords = function ( prop ) { name = keywords.pop(); - var i = keywords.length, - keyword = ''; - + var i = keywords.length; while ( i-- ) { - keyword = keywords[ i ]; + var keyword = keywords[ i ], + kval = _keywords[ keyword ]; // ensure the keyword is recognized - if ( !_keywords[ keyword ] ) + if ( !kval ) { throw Error( "Unexpected keyword for '" + name + "': " + keyword ); } + // ease-of-access keyword_obj[ keyword ] = true; + + // permits quick and concise checks + bitwords |= kval; } } + // members with an underscore prefix are implicitly private, unless an + // access modifier is explicitly provided; double-underscore is ingored, + // as they denote special members that do not become part of the + // prototype and are reserved by ease.js + if ( ( name.match( /^_[^_]/ ) && !( bitwords & _kmasks.amods ) ) ) + { + keyword_obj[ 'private' ] = true; + bitwords |= _keywords[ 'private' ]; + } + return { name: name, keywords: keyword_obj, + bitwords: bitwords, }; } diff --git a/test/PropParserKeywordsTest.js b/test/PropParserKeywordsTest.js index 592e41f..913dab6 100644 --- a/test/PropParserKeywordsTest.js +++ b/test/PropParserKeywordsTest.js @@ -141,4 +141,66 @@ require( 'common' ).testCase( this.assertFail( "Should not permit unknown keywords" ); }, + + + /** + * It's accepted convention in nearly every modern object-oriented + * language that underscore-prefixed members denote private. (Granted, + * the Java community sometimes uses underscore suffixes, but that's + * considerably less common in the JavaScript community.) + * + * For the sake of conciseness, this allows omission of the `private' + * keyword; this, coupled with the fact that all non-underscore-prefixed + * members are public by default, satisfies the two most common + * visibility modifiers for classes and allows a definition style more + * natural to JavaScript developers from prototypal development. + */ + 'Implciity marks underscore-prefixed members as private': function() + { + this.assertDeepEqual( + this.Sut.parseKeywords( '_foo' ).keywords, + { 'private': true } + ); + }, + + + /** + * All that said, we want users to be able to do what they want. Let's + * have explicit access modifier declarations override the implicit + * behavior rather than providing confusing errors (because multiple + * access modifiers were provided). + */ + 'Fields are not implicitly private with explicit access modifier': + function() + { + this.assertDeepEqual( + this.Sut.parseKeywords( 'public _foo' ).keywords, + { 'public': true } + ); + + this.assertDeepEqual( + this.Sut.parseKeywords( 'protected _foo' ).keywords, + { 'protected': true } + ); + + this.assertDeepEqual( + this.Sut.parseKeywords( 'private _foo' ).keywords, + { 'private': true } + ); + }, + + + /** + * Double-underscore members are reserved by ease.js for special purposes + * and are not included as part of the prototype chain. Further, if we + * did not have this exception, then __construct would be marked as + * private, which would be in error. + */ + 'Double-underscore members are not implicitly private': function() + { + this.assertDeepEqual( + this.Sut.parseKeywords( '__foo' ).keywords, + {} + ); + }, } );