From fd7e0cbef7a2fae80aeb71da9431128152c7fdf4 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sun, 20 Apr 2014 02:28:38 -0400 Subject: [PATCH 1/3] 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, + {} + ); + }, } ); From 004fbd24ad9f164d9e3ee5f2751f1854f46bcb60 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sun, 20 Apr 2014 02:31:28 -0400 Subject: [PATCH 2/3] Added virtual kmask --- lib/prop_parser.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/prop_parser.js b/lib/prop_parser.js index a1e9a23..036d282 100644 --- a/lib/prop_parser.js +++ b/lib/prop_parser.js @@ -44,6 +44,9 @@ var _kmasks = { amods: _keywords[ 'public' ] | _keywords[ 'protected' ] | _keywords[ 'private' ], + + 'virtual': _keywords[ 'abstract' ] + | _keywords[ 'virtual' ], }; From 42b52bb692f616a19b6f46c0610bd3156567c57f Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sun, 20 Apr 2014 02:40:36 -0400 Subject: [PATCH 3/3] Exposing keyword bit values and bitmasks Available through property parser interface --- lib/prop_parser.js | 5 ++++ test/PropParserKeywordsTest.js | 49 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/lib/prop_parser.js b/lib/prop_parser.js index 036d282..6938328 100644 --- a/lib/prop_parser.js +++ b/lib/prop_parser.js @@ -50,6 +50,11 @@ var _kmasks = { }; +// expose magic values +exports.kvals = _keywords; +exports.kmasks = _kmasks; + + /** * Parses property keywords * diff --git a/test/PropParserKeywordsTest.js b/test/PropParserKeywordsTest.js index 913dab6..9816c39 100644 --- a/test/PropParserKeywordsTest.js +++ b/test/PropParserKeywordsTest.js @@ -203,4 +203,53 @@ require( 'common' ).testCase( {} ); }, + + + /** + * As the keyword bit values are magic values, they must be exposed if + * the bitfield is to be used. The bitmasks are useful for quick and + * convenient checks in other parts of the framework. + */ + 'Exposes keyword bit values and masks': function() + { + this.assertOk( this.Sut.kvals ); + this.assertOk( this.Sut.kmasks ); + }, + + + /** + * Access modifier checks are common; ensure that the bitmask can + * properly check them all and does not check for any other keywords. + */ + 'Access modifier bitmask catches all access modifiers': function() + { + var kvals = this.Sut.kvals; + + // this can be easily checked by ensuring that the inclusive logical + // or of all the access modifier bits are no different than the mask + this.assertEqual( + this.Sut.kmasks.amods + | kvals[ 'public' ] + | kvals[ 'protected' ] + | kvals[ 'private' ], + this.Sut.kmasks.amods + ); + }, + + + /** + * Likewise, a virtual bitmask is also useful since it can be denoted by + * multiple keywords (abstract is implicitly virtual). + */ + 'Virtual bitmask catches abstract and virtual keywords': function() + { + var kvals = this.Sut.kvals; + + this.assertEqual( + this.Sut.kmasks[ 'virtual' ] + | kvals[ 'abstract' ] + | kvals[ 'virtual' ], + this.Sut.kmasks[ 'virtual' ] + ); + }, } );