1
0
Fork 0

Added support for implicit private members

Members with an underscore prefix are now implicitly private, which follows
conventions established in many object-oriented languages. This allows for a
concise definition style familiar to prototypal (and I suppose Ruby)
programmers.
newmaster
Mike Gerwitz 2014-04-20 02:41:07 -04:00
commit d5965f4672
No known key found for this signature in database
GPG Key ID: F22BB8158EE30EAB
2 changed files with 159 additions and 15 deletions

View File

@ -24,18 +24,36 @@
* @type {Object.<string,boolean>} * @type {Object.<string,boolean>}
*/ */
var _keywords = { var _keywords = {
'public': true, 'public': 1,
'protected': true, 'protected': 1<<1,
'private': true, 'private': 1<<2,
'static': true, 'static': 1<<3,
'abstract': true, 'abstract': 1<<4,
'const': true, 'const': 1<<5,
'virtual': true, 'virtual': 1<<6,
'override': true, 'override': 1<<7,
'proxy': true, 'proxy': 1<<8,
'weak': true, 'weak': 1<<9,
}; };
/**
* Keyword masks for conveniently checking the keyword bitfield
* @type {Object.<string,integer>}
*/
var _kmasks = {
amods: _keywords[ 'public' ]
| _keywords[ 'protected' ]
| _keywords[ 'private' ],
'virtual': _keywords[ 'abstract' ]
| _keywords[ 'virtual' ],
};
// expose magic values
exports.kvals = _keywords;
exports.kmasks = _kmasks;
/** /**
* Parses property keywords * Parses property keywords
@ -48,6 +66,7 @@ exports.parseKeywords = function ( prop )
{ {
var name = prop, var name = prop,
keywords = [], keywords = [],
bitwords = 0x00,
keyword_obj = {}; keyword_obj = {};
prop = ''+( prop ); prop = ''+( prop );
@ -58,27 +77,41 @@ exports.parseKeywords = function ( prop )
{ {
name = keywords.pop(); name = keywords.pop();
var i = keywords.length, var i = keywords.length;
keyword = '';
while ( i-- ) while ( i-- )
{ {
keyword = keywords[ i ]; var keyword = keywords[ i ],
kval = _keywords[ keyword ];
// ensure the keyword is recognized // ensure the keyword is recognized
if ( !_keywords[ keyword ] ) if ( !kval )
{ {
throw Error( throw Error(
"Unexpected keyword for '" + name + "': " + keyword "Unexpected keyword for '" + name + "': " + keyword
); );
} }
// ease-of-access
keyword_obj[ keyword ] = true; 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 { return {
name: name, name: name,
keywords: keyword_obj, keywords: keyword_obj,
bitwords: bitwords,
}; };
} }

View File

@ -141,4 +141,115 @@ require( 'common' ).testCase(
this.assertFail( "Should not permit unknown keywords" ); 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,
{}
);
},
/**
* 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' ]
);
},
} ); } );