1
0
Fork 0

Fix for subtypes of prototype subtypes

When extending a class that is a subtype of a prototype, it would clobber
___$$vis$$, yielding a useless class. See the test case for a detailed
description.
protolib
Mike Gerwitz 2014-04-29 10:47:19 -04:00
commit 21821cd9e7
No known key found for this signature in database
GPG Key ID: F22BB8158EE30EAB
3 changed files with 61 additions and 5 deletions

View File

@ -365,7 +365,8 @@ exports.prototype.build = function extend( _, __ )
// if we are inheriting from a prototype, we must make sure that all // if we are inheriting from a prototype, we must make sure that all
// properties initialized by the ctor are implicitly public; otherwise, // properties initialized by the ctor are implicitly public; otherwise,
// proxying will fail to take place // proxying will fail to take place
if ( !( prototype instanceof exports.ClassBase ) ) // TODO: see Class.isA TODO
if ( prototype.___$$vis$$ === undefined )
{ {
this._discoverProtoProps( prototype, prop_init ); this._discoverProtoProps( prototype, prop_init );
} }

View File

@ -148,6 +148,9 @@ module.exports.use = function( traits )
}; };
var _dummyclass = { prototype: {} };
var _dummyinst = { constructor: { prototype: {} } };
/** /**
* Determines whether the provided object is a class created through ease.js * Determines whether the provided object is a class created through ease.js
* *
@ -157,9 +160,18 @@ module.exports.use = function( traits )
*/ */
module.exports.isClass = function( obj ) module.exports.isClass = function( obj )
{ {
obj = obj || {}; obj = obj || _dummyclass;
return ( obj.prototype instanceof ClassBuilder.ClassBase ) if ( !obj.prototype )
{
return false;
}
// TODO: this just checks one of many internal fields; we need something
// more formal (cannot use a strict ClassBase check because it will fail
// when extending prototypes)
return ( ( obj.prototype.___$$vis$$ !== undefined )
|| ( obj.prototype instanceof ClassBuilder.ClassBase ) )
? true ? true
: false : false
; ;
@ -177,9 +189,16 @@ module.exports.isClass = function( obj )
*/ */
module.exports.isClassInstance = function( obj ) module.exports.isClassInstance = function( obj )
{ {
obj = obj || {}; obj = obj || _dummyinst;
return ( obj instanceof ClassBuilder.ClassBase ) if ( !obj.constructor || !obj.constructor.prototype )
{
return false;
}
// TODO: see isClass TODO
return ( ( obj.constructor.prototype.___$$vis$$ !== undefined )
|| ( obj instanceof ClassBuilder.ClassBase ) )
? true ? true
: false; : false;
}; };

View File

@ -319,6 +319,42 @@ require( 'common' ).testCase(
}, },
/**
* This is a regression test for an interesting (and particularily
* nasty) bug for a situation that is probably reasonably rare. The
* original check for a non-class supertype checked whether the
* supertype was an instance of the internal base class. While this
* works, it unforunately causes problems for subtypes of the class that
* extended the prototype---the check will fail, since there is no
* ClassBase in the prototype chain.
*
* This resulted in it processing the class fields, which ended up
* overwriting ___$$vis$$, which clobbered all the methods. Doh.
*/
'Subtypes of prototype subtypes yield stable classes': function()
{
function P() {};
// sub-subtype of P
var expected = {};
var C = this.Class.extend( P, {} ).extend(
{
foo: function() { return expected; }
} );
var inst = C();
// this should be recognized as a class (prior to the fix, it was
// not), and inst should be an instance of a class
this.assertOk( this.Class.isClass( C ) );
this.assertOk( this.Class.isClassInstance( inst ) );
this.assertOk( this.Class.isA( C, inst ) );
// before the fix, foo is undefined since ___$$vis$$ was clobbered
this.assertStrictEqual( inst.foo(), expected );
},
/** /**
* When prototypally extending a class, it is not wise to invoke the * When prototypally extending a class, it is not wise to invoke the
* constructor (just like ease.js does not invoke the constructor of * constructor (just like ease.js does not invoke the constructor of