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
commit
21821cd9e7
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
27
lib/class.js
27
lib/class.js
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue