cfad47cfa3/t3compiler/tads3/lib/multmeth.t

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#charset "us-ascii"
2
3
/*
4
 *   Copyright (c) 2008 Michael J. Roberts.  All Rights Reserved.
5
 *   
6
 *   This module provides the run-time component of "multi-methods" in TADS
7
 *   3.  This works with the compiler to implement a multiple-dispatch
8
 *   system.
9
 *   
10
 *   Multi-methods are essentially a combination of regular object methods
11
 *   and "overloaded functions" in languages like C++.  Like a regular
12
 *   object method, multi-methods are polymorphic: you can define several
13
 *   incarnations of the same function name, with different parameter
14
 *   types, the system picks the right binding for each invocation
15
 *   dynamically, based on the actual argument values at run-time.  Unlike
16
 *   regular methods, though, the selection is made on ALL of the argument
17
 *   types, not just a special "self" argument.  In that respect,
18
 *   multi-methods are like overloaded functions in C++; but multi-methods
19
 *   differ from C++ overloading in that the selection of which method to
20
 *   call is made dynamically at run-time, not at compile time.
21
 *   
22
 *   There are two main uses for multi-methods.
23
 *   
24
 *   First, most obviously, multi-methods provide what's known as "multiple
25
 *   dispatch" semantics.  There are some situations (actually, quite a
26
 *   few) where the ordinary Object Oriented notion of polymorphism -
27
 *   selecting a method based on a single target object - doesn't quite do
28
 *   the trick, because what you really want to do is select a particular
29
 *   method based on the *combination* of objects involved in an operation.
30
 *   Some canonical examples are calculating intersections of shapes in a
31
 *   graphics program, where you want to select a specialized "Rectangle +
32
 *   Circle" routine in one case and a "Line + Polygon" routine in another;
33
 *   or performing file format conversions, where you want to select, say,
34
 *   a specialized "JPEG to PNG" routine.  In an IF context, the obvious
35
 *   use is for carrying out multi-object verbs, where you might want a
36
 *   special routine for PUT (liquid) IN (vessel), and another for PUT
37
 *   (object) IN (container).
38
 *   
39
 *   Second, multi-methods offer a way of extending a class without having
40
 *   to change the class's source code.  Since a multi-method is defined
41
 *   externally to any classes it refers to, you can create a method that's
42
 *   polymorphic on class type - just like a regular method - but as a
43
 *   syntactically stand-alone function.  This feature isn't as important
44
 *   in TADS as in some other languages, since TADS lets you do essentially
45
 *   the same thing with the "modify" syntax; but for some purposes the
46
 *   multi-method approach might be preferable aesthetically, since it's
47
 *   wholly external to the class rather than a sort of lexically separate
48
 *   continuation of the class's code.  (However, as a practical matter,
49
 *   it's not all that different; our implementation of multi-methods does
50
 *   in fact modify the original class object, since we store the binding
51
 *   information in the class objects.)  
52
 */
53
54
#include <tads.h>
55
56
57
/* ------------------------------------------------------------------------ */
58
/* 
59
 *   Invoke a multi-method function.  For an expression of the form
60
 *   
61
 *.     f(a, b, ...)
62
 *   
63
 *   where 'f' has been declared as a multi-method, the compiler will
64
 *   actually generate code that invokes this function, like so:
65
 *   
66
 *.     _multiMethodCall(baseFunc, params);
67
 *   
68
 *   'baseFunc' is a function pointer giving the base function; this is a
69
 *   pointer to the common stub function that the compiler generates to
70
 *   identify all of the multi-methods with a given name.  'params' is a
71
 *   list giving the actual parameter values for invoking the function.
72
 *   
73
 *   Our job is to find the actual run-time binding for the function given
74
 *   the actual parameters, and invoke it.  
75
 */
76
_multiMethodCall(baseFunc, args)
77
{
78
    /* get the function binding lookup information */
79
    local info = _multiMethodRegistry.boundFuncTab_[baseFunc];
80
81
    /* it's an error if there's no binding */
82
    if (info == nil)
83
        throw new UnboundMultiMethod(baseFunc, args);
84
85
    /* 
86
     *   Look up the function binding based on the arguments.  To ensure
87
     *   that we match a function with the correct number of argument, we
88
     *   have to explicitly add the last-argument placeholder to the list. 
89
     */
90
    local func = _multiMethodSelect(info, args + _multiMethodEndOfList);
91
92
    /* if we found a binding, invoke it; otherwise throw an error */
93
    if (func == nil)
94
        throw new UnboundMultiMethod(baseFunc, args);
95
    else
96
        return (func)(args...);
97
}
98
99
/*
100
 *   Invoke the base multi-method inherited from the given multi-method.
101
 *   'fromFunc' is a pointer to a multi-method, presumably the one
102
 *   currently running; we look up the next in line in inheritance order
103
 *   and invoke it with the given argument list.  
104
 */
105
_multiMethodCallInherited(fromFunc, [args])
106
{
107
#ifdef MULTMETH_STATIC_INHERITED
108
109
    /* static mode - get the cached inheritance information */
110
    local inh = _multiMethodRegistry.inhTab_[fromFunc];
111
112
#else
113
114
    /* dynamic mode - get the base function binding */
115
    local info = _multiMethodRegistry.boundFuncTab_[
116
        _multiMethodRegistry.baseFuncTab_[fromFunc]];
117
118
    /* it's an error if it doesn't exist */
119
    if (info == nil)
120
        throw new UnboundInheritedMultiMethod(fromFunc, args);
121
122
    /* look up the inherited function based on the actual parameters */
123
    local inh = _multiMethodInherit(
124
        fromFunc, info, args + _multiMethodEndOfList);
125
126
#endif
127
    
128
    /* it's an error if there's no inherited binding */
129
    if (inh == nil)
130
        throw new UnboundInheritedMultiMethod(fromFunc, args);
131
132
    /* call it */
133
    return (inh)(args...);
134
}
135
136
137
/* ------------------------------------------------------------------------ */
138
/*
139
 *   Get a pointer to a resolved multi-method function.  This takes a
140
 *   pointer to the base function for the multi-method and a list of actual
141
 *   argument values, and returns a function pointer to the specific
142
 *   version of the multi-method that would be invoked if you called the
143
 *   multi-method with that argument list.
144
 *   
145
 *   For example, if you want to get a pointer to the function that would
146
 *   be called if you were to call foo(x, y, z), you'd use:
147
 *   
148
 *.     local func = getMultiMethodPointer(foo, x, y, z);
149
 *   
150
 *   We return a pointer to the individual multi-method function that
151
 *   matches the argument list, or nil if there's no matching multi-method.
152
 */
153
getMultiMethodPointer(baseFunc, [args])
154
{
155
    /* get the function binding lookup information */
156
    local info = _multiMethodRegistry.boundFuncTab_[baseFunc];
157
    
158
    /* if there's no binding information, return failure */
159
    if (info == nil)
160
        return nil;
161
162
    /* look up and return the function binding based on the arguments */
163
    return _multiMethodSelect(info, args + _multiMethodEndOfList);
164
}
165
166
167
/* ------------------------------------------------------------------------ */
168
/*
169
 *   Resolve a multi-method binding.  This function takes a binding
170
 *   property ID (the property we assign during the registration process to
171
 *   generate the binding tables) and a "remaining" argument list.  This
172
 *   function invokes itself recursively to traverse the arguments from
173
 *   left to right, so at each recursive invocation, we lop off the
174
 *   leftmost argument (the one we're working on currently) and pass in the
175
 *   remaining arguments in the list.
176
 *   
177
 *   We look up the binding property on the first argument in the remaining
178
 *   argument list.  This can yield one of three things:
179
 *   
180
 *   - The trivial result is nil, which means that this binding property
181
 *   has no definition on the first argument.  This doesn't necessarily
182
 *   mean that the whole function is undefined on the arguments; it only
183
 *   means that the current inheritance level we're looking at for the
184
 *   previous argument(s) has no binding.  If we get this result we simply
185
 *   return nil to tell the caller that it must look at an inherited
186
 *   binding for the previous argument.
187
 *   
188
 *   - If the result is a function pointer, it's the bound function.  This
189
 *   is the final result for the recursion, so we simply return it.
190
 *   
191
 *   - Otherwise, the result will be a new property ID, giving the property
192
 *   that resolves the binding for the *next* argument.  In this case, we
193
 *   use this property to resolve the next argument in the list by a
194
 *   recursive invocation.  If that recursive call succeeds (i.e., returns
195
 *   a non-nil value), we're done - we simply return the recursive result
196
 *   as though it were our own.  If it fails, it means that there's no
197
 *   binding for the particular subclass we're currently working on for the
198
 *   first argument - however, there could still be a binding for a parent
199
 *   class of the first argument.  So, we iterate up to any inherited
200
 *   binding for the first argument, and if we find one, we try again with
201
 *   the same recursive call.  We continue up our first argument's class
202
 *   tree until we either find a binding (in which case we return it) or
203
 *   exhaust the class tree (in which case we return nil).  
204
 */
205
_multiMethodSelect(prop, args)
206
{
207
    local obj, binding;
208
209
    /* 
210
     *   Get the first argument from the remaining arguments.  If it's not
211
     *   an object, use the placeholder object for non-object parameter
212
     *   bindings.  
213
     */
214
    local orig = args[1];
215
    if (dataType(orig) not in (TypeObject, TypeList, TypeSString))
216
        orig = _multiMethodNonObjectBindings;
217
218
    /* get the remaining arguments */
219
    args = args.sublist(2);
220
221
    /* 
222
     *   Look up the initial binding - this is simply the value of the
223
     *   binding property for the first argument.  In order to process the
224
     *   inheritance tree later, we'll need to know where we got this
225
     *   definition from, so look up the specific defining object.
226
     *   
227
     *   If the initial binding property isn't defined, or its value is
228
     *   explicitly nil, the function isn't bound (or, in the case of nil,
229
     *   is explicitly unbound).  Inheritance won't help in these cases, so
230
     *   we can immediately return nil to indicate that we don't have a
231
     *   binding.  
232
     */
233
    if ((obj = orig.propDefined(prop, PropDefGetClass)) == nil
234
        || (binding = obj.(prop)) == nil)
235
        return nil;
236
237
    /* 
238
     *   If there are no more arguments, but we didn't just find a final
239
     *   function binding, we don't have enough arguments to match the
240
     *   current multi-method path.  Return failure.  
241
     */
242
    if (args.length() == 0 && dataType(binding) != TypeFuncPtr)
243
        return nil;
244
245
    /* 
246
     *   starting at our current defining object for the first argument,
247
     *   scan up its superclass tree until we find a binding 
248
     */
249
    for (;;)
250
    {
251
        local ret;
252
253
        /* if we have a function pointer, we've found our binding */
254
        if (dataType(binding) == TypeFuncPtr)
255
            return binding;
256
257
        /* 
258
         *   Recursively bind the binding for the remaining arguments.  If
259
         *   we find a binding, we're done - simply return it. 
260
         */
261
        if ((ret = _multiMethodSelect(binding, args)) != nil)
262
            return ret;
263
264
        /* 
265
         *   We didn't find a binding for the remaining arguments, so we
266
         *   must have chosen too specific a binding for the first
267
         *   argument.  Look for an inherited value of the binding property
268
         *   in the next superclass of the object where we found the last
269
         *   binding value.  
270
         */
271
        obj = orig.propInherited(prop, orig, obj, PropDefGetClass);
272
        if (obj == nil)
273
            return nil;
274
275
        /* we found an inherited value, so retrieve it from the superclass */
276
        binding = obj.(prop);
277
    }
278
}
279
280
/*
281
 *   Select the INHERITED version of a multi-method.  This takes a
282
 *   particular version of the multi-method, and finds the next version in
283
 *   inheritance order.
284
 *   
285
 *   This is basically a copy of _multiMethodSelect(), with a small amount
286
 *   of extra logic.  This code repetition isn't good maintenance-wise, and
287
 *   the two functions could in principle be merged into one.  However,
288
 *   doing so would have an efficiency cost to _multiMethodSelect(), which
289
 *   we want to keep as lean as possible.  
290
 */
291
_multiMethodInherit(fromFunc, prop, args)
292
{
293
    return _multiMethodInheritMain(
294
        new _MultiMethodInheritCtx(), fromFunc, prop, args);
295
}
296
297
class _MultiMethodInheritCtx: object
298
    foundFromFunc = nil
299
;
300
    
301
_multiMethodInheritMain(ctx, fromFunc, prop, args)
302
{
303
    local obj, binding;
304
305
    /* 
306
     *   Get the first argument from the remaining arguments.  If it's not
307
     *   an object, use the placeholder object for non-object parameter
308
     *   bindings.  
309
     */
310
    local orig = args[1];
311
    if (dataType(orig) not in (TypeObject, TypeList, TypeSString))
312
        orig = _multiMethodNonObjectBindings;
313
314
    /* get the remaining arguments */
315
    args = args.sublist(2);
316
317
    /* 
318
     *   Look up the initial binding - this is simply the value of the
319
     *   binding property for the first argument.  In order to process the
320
     *   inheritance tree later, we'll need to know where we got this
321
     *   definition from, so look up the specific defining object.
322
     *   
323
     *   If the initial binding property isn't defined, or its value is
324
     *   explicitly nil, the function isn't bound (or, in the case of nil,
325
     *   is explicitly unbound).  Inheritance won't help in these cases, so
326
     *   we can immediately return nil to indicate that we don't have a
327
     *   binding.  
328
     */
329
    if ((obj = orig.propDefined(prop, PropDefGetClass)) == nil
330
        || (binding = obj.(prop)) == nil)
331
        return nil;
332
333
    /* 
334
     *   If there are no more arguments, but we didn't just find a final
335
     *   function binding, we don't have enough arguments to match the
336
     *   current multi-method path.  Return failure.  
337
     */
338
    if (args.length() == 0 && dataType(binding) != TypeFuncPtr)
339
        return nil;
340
341
    /* 
342
     *   starting at our current defining object for the first argument,
343
     *   scan up its superclass tree until we find a binding 
344
     */
345
    for (;;)
346
    {
347
        /* we haven't found a function binding yet */
348
        local ret = nil;
349
350
        /* 
351
         *   we either have a function pointer, in which case it's the
352
         *   actual binding, or a property, in which case it's the next
353
         *   binding level 
354
         */
355
        if (dataType(binding) == TypeFuncPtr)
356
        {
357
            /* this is the binding */
358
            ret = binding;
359
        }
360
        else
361
        {
362
            /* if there are no more arguments, return failure */
363
            if (args.length() == 0)
364
                return nil;
365
366
            /* 
367
             *   Recursively bind the binding for the remaining arguments.
368
             *   If we find a binding, we're done - simply return it.  
369
             */
370
            ret = _multiMethodInheritMain(ctx, fromFunc, binding, args);
371
        }
372
373
        /* check to see if we found a binding */
374
        if (ret != nil)
375
        {
376
            /* 
377
             *   We found a binding.  If we've already found the inheriting
378
             *   version, return the first thing we find, since that's the
379
             *   next inheriting level.  Otherwise, if this is the
380
             *   inheriting version, note that we've found it, but keep
381
             *   looking, since we want to find the next one after that.
382
             *   Otherwise, just keep looking, since we haven't even
383
             *   reached the overriding version yet. 
384
             */
385
            if (ctx.foundFromFunc)
386
                return ret;
387
            else if (ret == fromFunc)
388
                ctx.foundFromFunc = true;
389
        }
390
        
391
        /* 
392
         *   We didn't find a binding for the remaining arguments, so we
393
         *   must have chosen too specific a binding for the first
394
         *   argument.  Look for an inherited value of the binding property
395
         *   in the next superclass of the object where we found the last
396
         *   binding value.  
397
         */
398
        obj = orig.propInherited(prop, orig, obj, PropDefGetClass);
399
        if (obj == nil)
400
            return nil;
401
402
        /* we found an inherited value, so retrieve it from the superclass */
403
        binding = obj.(prop);
404
    }
405
}
406
407
/* ------------------------------------------------------------------------ */
408
/*
409
 *   Unbound multi-method exception.  This is thrown when a call to resolve
410
 *   a multi-method fails to find a binding, meaning that there's no
411
 *   definition of the method that matches the types of the arguments.  
412
 */
413
class UnboundMultiMethod: Exception
414
    construct(func, args)
415
    {
416
        /* note the function name and argument list */
417
        func_ = func;
418
        args_ = args;
419
420
        /* look up the function's name */
421
        name_ = _multiMethodRegistry.funcNameTab_[func];
422
    }
423
424
    /* display an error message describing the exception */
425
    displayException()
426
    {
427
        "Unbound multi-method \"<<name_>>\" (<<args_.length()>> argument(s))";
428
    }
429
430
    /* the base function pointer */
431
    func_ = nil
432
433
    /* the symbol name of the base function */
434
    name_ = ''
435
436
    /* the number of arguments */
437
    args_ = 0
438
;
439
440
class UnboundInheritedMultiMethod: UnboundMultiMethod
441
    displayException()
442
    {
443
        "No inherited multi-method for \"<<name_>>\" (<<args_.length()>>
444
        arguments(s))";
445
    }
446
;
447
448
449
/* ------------------------------------------------------------------------ */
450
/*
451
 *   Base class for our internal placeholder objects for argument list
452
 *   matching.
453
 */
454
class _MultiMethodPlaceholder: object
455
;
456
457
/*
458
 *   A placeholder object for bindings for non-object arguments.  Whenever
459
 *   we have an actual argument value that's not an object, we'll look here
460
 *   for bindings for that parameter.  When registering a function, we'll
461
 *   register a binding here for any parameter that doesn't have a type
462
 *   specification.  
463
 */
464
_multiMethodNonObjectBindings: _MultiMethodPlaceholder
465
;
466
467
/*
468
 *   A placeholder object for end-of-list bindings.  When we're matching an
469
 *   argument list, we'll use this to represent the end of the list so that
470
 *   we can match the "..." in any varargs functions in the multi-method
471
 *   set that we're matching against.  
472
 */
473
_multiMethodEndOfList: _MultiMethodPlaceholder
474
;
475
476
477
/* ------------------------------------------------------------------------ */
478
/*
479
 *   Register a multi-method.
480
 *   
481
 *   The compiler automatically generates a call to this function during
482
 *   pre-initialization for each defined multi-method.  'baseFunc' is a
483
 *   pointer to the "base" function - this is a stub function that the
484
 *   compiler generates to refer to the whole collection of multi-methods
485
 *   with a given name.  'func' is the pointer to the specific multi-method
486
 *   we're registering; this is the actual function defined in the code
487
 *   with a given set of parameter types.  'params' is a list of the
488
 *   parameter type values; each parameter type in the list is given as a
489
 *   class object (meaning that the parameter matches that class), nil
490
 *   (meaning that the parameter matches ANY type of value), or the string
491
 *   '...' (meaning that this is a "varargs" function, and any number of
492
 *   additional parameters can be supplied at this point in the parameters;
493
 *   this is always the last parameter in the list if it's present).  
494
 */
495
_multiMethodRegister(baseFunc, func, params)
496
{
497
    /* if there's no hash entry for the function yet, add one */
498
    local tab = _multiMethodRegistry.funcTab_;
499
    if (tab[baseFunc] == nil)
500
        tab[baseFunc] = new Vector(10);
501
502
    /* add the entry to the list of variants for this function */
503
    tab[baseFunc].append([func, params]);
504
505
    /* add the mapping from the function to the base function */
506
    _multiMethodRegistry.baseFuncTab_[func] = baseFunc;
507
508
    /* also add the function to the direct parameter table */
509
    _multiMethodRegistry.funcParamTab_[func] = params;
510
}
511
512
/*
513
 *   Build the method bindings.  The compiler generates a call to this
514
 *   after all methods have been registered; we run through the list of
515
 *   registered methods and generate the binding properties in the
516
 *   referenced objects.  
517
 */
518
_multiMethodBuildBindings()
519
{
520
    /* no errors yet */
521
    local errs = [];
522
    
523
    /* 
524
     *   build a lookup table that maps function pointers to symbol names,
525
     *   so we can look up our function names for diagnostic purposes 
526
     */
527
    local nameTab = new LookupTable(128, 256);
528
    t3GetGlobalSymbols().forEachAssoc(new function(key, val)
529
    {
530
        /* if it's a function, store a value-to-name association */
531
        if (dataType(val) == TypeFuncPtr)
532
            nameTab[val] = key;
533
    });
534
535
    /* run through each entry in the method table */
536
    _multiMethodRegistry.funcTab_.forEachAssoc(new function(baseFunc, val)
537
    {
538
        /* look up the base function's name */
539
        local name = nameTab[baseFunc];
540
541
        /* add this to the saved name table */
542
        _multiMethodRegistry.funcNameTab_[baseFunc] = name;
543
544
        /* note the number of registered instances of this function */
545
        local funcCnt = val.length();
546
547
        /* 
548
         *   Assign the initial binding property for this function.  This
549
         *   is the property that gives us the binding for the first
550
         *   variant argument.  Each unique multi-method (which is defined
551
         *   as a multi-method with a given name and a given number of
552
         *   parameters) has a single initial binding property.  
553
         */
554
        local initProp = t3AllocProp();
555
556
        /* 
557
         *   store the binding starter information for the function - to
558
         *   find the binding on invocation, we'll need the initial binding
559
         *   property so that we can trace the argument list 
560
         */
561
        _multiMethodRegistry.boundFuncTab_[baseFunc] = initProp;
562
563
        /* build the argument binding tables */
564
        for (local i = 1 ; i <= funcCnt ; i++)
565
        {
566
            /* get the function binding */
567
            local func = val[i][1];
568
569
            /* get the formal parameter type list for this function */
570
            local params = val[i][2];
571
            local paramCnt = params.length();
572
573
            /*
574
             *   If the last formal isn't a varargs placeholder, then we
575
             *   must explicitly find the end of the list in the actual
576
             *   parameters in order to match a call.  To match the end of
577
             *   the list, add the special End-Of-List placeholder to the
578
             *   formals list.
579
             *   
580
             *   This isn't necessary when there's a varargs placeholder
581
             *   because the placeholder can match zero or more - so it
582
             *   doesn't matter where the list ends as long as we get to
583
             *   the varargs slot.  
584
             */
585
            if (paramCnt == 0 || params[paramCnt] != '...')
586
            {
587
                params += _multiMethodEndOfList;
588
                ++paramCnt;
589
            }
590
591
            /* start at the initial binding property */
592
            local prop = initProp;
593
594
            /* run through the parameters and build the bindings */
595
            for (local j = 1 ; j <= paramCnt ; j++)
596
            {
597
                /* get this parameter type */
598
                local origTyp = params[j], typ = origTyp;
599
600
                /* 
601
                 *   If the type is nil, it means that this parameter slot
602
                 *   can accept any type.  So, map the slot to the generic
603
                 *   Object type - this will catch everything, since we
604
                 *   handle non-objects by mapping them to the
605
                 *   _multiMethodNonObjectBindings placeholder object,
606
                 *   which like all objects inherits from Object.  This
607
                 *   means we'll match argument values that are objects or
608
                 *   non-objects, thus fulfilling our requirement to match
609
                 *   all values.
610
                 *   
611
                 *   If the type is the string '...', it means that this is
612
                 *   a varargs placeholder argument.  In this case, we need
613
                 *   to set up a match for the generic Object, in case we
614
                 *   have one or more actual arguments for the varargs
615
                 *   portion.  This will also automatically match the case
616
                 *   where we have no extra arguments, because in this case
617
                 *   the matcher will try to match the End-Of-List
618
                 *   placeholder object _multiMethodEndOfList, which (as
619
                 *   above) inherits from Object and thus picks up the
620
                 *   any-type binding.
621
                 *   
622
                 *   The one tricky bit is that when we have a parameter
623
                 *   explicitly bound to Object, or an explicit End-Of-List
624
                 *   flag object, we'll get an undesired side effect of
625
                 *   this otherwise convenient arrangement: we'll
626
                 *   effectively bind non-object types to the Object by
627
                 *   virtue of the inheritance.  To deal with this, we'll
628
                 *   explicitly set the placeholders' binding to nil in
629
                 *   this situation - this makes non-object types
630
                 *   explicitly *not* bound to the function, overriding any
631
                 *   binding we'd otherwise inherit from Object.  
632
                 */
633
                if (typ == nil || typ == '...')
634
                    typ = Object;
635
636
                /* 
637
                 *   Figure the binding.
638
                 *   
639
                 *   - If this is the last parameter, it's the end of the
640
                 *   line, so bind directly to the function pointer.
641
                 *   
642
                 *   - If this isn't the variant parameter, the binding is
643
                 *   the next binding property.  At invocation, we'll
644
                 *   continue on to the next argument value, evaluating
645
                 *   this next property to get its binding in the context
646
                 *   established by the current argument and property.  The
647
                 *   next binding property is specific to the current class
648
                 *   in the current position, so we might already have
649
                 *   assigned a property for it from another version of
650
                 *   this function.  Look it up, or create a new one if we
651
                 *   haven't assigned it already.  
652
                 */
653
                local binding;
654
                if (j == paramCnt)
655
                {
656
                    /* end of the line - the binding is the actual function */
657
                    binding = func;
658
659
                    /* 
660
                     *   if this type is already bound to a different
661
                     *   definition for this function, we have a
662
                     *   conflicting definition 
663
                     */
664
                    if (typ.propDefined(prop, PropDefDirectly)
665
                        && typ.(prop) != binding)
666
                        errs += [baseFunc, func, params, name];
667
                }
668
                else
669
                {
670
                    /* check for an existing binding property here */
671
                    if (typ.propDefined(prop, PropDefDirectly))
672
                    {
673
                        /* we already have a forward binding here - use it */
674
                        binding = typ.(prop);
675
                    }
676
                    else
677
                    {
678
                        /* it's not defined here, so create a new property */
679
                        binding = t3AllocProp();
680
                    }
681
                }
682
683
                /* set the binding */
684
                typ.(prop) = binding;
685
                
686
                /*
687
                 *   As we mentioned above, if the original type is
688
                 *   explicitly Object, we *don't* want to allow non-object
689
                 *   types (int, true, nil, property pointers, etc) and
690
                 *   End-Of-List placeholders to match - without some kind
691
                 *   of explicit intervention here, the placeholders would
692
                 *   match by inheritance because the placeholders are just
693
                 *   objects themselves.  To handle this properly, set the
694
                 *   non-object placeholder bindings explicitly to nil.  
695
                 */
696
                if (origTyp == Object)
697
                    _MultiMethodPlaceholder.(prop) = nil;
698
699
                /* 
700
                 *   if there's another argument, this binding is the
701
                 *   binding property for the next argument 
702
                 */
703
                prop = binding;
704
            }
705
        }
706
    });
707
708
#ifdef MULTMETH_STATIC_INHERITED
709
    /*
710
     *   If we're operating in static inheritance mode, cache the
711
     *   next-override information for inherited() calls.  Since we're
712
     *   using static inheritance, we can figure this at startup and just
713
     *   look up the cached information whenever we need to perform an
714
     *   inherited() call.  
715
     */
716
    _multiMethodRegistry.funcTab_.forEachAssoc(new function(baseFunc, val)
717
    {
718
        /* get the binding property for the base function */
719
        local prop = _multiMethodRegistry.boundFuncTab_[baseFunc];
720
721
        /* run through the functions registered under this function name */
722
        for (local i = 1 ; i <= val.length() ; i++)
723
        {
724
            /* get this function binding and the type list */
725
            local func = val[i][1];
726
            local params = val[i][2];
727
            local paramCnt = params.length();
728
729
            /* 
730
             *   Add the end-of-list marker if applicable.  For a varargs
731
             *   function, add one generic Object parameter in place of the
732
             *   variable list - but we'll check later to make sure that
733
             *   any match we find is really varargs, since a varargs
734
             *   function can only inherit from another varargs function.
735
             *   (This is because, in order to actually invoke the
736
             *   inherited function from an overrider, the callee must be
737
             *   varargs to be able to handle varargs from the caller.)  
738
             */
739
            local varargs = (paramCnt != 0 && params[paramCnt] == '...');
740
            if (varargs)
741
                params[paramCnt] = Object;
742
            else
743
                params += _multiMethodEndOfList;
744
745
            /* look up the inherited version of the method */
746
            local inh = _multiMethodInherit(func, prop, params);
747
748
            /* varargs can only inherit from varargs */
749
            if (inh != nil && varargs)
750
            {
751
                /* look up the inherited parameters */
752
                local inhParams = _multiMethodRegistry.funcParamTab_[inh];
753
                local inhParamCnt = inhParams.length();
754
755
                /* make sure it's varargs, too */
756
                if (inhParamCnt == 0 || inhParams[inhParamCnt] != '...')
757
                    inh = nil;
758
            }
759
760
            /* remember the inherited function */
761
            _multiMethodRegistry.inhTab_[func] = inh;
762
        }
763
    });
764
#endif /* MULTMETH_STATIC_INHERITED */
765
    
766
    /* we're done with the source bindings - discard them to save memory */
767
    _multiMethodRegistry.funcTab_ = nil;
768
    _multiMethodRegistry.funcParamTab_ = nil;
769
}
770
771
/*
772
 *   Multi-method registry.  This is where we keep the registry information
773
 *   that we build during initialization.  
774
 */
775
_multiMethodRegistry: object
776
    /* table of registered functions, indexed by base function */
777
    funcTab_ = static new LookupTable(128, 256)
778
779
    /* table of function parameter lists, indexed by function */
780
    funcParamTab_ = static new LookupTable(128, 256)
781
782
    /* function name table */
783
    funcNameTab_ = static new LookupTable(64, 128)
784
785
    /* base function -> initial binding property */
786
    boundFuncTab_ = static new LookupTable(64, 128)
787
788
    /* function -> base function */
789
    baseFuncTab_ = static new LookupTable(64, 128)
790
791
#ifdef MULTMETH_STATIC_INHERITED
792
    /* table of cached inherited() information, indexed by function */
793
    inhTab_ = static new LookupTable(64, 128)
794
#endif
795
;