Fix trait extending of supertype with constructor
Supertypes that extend constructors may now be extended by traits without completely blowing up. Good feature. * lib/Trait.js (__tconstruct): Add function. (createVirtProxy): Use it. * test/Trait/ClassExtendTest.js: Add test.master
parent
92c57c8ffe
commit
b841b9cc5e
48
lib/Trait.js
48
lib/Trait.js
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Provides system for code reuse via traits
|
||||
*
|
||||
* Copyright (C) 2014, 2015, 2016 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2014, 2015, 2016, 2017 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GNU ease.js.
|
||||
*
|
||||
|
@ -498,14 +498,12 @@ function createConcrete( acls )
|
|||
// parent ACLS has access to it (!), which is not prohibited since
|
||||
// JS does not provide a strict typing mechanism...this is a kluge)
|
||||
// and target supertype---that is, what __super calls should
|
||||
// referene
|
||||
'protected ___$$pmo$$': null,
|
||||
'protected ___$$super$$': null,
|
||||
__construct: function( base, pmo )
|
||||
{
|
||||
this.___$$super$$ = base;
|
||||
this.___$$pmo$$ = pmo;
|
||||
},
|
||||
// reference
|
||||
'protected ___$$pmo$$': null,
|
||||
'protected ___$$super$$': null,
|
||||
|
||||
'weak virtual __construct': _fvoid,
|
||||
'override __construct': __tconstruct,
|
||||
|
||||
// mainly for debugging; should really never see this.
|
||||
__name: '#ConcreteTrait#',
|
||||
|
@ -544,6 +542,30 @@ function createConcrete( acls )
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Trait class __construct method
|
||||
*
|
||||
* This is to be used for the `__construct' method of a concrete trait
|
||||
* class. This simply performs low-level trait initialization.
|
||||
*
|
||||
* Just below this function's definition, its `__length' property is set to
|
||||
* Infinity---this avoids supertype compatibility issues based on argument
|
||||
* count (see argument length check in `MemberBuilderValidator').
|
||||
*
|
||||
* @param {Class} base supertype
|
||||
* @param {Object} pmo protected member object
|
||||
*/
|
||||
function __tconstruct( base, pmo )
|
||||
{
|
||||
this.___$$super$$ = base;
|
||||
this.___$$pmo$$ = pmo;
|
||||
}
|
||||
|
||||
// prevent any issues related to supertype compatability (__length is
|
||||
// recognized by easejs to store argument count)
|
||||
__tconstruct.__length = Infinity;
|
||||
|
||||
|
||||
/**
|
||||
* Create virtual method proxies for all virtual members
|
||||
*
|
||||
|
@ -567,6 +589,14 @@ function createVirtProxy( acls, dfn )
|
|||
// f = `field'
|
||||
for ( var f in vmembers )
|
||||
{
|
||||
// constructors may exist when extending a class; they require
|
||||
// special treatment and it makes no sense to create a proxy for
|
||||
// them
|
||||
if ( f === '__construct' )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var vis = ( acls.___$$methods$$['public'][ f ] !== undefined )
|
||||
? 'public'
|
||||
: 'protected';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Tests extending traits from classes
|
||||
*
|
||||
* Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2015, 2017 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GNU ease.js.
|
||||
*
|
||||
|
@ -189,6 +189,47 @@ require( 'common' ).testCase(
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
* Also an implementation detail: when a constructor is present on the
|
||||
* supertype, special care is needed to make sure that we have no errors
|
||||
* in an override---the trait itself has its own constructor.
|
||||
*
|
||||
* Another subtle detail is that our constructor override needs to take
|
||||
* into account that the supertype constructor could have any number of
|
||||
* arguments. Since easejs enforces argument length for overrides, we
|
||||
* need to make sure that the trait will handle this. (In actuality,
|
||||
* the implementation just sets the argument length of the trait class
|
||||
* `__construct' to Infinity.)
|
||||
*/
|
||||
'Trait mixin handles supertype constructor': function()
|
||||
{
|
||||
var ctor_called = 0;
|
||||
|
||||
var C = this.Class(
|
||||
{
|
||||
// notice that this isn't virtual (another implementation quirk
|
||||
// to handle), and notice the argument count (which creates
|
||||
// quite the rainbow if you have semantic coloring for your
|
||||
// editor!)
|
||||
__construct: function( a, b, c, d, e, f, g, h, i, j, k, l )
|
||||
{
|
||||
ctor_called++;
|
||||
}
|
||||
} );
|
||||
|
||||
var T = this.Sut.extend( C, {} );
|
||||
|
||||
this.assertDoesNotThrow( function()
|
||||
{
|
||||
C.use( T )();
|
||||
} );
|
||||
|
||||
// the supertype's constructor should be invoked only _once_ (we
|
||||
// were mixed into an object that should have already invoked it)
|
||||
this.assertEqual( 1, ctor_called );
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* This is a corollary, but is still worth testing for assurance.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue