1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
.. index::
   pair: protocol inheritance; design

.. _design-protocol:


Protocol inheritance
====================

.. mps:prefix:: design.mps.protocol


Introduction
------------

:mps:tag:`intro` This document explains the design of the support for class
inheritance in MPS. It is not yet complete. It describes support for
single inheritance of classes. Future extensions will describe
multiple inheritance and the relationship between instances and
classes.

:mps:tag:`readership` This document is intended for any MPS developer.


Purpose
-------

:mps:tag:`purpose.code-maintain` The purpose of the protocol inheritance
design is to ensure that the MPS code base can make use of the
benefits of object-oriented class inheritance to maximize code reuse,
minimize code maintenance and minimize the use of boilerplate code.

:mps:tag:`purpose.related` For related discussion, see
mail.tony.1998-08-28.16-26(0), mail.tony.1998-09-01.11-38(0),
mail.tony.1998-10-06.11-03(0) and other messages in the same threads.


Requirements
------------

:mps:tag:`req.implicit` The object system should provide a means for classes
to inherit the methods of their direct superclasses implicitly for all
functions in the protocol without having to write any explicit code
for each inherited function.

:mps:tag:`req.override` There must additionally be a way for classes to
override the methods of their superclasses.

:mps:tag:`req.next-method` As a result of :mps:ref:`.req.implicit`, classes cannot
make static assumptions about methods used by direct superclasses. The
object system must provide a means for classes to extend (not just
replace) the behaviour of protocol functions, such as a mechanism for
invoking the "next-method".

:mps:tag:`req.ideal.extend` The object system must provide a standard way
for classes to implement the protocol supported by their superclass and
additionally add new methods of their own which can be specialized by
subclasses.

:mps:tag:`req.ideal.multiple-inheritance` The object system should support
multiple inheritance such that sub-protocols can be "mixed in" with
several classes which do not themselves support identical protocols.


Overview
--------

.. c:type:: struct ProtocolClassStruct *ProtocolClass

:mps:tag:`overview.root` We start with the root of all conformant class
hierarchies, which is called :c:type:`ProtocolClass`. This is an "abstract"
class (that is, it has no direct instances, but it is intended to have
subclasses). To use Dylan terminology, instances of its subclasses are
"general" instances of ProtocolClass. They look like this::

     Instance Object                    Class Object

     --------------------              --------------------
     |     sig          |    |-------->|    sig           |
     --------------------    |         --------------------
     |     class        |----|         |    superclass    |
     --------------------              --------------------
     |     ...          |              |    coerceInst    |
     --------------------              --------------------
     |     ...          |              |    coerceClass   |
     --------------------              --------------------
     |                  |              |     ...          |

:mps:tag:`overview.inherit` Classes inherit the protocols supported by their
superclasses. By default they have the same methods as the class(es)
from which they inherit.

:mps:tag:`overview.inherit.specialize` Classes may specialize the behaviour
of their superclass. They do this by by overriding methods or other
fields in the class object.

:mps:tag:`overview.extend` Classes may extend the protocols supported by
their superclasses by adding new fields for methods or other data.

:mps:tag:`overview.sig.inherit` Classes will contain (possibly several)
signatures. Classes must not specialize (override) the signatures they
inherit from their superclasses.

:mps:tag:`overview.sig.extend` If a class definition extends a protocol, it
is normal policy for the class definition to include a new signature
as the last field in the class object.

:mps:tag:`overview.coerce-class` Each class contains a ``coerceClass``
field. This contains a method which can find the part of the class
object which implements the protocols of a supplied superclass
argument (if, indeed, the argument *is* a superclass). This function
may be used for testing subclass/superclass relationships, and it also
provides support for multiple inheritance.

:mps:tag:`overview.coerce-inst` Each class contains a ``coerceInst`` field.
This contains a method which can find the part of an instance object
which contains the instance slots of a supplied superclass argument
(if, indeed, the argument *is* a superclass). This function may be
used for testing whether an object is an instance of a given class,
and it also provides support for multiple inheritance.

:mps:tag:`overview.superclass` Each class contains a ``superclass`` field.
This enables classes to call "next-method", as well as enabling the
coercion functions.

:mps:tag:`overview.next-method` A specialized method in a class can make use
of an overridden method from a superclass by accessing the method from
the appropriate field in the superclass object and calling it. The
superclass may be accessed indirectly from the class's "Ensure"
function when it is statically known (see :mps:ref:`.overview.access`). This
permits "next-method" calls, and is fully scalable in that it allows
arbitrary length method chains. The :c:func:`SUPERCLASS()` macro helps with
this (see :mps:ref:`.int.static-superclass`).

:mps:tag:`overview.next-method.naive` In some cases it is necessary to write
a method which is designed to specialize an inherited method, needs to
call the next-method, and yet the implementation doesn't have static
knowledge of the superclass. This might happen because the specialized
method is designed to be reusable by many class definitions. The
specialized method can usually locate the class object from one of the
parameters passed to the method. It can then access the superclass
through the ``superclass`` field of the class, and hence call the next
method. This technique has some limitations and doesn't support longer
method chains. It is also dependent on none of the class definitions
which use the method having any subclasses.

:mps:tag:`overview.access` Classes must be initialized by calls to
functions, since it is these function calls which copy properties from
superclasses. Each class must provide an "Ensure" function, which
returns the canonical copy of the class. The canonical copy may reside
in static storage, but no MPS code may refer to that static storage by
name.

:mps:tag:`overview.naming` There are some strict naming conventions which
must be followed when defining and using classes. The use is
obligatory because it is assumed by the macros which support the
definition and inheritance mechanism. For every class :c:type:`SomeClass`,
we insist upon the following naming conventions:-

* :c:type:`SomeClassStruct`

   names the type of the structure for the protocol class. This might
   be a ``typedef`` which aliases the type to the type of the
   superclass, but if the class has extended the protocols of the
   superclass the it will be a type which contains the new class
   fields.

* :c:type:`SomeClass`

   names the type ``*SomeClassStruct``. This might be a ``typedef``
   which aliases the type to the type of the superclass, but if the
   class has extended the protocols of the superclass then it will be
   a type which contains the new class fields.

* :c:func:`EnsureSomeClass()`

   names the function that returns the initialized class object.



Interface
---------

Class definition
................

.. c:function:: DEFINE_CLASS(className, var)

:mps:tag:`int.define-class` Class definition is performed by the macro
:c:func:`DEFINE_CLASS()`. A call to the macro must be followed by a body of
initialization code in braces ``{}``. The parameter ``className`` is
used to name the class being defined. The parameter ``var`` is used to
name a local variable of type ``className``, which is defined by the
macro; it refers to the canonical storage for the class being defined.
This variable may be used in the initialization code. (The macro
doesn't just pick a name implicitly because of the danger of a name
clash with other names used by the programmer). A call to
``DEFINE_CLASS(SomeClass, var)`` defines the :c:func:`EnsureSomeClass()`
function, defines some static storage for the canonical class object,
and defines some other things to ensure the class gets initialized
exactly once.

.. c:function:: DEFINE_ALIAS_CLASS(className, typeName, var)

:mps:tag:`int.define-alias-class` A convenience macro
:c:func:`DEFINE_ALIAS_CLASS()` is provided which both performs the class
definition and defines the types :c:type:`SomeClass` and ``SomeClass
struct`` as aliases for some other class types. This is particularly
useful for classes which simply inherit, and don't extend protocols.
The macro call ``DEFINE_ALIAS_CLASS(className, superName, var)`` is
exactly equivalent to the following::

     typedef superName className;
     typedef superNameStruct classNameStruct;
     DEFINE_CLASS(className, var)

:mps:tag:`int.define-special` If classes are particularly likely to be
subclassed without extension, the class implementor may choose to
provide a convenience macro which expands into :c:func:`DEFINE_ALIAS_CLASS()`
with an appropriate name for the superclass. For example, there might
be a macro for defining pool classes such that the macro call
``DEFINE_POOL_CLASS(className, var)`` is exactly equivalent to the
macro call ``DEFINE_ALIAS_CLASS(className, PoolClass, var)``. It may
also be convenient to define a static superclass accessor macro at the
same time (see :mps:ref:`.int.static-superclass.special`).


Single inheritance
..................

.. c:function:: INHERIT_CLASS(thisClassCoerced, parentName)

:mps:tag:`int.inheritance` Class inheritance details must be provided in the
class initialization code (see :mps:ref:`.int.define-class`). Inheritance is
performed by the macro :c:func:`INHERIT_CLASS()`. A call to this macro will
make the class being defined a direct subclass of ``parentClassName``
by ensuring that all the fields of the parent class are copied into
``thisClass``, and setting the superclass field of ``thisClass`` to be
the parent class object. The parameter ``thisClassCoerced`` must be of
type ``parentClassName``. If the class definition defines an alias
class (see :mps:ref:`.int.define-alias-class`), then the variable named as the
second parameter to :c:func:`DEFINE_CLASS()` will be appropriate to pass to
:c:func:`INHERIT_CLASS()`.


Specialization
..............

:mps:tag:`int.specialize` Class specialization details must be given
explicitly in the class initialization code (see
:mps:ref:`.int.define-class`). This must happen *after* the inheritance
details are given (see :mps:ref:`.int.inheritance`).


Extension
.........

:mps:tag:`int.extend` To extend the protocol when defining a new class, a
new type must be defined for the class structure. This must embed the
structure for the primarily inherited class as the first field of the
structure. Class extension details must be given explicitly in the
class initialization code (see :mps:ref:`.int.define-class`). This must happen
*after* the inheritance details are given (see :mps:ref:`.int.inheritance`).


Introspection
.............

:mps:tag:`introspect.c-lang` The design includes a number of introspection
functions for dynamically examining class relationships. These
functions are polymorphic and accept arbitrary subclasses of
:c:type:`ProtocolClass`. C doesn't support such polymorphism. So although
these have the semantics of functions (and could be implemented as
functions in another language with compatible calling conventions)
they are actually implemented as macros. The macros are named as
method-style macros despite the fact that this arguably contravenes
guide.impl.c.macro.method. The justification for this is that this
design is intended to promote the use of polymorphism, and it breaks
the abstraction for the users to need to be aware of what can and
can't be expressed directly in C function syntax. These functions all
have names ending in ``Poly`` to identify them as polymorphic
functions.

.. c:function:: ProtocolClassSuperclassPoly(class)

:mps:tag:`int.superclass` An introspection function which returns the direct
superclass of class object ``class``.

.. c:function:: SUPERCLASS(className)

:mps:tag:`int.static-superclass` An introspection macro which returns the
direct superclass given a class name, which must (obviously) be
statically known. The macro expands into a call to the ensure function
for the class name, so this must be in scope (which may require a
forward declaration). The macro is useful for next-method calls (see
:mps:ref:`.overview.next-method`). The superclass is returned with type
:c:type:`ProtocolClass` so it may be necessary to cast it to the type for
the appropriate subclass.

:mps:tag:`int.static-superclass.special` Implementors of classes which are
designed to be subclassed without extension may choose to provide a
convenience macro which expands into a call to :c:macro:`SUPERCLASS` along
with a type cast. For example, there might be a macro for finding pool
superclasses such that the macro call ``POOL_SUPERCLASS(className)``
is exactly equivalent to ``(PoolClass)SUPERCLASS(className)``. It's
convenient to define these macros alongside the convenience class
definition macro (see :mps:ref:`.int.define-special`).

.. c:function:: ClassOfPoly(inst)

:mps:tag:`int.class` An introspection function which returns the class of
which ``inst`` is a direct instance.

.. c:function:: IsSubclassPoly(sub, super)

:mps:tag:`int.subclass` An introspection function which returns a :c:type:`Bool`
indicating whether ``sub`` is a subclass of ``super``. That is, it is
a predicate for testing subclass relationships.


Multiple inheritance
....................

:mps:tag:`int.mult-inherit` Multiple inheritance involves an extension of
the protocol (see :mps:ref:`.int.extend`) and also multiple uses of the single
inheritance mechanism (see :mps:ref:`.int.inheritance`). It also requires
specialized methods for ``coerceClass`` and ``coerceInst`` to be
written (see :mps:ref:`.overview.coerce-class` and :mps:ref:`.overview.coerce-inst`).
Documentation on support for multiple inheritance is under
construction. This facility is not currently used. The basic idea is
described in mail.tony.1998-10-06.11-03(0).


Protocol guidelines
...................

:mps:tag:`guide.fail` When designing an extensible function which might
fail, the design must permit the correct implementation of the
failure-case code. Typically, a failure might occur in any method in
the chain. Each method is responsible for correctly propagating
failure information supplied by superclass methods and for managing
it's own failures.

:mps:tag:`guide.fail.before-next` Dealing with a failure which is detected
before any next-method call is made is similar to a fail case in any
non-extensible function. See :mps:ref:`.example.fail` below.

:mps:tag:`guide.fail.during-next` Dealing with a failure returned from a
next-method call is also similar to a fail case in any non-extensible
function. See :mps:ref:`.example.fail` below.

:mps:tag:`guide.fail.after-next` Dealing with a failure which is detected
after the next methods have been successfully invoked is more complex.
If this scenario is possible, the design must include an
"anti-function", and each class must ensure that it provides a method
for the anti-method which will clean up any resources which are
claimed after a successful invocation of the main method for that
class. Typically the anti-function would exist anyway for clients of
the protocol (for example, "finish" is an anti-function for "init").
The effect of the next-method call can then be cleaned up by calling
the anti-method for the superclass. See :mps:ref:`.example.fail` below.


Example
.......

:mps:tag:`example.inheritance` The following example class definition shows
both inheritance and specialization. It shows the definition of the
class :c:type:`EPDRPoolClass`, which inherits from :c:type:`EPDLPoolClass` and has
specialized values of the ``name``, ``init``, and ``alloc`` fields.
The type :c:type:`EPDLPoolClass` is an alias for :c:type:`PoolClass`. ::

    typedef EPDLPoolClass EPDRPoolClass;
    typedef EPDLPoolClassStruct EPDRPoolClassStruct;

    DEFINE_CLASS(EPDRPoolClass, this)
    {
        INHERIT_CLASS(this, EPDLPoolClass);
        this->name = "EPDR";
        this->init = EPDRInit;
        this->alloc = EPDRAlloc;
    }

:mps:tag:`example.extension` The following (hypothetical) example class
definition shows inheritance, specialization and also extension. It
shows the definition of the class :c:type:`EPDLDebugPoolClass`, which
inherits from :c:type:`EPDLPoolClass`, but also implements a method for
checking properties of the pool. ::

    typedef struct EPDLDebugPoolClassStruct {
        EPDLPoolClassStruct epdl;
        DebugPoolCheckMethod check;
        Sig sig;
    } EPDLDebugPoolClassStruct;

    typedef EPDLDebugPoolClassStruct *EPDLDebugPoolClass;

    DEFINE_CLASS(EPDLDebugPoolClass, this)
    {
        EPDLPoolClass epdl = &this->epdl;
        INHERIT_CLASS(epdl, EPDLPoolClass);
        epdl->name = "EPDLDBG";
        this->check = EPDLDebugCheck;
        this->sig = EPDLDebugSig;
    }

:mps:tag:`example.fail` The following example shows the implementation of
failure-case code for an "init" method, making use of the "finish"
anti-method::

    static Res mySegInit(Seg seg, Pool pool, Addr base, Size size, 
                         Bool reservoirPermit, va_list args)
    {
        SegClass super;
        MYSeg myseg;
        OBJ1 obj1;
        Res res;
        Arena arena;

        AVERT(Seg, seg);
        myseg = SegMYSeg(seg);
        AVERT(Pool, pool);
        arena = PoolArena(pool);

        /* Ensure the pool is ready for the segment */
        res = myNoteSeg(pool, seg);
        if(res != ResOK)
          goto failNoteSeg;

        /* Initialize the superclass fields first via next-method call */
        super = (SegClass)SUPERCLASS(MYSegClass);
        res = super->init(seg, pool, base, size, reservoirPermit, args);
        if(res != ResOK)
          goto failNextMethods;

        /* Create an object after the next-method call */
        res = ControlAlloc(&obj1, arena, sizeof(OBJ1Struct), reservoirPermit);
        if(res != ResOK)
          goto failObj1;

        myseg->obj1 = obj1
        return ResOK;

    failObj1:
        /* call the anti-method for the superclass */
        super->finish(seg);
    failNextMethods:
        /* reverse the effect of myNoteSeg */
        myUnnoteSeg(pool, seg);
    failNoteSeg:
        return res;
    }


Implementation
--------------

:mps:tag:`impl.derived-names` The :c:func:`DEFINE_CLASS()` macro derives some
additional names from the class name as part of it's implementation.
These should not appear in the source code - but it may be useful to
know about this for debugging purposes. For each class definition for
class :c:type:`SomeClass`, the macro defines the following:

* ``extern SomeClass EnsureSomeClass(void);``

  The class accessor function. See :mps:ref:`.overview.naming`.

* ``static Bool protocolSomeClassGuardian;``

  A Boolean which indicates whether the class has been initialized
  yet.

* ``static void protocolEnsureSomeClass(SomeClass);``

  A function called by :c:func:`EnsureSomeClass()`. All the class
  initialization code is actually in this function.

* ``static SomeClassStruct protocolSomeClassStruct;``

  Static storage for the canonical class object.

:mps:tag:`impl.init-once` Class objects only behave according to their
definition after they have been initialized, and class protocols may
not be used before initialization has happened. The only code which is
allowed to see a class object in a partially initialized state is the
initialization code itself -- and this must take care not to pass the
object to any other code which might assume it is initialized. Once a
class has been initialized, the class might have a client. The class
must not be initialized again when this has happened, because the
state is not necessarily consistent in the middle of an initialization
function. The initialization state for each class is stored in a
Boolean "guardian" variable whose name is derived from the class name
(see :mps:ref:`.impl.derived-names`). This ensures the initialization happens
only once. The path through the :c:func:`EnsureSomeClass()` function should be
very fast for the common case when this variable is :c:macro:`TRUE`, and the
class has already been initialized, as the canonical static storage
can simply be returned in that case. However, when the value of the
guardian is :c:macro:`FALSE`, the class is not initialized. In this case, a
call to :c:func:`EnsureSomeClass()` must first execute the initialization code
and then set the guardian to :c:macro:`TRUE`. However, this must happen
atomically (see :mps:ref:`.impl.init-lock`).

:mps:tag:`impl.init-lock` There would be the possibility of a race condition
if :c:func:`EnsureSomeClass()` were called concurrently on separate threads
before :c:type:`SomeClass` has been initialized. The class must not be
initialized more than once, so the sequence test-guard, init-class,
set-guard must be run as a critical region. It's not sufficient to use
the arena lock to protect the critical region, because the class
object might be shared between multiple arenas. The :c:func:`DEFINE_CLASS()`
macro uses a global recursive lock instead. The lock is only claimed
after an initial unlocked access of the guard variable shows that the
class is not initialized. This avoids any locking overhead for the
common case where the class is already initialized. This lock is
provided by the lock module -- see design.mps.lock(0).