Ticket #32: 4.scm

File 4.scm, 61.7 KB (added by Tony Sidaway, 14 years ago)

Trimmed version of the version 4 egg, for comparison using diff

Line 
1; From '@' by Dan Muresan
2(define-syntax slot@
3  (syntax-rules (=)
4    ((_ o) o)
5    ((_ o slot = v) (slot-set! o 'slot v))
6    ((_ o slot . slots) (slot@ (slot-ref o 'slot) . slots))))
7
8(define-syntax define-class
9  (syntax-rules ()
10    [(_ name () slots)
11     (define-class name (<object>) slots) ]
12    [(_ name supers slots)
13     (define-class name supers slots <class>) ]
14    [(_ name () slots meta)
15     (define-class name (<object>) slots meta) ]
16    [(_ cname (supers ...) (slots ...) meta)
17     (define cname (make meta 'name 'cname 'direct-supers (list supers ...) 'direct-slots (list 'slots ...))) ] ) )
18
19(define-syntax define-generic
20  (syntax-rules ()
21    [(_ n class) (define n (make class 'name 'n))]
22    [(_ n) (define n (make-generic 'n))] ) )
23
24(define-syntax (define-method x r c)
25  (let ((head (cadr x))
26        (body (cddr x))
27        (%add-global-method (r 'add-global-method))
28        (%make-method (r 'make-method))
29        (%lambda (r 'lambda))
30        (%list (r 'list))
31        (%<top> (r '<top>)))
32    (##sys#check-syntax 'define-method head '(symbol . _))
33    (##sys#check-syntax 'define-method body '#(_ 1))
34    (let gather ([args (##sys#slot head 1)]
35                 [specs '()]
36                 [vars '()] )
37      (if (or (not (pair? args))
38              (memq (car args) '(#!optional #!key #!rest)) )
39          (let ([name (##sys#slot head 0)])
40            `(set! ,name
41               (,%add-global-method
42                (##core#global-ref ,name)
43                ',name
44                (,%list ,@(reverse specs))
45                ;; `call-next-method' not renamed:
46                (,%lambda (call-next-method ,@(reverse vars) ,@args) ,@body) ) ) )
47          (let ([arg (##sys#slot args 0)])
48            (gather (##sys#slot args 1)
49                    (cons (if (pair? arg) (cadr arg) %<top>) specs)
50                    (cons (if (pair? arg) (car arg) arg) vars) ) ) ) ) ) )
51
52;; For system use in extending the set of "builtin" classes.
53
54(define-for-syntax (##tinyclos#make-classname-symbol str)
55  (string->symbol (string-append "<" (##sys#strip-syntax str) ">")) )
56
57(define-syntax (define-primitive-class x r c)
58  (let ((name (cadr x))
59        (pred (caddr x))
60        (sclasses (cdddr x)))
61    `(,(r 'define) ,(##tinyclos#make-classname-symbol name)
62      (,(r 'new-primitive-class) ,name ,pred ,@sclasses)) ))
63
64(define-syntax (define-structure-class x r c)
65  (let ((name (cadr x))
66        (tag (caddr x)))
67    `(,(r 'define) ,(##tinyclos#make-classname-symbol name)
68      (,(r 'new-structure-class) name (,(r 'quote) ,tag)) )))
69
70(define-syntax (define-tagged-pointer-class x r c)
71  (let ((name (cadr x))
72        (pred (caddr x)))
73    `(,(r 'define) ,(##tinyclos#make-classname-symbol name)
74      (,(r 'new-tagged-pointer-class) name (,(r 'quote) ,pred)) )))
75
76(define-syntax (define-extended-procedure-class x r c)
77  (let ((name (cadr x))
78        (pred (caddr x)))
79    `(,(r 'define) ,(##tinyclos#make-classname-symbol name)
80      (,(r 'new-extended-procedure-class) name (,(r 'quote) ,pred)) )))
81
82;
83; A very simple CLOS-like language, embedded in Scheme, with a simple
84; MOP.  The features of the default base language are:
85;
86;   * Classes, with instance slots, but no slot options.
87;   * Multiple-inheritance.
88;   * Generic functions with multi-methods and class specializers only.
89;   * Primary methods and call-next-method; no other method combination.
90;   * Uses Scheme's lexical scoping facilities as the class and generic
91;     function naming mechanism.  Another way of saying this is that
92;     class, generic function and methods are first-class (meta)objects.
93;
94; While the MOP is simple, it is essentially equal in power to both MOPs
95; in AMOP.  This implementation is not at all optimized, but the MOP is
96; designed so that it can be optimized.  In fact, this MOP allows better
97; optimization of slot access extenstions than those in AMOP.
98;
99;
100;
101; In addition to calling a generic, the entry points to the default base
102; language are:
103;
104;   (MAKE-CLASS list-of-superclasses list-of-slot-names)
105;   (MAKE-GENERIC)
106;   (MAKE-METHOD list-of-specializers procedure)
107;   (ADD-METHOD generic method)
108;
109;   (MAKE class . initargs)
110;   (INITIALIZE instance initargs)            ;Add methods to this,
111;                                             ;don't call it directly.
112;
113;   (SLOT-REF  object slot-name)
114;   (SLOT-SET! object slot-name new-value)
115;
116;
117; So, for example, one might do (but only within this file! 'getl' is not exported):
118;
119;   (define <position> (make-class (list <object>) (list 'x 'y)))
120;   (add-method initialize
121;       (make-method (list <position>)
122;         (lambda (call-next-method pos initargs)
123;           (for-each (lambda (initarg-name slot-name)
124;                       (slot-set! pos slot-name (getl initargs initarg-name 0)))
125;                     '(x y)
126;                     '(x y)))))
127;
128;   (set! p1 (make <position> 'x 1 'y 3))
129;
130;
131;
132; NOTE!  Do not use EQUAL? to compare objects!  Use EQ? or some hand
133;        written procedure.  Objects have a pointer to their class,
134;        and classes are circular structures, and ...
135;
136;
137;
138; The introspective part of the MOP looks like the following.  Note that
139; these are ordinary procedures, not generics.
140;
141;   CLASS-OF
142;
143;   CLASS-DIRECT-SUPERS
144;   CLASS-DIRECT-SLOTS
145;   CLASS-CPL
146;   CLASS-SLOTS
147;
148;   GENERIC-METHODS
149;
150;   METHOD-SPECIALIZERS
151;   METHOD-PROCEDURE
152;
153;
154; The intercessory protocol looks like (generics in uppercase):
155;
156;   make
157;     ALLOCATE-INSTANCE
158;     INITIALIZE                   (really a base-level generic)
159;
160;   class initialization
161;     COMPUTE-CPL
162;     COMPUTE-SLOTS
163;     COMPUTE-GETTER-AND-SETTER
164;
165;   add-method                     (Notice this is not a generic!)
166;     COMPUTE-APPLY-GENERIC
167;       COMPUTE-METHODS
168;         COMPUTE-METHOD-MORE-SPECIFIC?
169;       COMPUTE-APPLY-METHODS
170;
171
172;;; Aliases for sys stuff
173
174(define-inline (%car p) (##sys#slot p 0))
175(define-inline (%cdr p) (##sys#slot p 1))
176(define-inline (%cadr p) (%car (%cdr p)))
177(define-inline (%cddr p) (%cdr (%cdr p)))
178(define-inline (%set-car! p y) (##sys#setslot p 0 y))
179(define-inline (%set-cdr! p y) (##sys#setslot p 1 y))
180
181(define-inline (%string-length s) (##sys#size s))
182
183(define-inline (%vector-ref v i) (##sys#slot v i))
184(define-inline (%vector-set! v i x) (##sys#setslot v i x))
185(define-inline (%vector-length v) (##sys#size v))
186
187(define-syntax (%structure? x r c)
188  (let ((?x (cadr x))
189        (?t (cddr x)))
190    (if (null? ?t)
191        `(##sys#generic-structure? ,?x)
192        `(##sys#structure? ,?x ,(car ?t)))))
193
194(define-inline (%structure-ref r i) (##sys#slot r i))
195(define-inline (%structure-set! r i x) (##sys#setslot r i x))
196(define-inline (%structure-length r) (##sys#size r))
197(define-inline (%structure-tag r) (##sys#slot r 0))
198
199(define-inline (%closure-ref c i) (##sys#slot c i))
200(define-inline (%closure-length c) (##sys#size c))
201
202(define-inline (%tagged-pointer-data p) (##sys#slot p 1))
203
204(define-inline (%null-pointer? p) (##sys#null-pointer? p))
205
206(define-inline (%blob? x) (##sys#bytevector? x))
207
208(define-inline (%immediate-value? x) (##sys#immediate? x))
209
210(define-inline (%undefined? x) (##core#inline "C_undefinedp" x))
211
212(define-inline (%unbound? x) (eq? x (##sys#slot '##sys#arbitrary-unbound-symbol 0)))
213
214(define-inline (%pointer? x) (##core#inline "C_pointerp" x))
215
216(define-inline (%tagged-pointer? x) (##core#inline "C_taggedpointerp" x))
217
218(define-inline (%swig-pointer? x) (##core#inline "C_swigpointerp" x))
219
220(define-inline (%locative? x) (##core#inline "C_locativep" x))
221
222(define-inline (%random-fixnum x) (##core#inline "C_random_fixnum" x))
223
224;;; Support code
225
226(define-syntax (define-unique-object x r c)
227  `(,(r 'define) ,(cadr x) (,(r 'gensym)) ))
228
229(define (filter-in f l)
230  (let loop ([l l])
231    (cond [(null? l) '()]
232          [else
233            (let ([h (%car l )]
234                  [r (%cdr l)] )
235              (if (f h)
236                  ; Unlike SRFI-1 filter this doesn't share the undeleted longest tail
237                  #;(let ([t (loop r)]) (if (eq? r t) l (cons h t)))
238                  (cons h (loop r))
239                  (loop r) ) ) ] ) ) )
240
241(define fast-getl
242  (foreign-lambda* scheme-object ((scheme-object initargs) (scheme-object name) (scheme-object def)) "
243    while(initargs != C_SCHEME_END_OF_LIST) {
244      if(name == C_block_item(initargs, 0)) {
245        if((initargs = C_block_item(initargs, 1)) == C_SCHEME_END_OF_LIST) return(def);
246        else return(C_block_item(initargs, 0));
247      }
248      initargs = C_block_item(initargs, 1);
249    }
250    return(def);") )
251
252(define-unique-object not-found-object)
253
254(define (getl initargs name . def)
255  (let ([value (fast-getl initargs name not-found-object)])
256    (if (eq? value not-found-object)
257        (optional def (##sys#error 'getl "cannot find item" name initargs))
258        value ) ) )
259
260;
261; A simple topological sort.
262;
263; This is a fairly modified version of code I originally got from Anurag
264; Mendhekar <anurag@moose.cs.indiana.edu>.
265;
266
267(define (compute-std-cpl c get-direct-supers)
268
269  (define-inline (every1 test lst)
270    (let loop ([lst lst])
271      (or (null? lst)
272          (and (test (%car lst))
273               (loop (%cdr lst)) ) ) ) )
274
275  (define (top-sort elements constraints tie-breaker)
276    (let loop ((elements    elements)
277               (constraints constraints)
278               (result      '()))
279        (if (null? elements)
280            result
281            (let ((can-go-in-now
282                    (filter-in
283                      (lambda (x)
284                        (every1 (lambda (constraint)
285                                 (or (not (eq? (%cadr constraint) x))
286                                     (memq (%car constraint) result)))
287                               constraints))
288                      elements)))
289              (if (null? can-go-in-now)
290                  (##sys#error 'top-sort "invalid constraints")
291                  (let ((choice (if (null? (%cdr can-go-in-now))
292                                    (%car can-go-in-now)
293                                    (tie-breaker result can-go-in-now))))
294                    (loop (filter-in (lambda (x) (not (eq? x choice))) elements)
295                          ; Include all constraints
296                          #;(filter-in (lambda (x) (not (eq? (%cadr x) choice))) constraints)
297                          constraints
298                          (append result (list choice))))))) ) )
299
300  (define (std-tie-breaker get-supers)
301    (lambda (partial-cpl min-elts)
302      (let loop ((pcpl (reverse partial-cpl)))
303        (let* ((current-elt (%car pcpl))
304              (ds-of-ce (get-supers current-elt))
305              (common (filter-in (cut memq <> ds-of-ce) min-elts)))
306          (if (null? common)
307              (let ([r (%cdr pcpl)])
308                (if (null? r)
309                    (##sys#error 'std-tie-breaker "nothing valid")
310                    (loop r)) )
311              (%car common)) ) ) ) )
312
313  (define (build-transitive-closure get-follow-ons)
314    (lambda (x)
315      (let track ((result '())
316                  (pending (list x)))
317           (if (null? pending)
318               result
319               (let ((next (%car pending)))
320                 (if (memq next result)
321                     (track result (%cdr pending))
322                     (track (cons next result)
323                            (append (get-follow-ons next)
324                                    (%cdr pending)))))) ) ) )
325
326  (define (build-constraints get-follow-ons)
327    (lambda (x)
328      (let loop ((elements ((build-transitive-closure get-follow-ons) x))
329                 (this-one '())
330                 (result '()))
331           (if (or (null? this-one) (null? (%cdr this-one)))
332               (if (null? elements)
333                   result
334                   (loop (%cdr elements)
335                         (cons (%car elements)
336                               (get-follow-ons (%car elements)))
337                         result))
338               (loop elements
339                     (%cdr this-one)
340                     (cons (list (%car this-one) (%cadr this-one))
341                           result))) ) ) )
342
343  (top-sort ((build-transitive-closure get-direct-supers) c)
344            ((build-constraints get-direct-supers) c)
345            (std-tie-breaker get-direct-supers) ) )
346
347;;; Method cache support code:
348
349#>
350#define C_METHOD_CACHE_SIZE 8
351<#
352
353(define-foreign-variable method-cache-size int "C_METHOD_CACHE_SIZE")
354
355(define method-caching-enabled #f)
356(define method-cache-tag #f)
357
358(define (make-method-cache)
359  (cons method-cache-tag (make-vector (arithmetic-shift method-cache-size 1) #f)) )
360
361(define method-cache-lookup
362  (foreign-lambda* scheme-object ((scheme-object mcache) (scheme-object hash) (scheme-object classes)) "
363    C_word v = C_block_item(mcache, 1);
364    C_word clist, x, y;
365    int free_index = -1;
366    int i = ((C_unfix(hash) & (C_METHOD_CACHE_SIZE - 1)) << 1) & 0xffff,
367        i0, i2;
368    for(i0 = i;; i = i2) {
369      clist = C_block_item(v, i);
370      if(clist != C_SCHEME_FALSE) {
371        x = classes;
372        y = clist;
373        while(x != C_SCHEME_END_OF_LIST && y != C_SCHEME_END_OF_LIST) {
374          if(C_block_item(x, 0) != C_block_item(y, 0)) goto mismatch;
375          else {
376            x = C_block_item(x, 1);
377            y = C_block_item(y, 1);
378          }
379        }
380        if (x == C_SCHEME_END_OF_LIST && y == C_SCHEME_END_OF_LIST)
381          return(C_block_item(v, i + 1));
382        else
383          goto mismatch;
384      }
385      else if(free_index == -1) free_index = i;
386    mismatch:
387      i2 = (i + 2) & ((C_METHOD_CACHE_SIZE << 1) - 1);
388      if(i2 == i0) return(free_index == -1 ? C_SCHEME_FALSE : C_fix(free_index));
389    }") )
390
391;
392; Then, we need to build what, in a more real implementation, would be
393; the interface to the memory subsystem: instances and entities.  The
394; former are used for instances of instances of <class>; the latter
395; are used for instances of instances of <entity-class>.  In this MOP,
396; none of this is visible to base- or MOP-level programmers.
397;
398; (One might consider rewriting the definition of instances using
399; define-record or something.  It doesn't turn out to make it much
400; simpler, at least not to me.  It also breaks the nice parallelism
401; with entities.)
402;
403; %allocate-instance, %allocate-entity, get-field, set-field!, and class-of are
404; the normal interface, from the rest of the code, to the low-level memory
405; system.
406;
407; One thing to take note of is that the protocol does not allow the user
408; to add low-level instance representations.  I have never seen a way to make
409; that work.
410;
411
412; Instance
413
414(define the-slots-of-a-class            ;
415  '(direct-supers                       ;(class ...)
416    direct-slots                        ;((name . options) ...)
417    cpl                                 ;(class ...)
418    slots                               ;((name . options) ...)
419    nfields                             ;an integer
420    field-initializers                  ;(proc ...)
421    getters-n-setters                   ;((slot-name getter . setter) ...)
422    name) )                             ;name
423
424; structure: tag class cache direct-supers ... name
425(define-constant basic-class-instance-size 11)
426
427(define (%allocate-instance class nfields)
428  (let ((instance (make-vector (+ nfields 3) #f)))
429    (##core#inline "C_vector_to_structure" instance)
430    (%structure-set! instance 0 'instance)
431    (%structure-set! instance 1 class)
432    instance))
433
434(define-inline (%instance? x)
435  (%structure? x 'instance))
436
437(define-inline (%instance-class instance)
438  (%structure-ref instance 1))
439
440(define-inline (%set-instance-class! instance new-value)
441  (%structure-set! instance 1 new-value))
442
443(define-inline (%instance-cache-ref instance)
444  (%structure-ref instance 2))
445
446(define-inline (%instance-cache-set! instance x)
447  (%structure-set! instance 2 x))
448
449(define-inline (%instance-ref instance index)
450  (%structure-ref instance (+ index 3)))
451
452(define-inline (%instance-set! instance index new-value)
453  (%structure-set! instance (+ index 3) new-value))
454
455(define-record-printer (instance x out)
456  (print-object x out) )
457
458; Entity
459
460(define-unique-object entity-tag)
461
462(define %allocate-entity
463  (let ([default-proc
464          (lambda args
465            (##sys#error '%allocate-entity "called entity without first setting proc"))] )
466    (lambda (class nfields name)
467      (letrec ((entity-def (make-vector (+ nfields 5) #f))
468               (closure (lambda args (apply (%structure-ref entity-def 1) args))))
469        (##core#inline "C_vector_to_structure" entity-def)
470        (%structure-set! entity-def 0 'entity)
471        (%structure-set! entity-def 1 default-proc)
472        (%structure-set! entity-def 2 class)
473        (%structure-set! entity-def 3 name)
474        ;; slot #4 is cache (defaults to #f)
475        (let ([len (%closure-length closure)])
476          (let ([entity (make-vector (+ len 2))] )
477            (do ([i 1 (add1 i)])
478                ((>= i len)
479                 (%vector-set! entity i entity-def)
480                 (%vector-set! entity (add1 i) entity-tag)
481                 (##core#inline "C_vector_to_closure" entity)
482                 (##core#inline "C_copy_pointer" closure entity)
483                 entity)
484              (%vector-set! entity i (%closure-ref closure i)) ) ) ) ) ) ) )
485
486(define-inline (%entity? x)
487  (and (procedure? x)
488       (let ([len (%closure-length x)])
489         (and (> len 3)
490              (eq? entity-tag (%closure-ref x (sub1 len))) ) ) ) )
491
492(define-inline (%entity-def entity)
493  (%closure-ref entity (- (%closure-length entity) 2)) )
494
495(define-inline (%set-entity-proc! entity proc)
496  (%structure-set! (%entity-def entity) 1 proc) )
497
498(define-inline (%entity-class entity)
499  (%structure-ref (%entity-def entity) 2) )
500
501(define-inline (%entity-name entity)
502  (%structure-ref (%entity-def entity) 3) )
503
504(define-inline (%set-entity-name! entity x)
505  (%structure-set! (%entity-def entity) 3 x) )
506
507(define-inline (%entity-cache-ref entity)
508  (%structure-ref (%entity-def entity) 4) )
509
510(define-inline (%entity-cache-set! entity x)
511  (%structure-set! (%entity-def entity) 4 x) )
512
513(define-inline (%entity-ref entity index)
514  (%structure-ref (%entity-def entity) (+ index 5)) )
515
516(define-inline (%entity-set! entity index new-value)
517  (%structure-set! (%entity-def entity) (+ index 5) new-value) )
518
519(define-record-printer (entity x out)
520  (print-object x out) )
521
522; Instance/Entity field accessors
523
524(define get-field
525  (lambda (object field)
526    (cond ((%instance? object) (%instance-ref object field))
527          ((%entity?   object) (%entity-ref   object field))
528          (else
529           (##sys#signal-hook #:type-error 'get-field "can only get-field of instances and entities" object)))))
530
531(define set-field!
532  (lambda (object field new-value)
533    (cond ((%instance? object) (%instance-set! object field new-value))
534          ((%entity?   object) (%entity-set!   object field new-value))
535          (else
536           (##sys#signal-hook #:type-error 'set-field! "can only set-field! of instances and entities" object)))))
537
538;
539; Note that this implementation of class-of assumes the name of the
540; primitive classes that are set up later.
541;
542
543; Local namespace - all "exported" procedures are inline so
544; defined in compilation unit top-level scope.
545
546(define (delete1! test lst)
547    (let loop ((cpair lst) (ppair #f))
548      (cond ((null? cpair) lst )
549            ((test (%car cpair))
550              (if ppair
551                  (begin (%set-cdr! ppair (%cdr cpair)) lst)
552                  (%cdr cpair)) )
553            (else (loop (%cdr cpair) cpair) ) ) ) )
554
555(define (any1 test lst)
556    (let loop ([lst lst])
557      (and (not (null? lst))
558           (or (test (%car lst))
559               (loop (%cdr lst)) ) ) ) )
560
561  ; Class-of extension helpers
562  ;
563  ; Implemented as a map: "test" <-> "class"
564  ;
565  ; tst - test value; symbol or procedure
566  ; cls - class
567  ; prd - predicate? obj
568  ; eql - symbol eq? obj
569
570  (define-inline (clsmapelm-tst cme)
571    (%car cme) )
572
573  (define-inline (clsmapelm-cls cme)
574    (%cdr cme) )
575
576  (define-inline (clsmapelm-tst-set! cme tst)
577    (%set-car! cme tst) )
578
579  (define-inline (clsmapelm-cls-set! cme cls)
580    (%set-cdr! cme cls) )
581
582  (define-inline (clsmap-tst? tst)
583    (or (procedure? tst) (symbol? tst)) )
584
585  (define-inline (clsmapelm-cls-of/prd? cme tst)
586    ((clsmapelm-tst cme) tst) )
587
588  (define-inline (clsmapelm-cls-of/eql? cme tst)
589    (eq? tst (clsmapelm-tst cme)) )
590
591  (define-inline (clsmapelm-cls-of/prd cme tst)
592    (and (clsmapelm-cls-of/prd? cme tst)
593         (clsmapelm-cls cme)) )
594
595  (define-inline (clsmapelm-cls-of/eql cme tst)
596    (and (clsmapelm-cls-of/eql? cme tst)
597         (clsmapelm-cls cme)) )
598
599  (define-inline (clsmap-add cm tst cls)
600    (cons (cons tst cls) cm) )
601
602   (define clsmap-update
603        (lambda (cm tst cls)
604          (let ((cme (any1 (cut clsmapelm-cls-of/eql? <> tst) cm)))
605            (if cme
606                (begin (clsmapelm-tst-set! cme tst) (clsmapelm-cls-set! cme cls) cm)
607                (clsmap-add cm tst cls) ) ) ))
608
609      (define clsmap-cls-of/prd
610        (lambda (cm tst)
611          (any1 (cut clsmapelm-cls-of/prd <> tst) cm) ))
612
613      (define clsmap-cls-of/eql
614        (lambda (cm tst)
615          (any1 (cut clsmapelm-cls-of/eql <> tst) cm) ))
616
617      (define clsmap-del/tst
618        (lambda (cm tst)
619          (delete1! (cut clsmapelm-cls-of/eql? <> tst) cm) ))
620
621      (define clsmap-del/cls
622        (lambda (cm cls)
623          (delete1! (lambda (cme) (eq? cls (clsmapelm-cls cme))) cm) ))
624
625    ; Primitive class-of extensions
626
627    (define *primitive-class-map* '())
628
629      (define-inline (primitive-class-of x)
630        (or (clsmap-cls-of/prd *primitive-class-map* x)
631            #;<object>) )
632
633      (define-inline (delete-primitive-class-of cls-or-tst)
634        (set! *primitive-class-map*
635              (if (clsmap-tst? cls-or-tst)
636                  (clsmap-del/tst *primitive-class-map* cls-or-tst)
637                  (clsmap-del/cls *primitive-class-map* cls-or-tst))) )
638
639      (define-inline (update-primitive-class-of prd cls)
640        (set! *primitive-class-map* (clsmap-update *primitive-class-map* prd cls)) )
641
642    ; Structure class-of extensions
643
644    (define *structure-class-map* '())
645
646      (define-inline (structure-class-of x)
647        (or (clsmap-cls-of/eql *structure-class-map* x)
648            <structure>) )
649
650      (define-inline (delete-structure-class-of cls-or-tst)
651        (set! *structure-class-map*
652              (if (clsmap-tst? cls-or-tst)
653                  (clsmap-del/tst *structure-class-map* cls-or-tst)
654                  (clsmap-del/cls *structure-class-map* cls-or-tst))) )
655
656      (define-inline (update-structure-class-of tag cls)
657        (set! *structure-class-map* (clsmap-update *structure-class-map* tag cls)) )
658
659    ; Tagged-pointer class-of extensions
660
661    (define *tagged-pointer-class-map* '())
662
663      (define-inline (tagged-pointer-class-of x)
664        (or (clsmap-cls-of/eql *tagged-pointer-class-map* (%tagged-pointer-data x))
665            <tagged-pointer>) )
666
667      (define-inline (delete-tagged-pointer-class-of cls-or-tst)
668        (set! *tagged-pointer-class-map*
669              (if (clsmap-tst? cls-or-tst)
670                  (clsmap-del/tst *tagged-pointer-class-map* cls-or-tst)
671                  (clsmap-del/cls *tagged-pointer-class-map* cls-or-tst))) )
672
673      (define-inline (update-tagged-pointer-class-of tag cls)
674        (set! *tagged-pointer-class-map* (clsmap-update *tagged-pointer-class-map* tag cls)) )
675
676    ; Extended-procedure class-of extensions
677
678    (define *extended-procedure-class-map* '())
679    (define xproc-tag (vector 'extended))
680
681      (define-inline (extended-procedure-lambda-decoration x)
682        (##sys#lambda-decoration x (lambda (x) (and (pair? x) (equal? xproc-tag (%car x))))) )
683
684      (define-inline (procedure-class-of x)
685        (or (and-let* ([d (extended-procedure-lambda-decoration x)])
686              (clsmap-cls-of/prd *extended-procedure-class-map* (%cdr d)) )
687            <procedure>) )
688
689      (define-inline (delete-extended-procedure-class-of cls-or-tst)
690        (set! *extended-procedure-class-map*
691              (if (clsmap-tst? cls-or-tst)
692                  (clsmap-del/tst *extended-procedure-class-map* cls-or-tst)
693                  (clsmap-del/cls *extended-procedure-class-map* cls-or-tst))) )
694
695      (define-inline (update-extended-procedure-class-of prd cls)
696        (set! *extended-procedure-class-map* (clsmap-update *extended-procedure-class-map* prd cls)) )
697
698;
699
700(define (class-of x)
701  (cond [(%unbound? x)                  (##sys#error 'class-of "unbound object")]
702        [(null? x)                      <null>]
703        [(fixnum? x)                    <exact>]
704        [(boolean? x)                   <boolean>]
705        [(char? x)                      <char>]
706        [(eof-object? x)                <end-of-file>]
707        [(%undefined? x)                <void>]
708        [(%immediate-value? x)          (##sys#error 'class-of "unidentified immediate object - cannot infer class" x)]
709        [(flonum? x)                    <inexact>]
710        [(integer? x)                   <integer>]
711        [(symbol? x)                    <symbol>]
712        [(%instance? x)                 (%instance-class x)]
713        [(%entity? x)                   (%entity-class x)]
714        [(vector? x)                    <vector>]
715        [(pair? x)                      <pair>]
716        [(string? x)                    <string>]
717        [(procedure? x)                 (procedure-class-of x)]
718        [(port? x)                      (if (input-port? x) <input-port> <output-port>)]
719        [(%blob? x)                     <blob>]
720        [(%pointer? x)                  <pointer>]
721        [(%tagged-pointer? x)           (tagged-pointer-class-of x)]
722        [(%swig-pointer? x)             <swig-pointer>]
723        [(%locative? x)                 <locative>]
724        [(%structure? x)
725         (case (%structure-tag x)
726           [(environment)               <environment>]
727           [(array)                     <array>]
728           [(hash-table)                <hash-table>]
729           [(queue)                     <queue>]
730           [(condition)                 <condition>]
731           [(condition-variable)        <condition-variable>]
732           [(char-set)                  <char-set>]
733           [(time)                      <time>]
734           [(lock)                      <lock>]
735           [(mmap)                      <mmap>]
736           [(promise)                   <promise>]
737           [(u8vector)                  <u8vector>]
738           [(s8vector)                  <s8vector>]
739           [(u16vector)                 <u16vector>]
740           [(s16vector)                 <s16vector>]
741           [(u32vector)                 <u32vector>]
742           [(s32vector)                 <s32vector>]
743           [(f32vector)                 <f32vector>]
744           [(f64vector)                 <f64vector>]
745           [(tcp-listener)              <tcp-listener>]
746           [(thread)                    <thread>]
747           [(mutex)                     <mutex>]
748           [(continuation)              <continuation>]
749           [(read-table)                <read-table>]
750           [(regexp)                    <regexp>]
751           [else                        (structure-class-of x)] ) ]
752        [(primitive-class-of x)]
753        [else
754          (##sys#error 'class-of "unidentified primitive object - cannot infer class" x) ] ) )
755
756;
757; Now we can get down to business.  First, we initialize the braid.
758;
759; For Bootstrapping, we define an early version of MAKE.  It will be
760; changed to the real version later on.  String search for ``set! make''.
761;
762
763(randomize)
764
765(define (make class . initargs)
766  (cond ((or (eq? class <class>)
767             (eq? class <entity-class>))
768         (let* ((new (%allocate-instance class (length the-slots-of-a-class)))
769                (dsupers (fast-getl initargs 'direct-supers '()))
770                (name (fast-getl initargs 'name "(anonymous)"))
771                (dslots  (map list (fast-getl initargs 'direct-slots  '())))
772                (cpl     (let loop ((sups dsupers) (so-far (list new)))
773                           (if (null? sups)
774                               (reverse so-far)
775                               (let ([cls (%car sups)])
776                                 (loop (class-direct-supers cls) (cons cls so-far))))))
777                (slots (apply append dslots (map class-direct-slots (%cdr cpl))))
778                (nfields 0)
779                (field-initializers '())
780                (allocator
781                 (lambda (init)
782                   (let ((f nfields))
783                     (set! nfields (add1 nfields))
784                     (set! field-initializers
785                       (cons init field-initializers))
786                     (values (lambda (o)   (get-field  o f))
787                             (lambda (o n) (set-field! o f n))))))
788                (getters-n-setters
789                 (map (lambda (s)
790                        (cons (%car s)
791                              (call-with-values (lambda () (allocator (lambda () (void)))) cons) ) )
792                      slots)))
793           (##tinyclos#slot-set! new 'direct-supers      dsupers)
794           (##tinyclos#slot-set! new 'direct-slots       dslots)
795           (##tinyclos#slot-set! new 'cpl                cpl)
796           (##tinyclos#slot-set! new 'slots              slots)
797           (##tinyclos#slot-set! new 'nfields            nfields)
798           (##tinyclos#slot-set! new 'field-initializers (reverse field-initializers))
799           (##tinyclos#slot-set! new 'getters-n-setters  getters-n-setters)
800           (##tinyclos#slot-set! new 'name               name)
801           (%instance-cache-set! new (%random-fixnum #x10000))
802           new))
803        ((eq? class <generic>)
804         (let ([new (%allocate-entity class (length (class-slots class))
805                                      (fast-getl initargs 'name "(unnamed)") ) ] )
806           (##tinyclos#slot-set! new 'methods '())
807           new))
808        ((eq? class <method>)
809         (let ((new (%allocate-instance class (length (class-slots class)))))
810           (##tinyclos#slot-set! new 'specializers (getl initargs 'specializers))
811           (##tinyclos#slot-set! new 'procedure    (getl initargs 'procedure))
812           new))
813        (else
814          (##sys#error "bootstrap make: unknown class" class)) ) )
815
816;
817; These are the real versions of slot-ref and slot-set!.  Because of the
818; way the new slot access protocol works, with no generic call inline,
819; they can be defined up front like this.  Cool eh?
820;
821
822(define (##tinyclos#slot-ref object slot-name)
823  ; if true, then this is an instance of <class>, with no additional slots
824  (if (and (%instance? object) (%instance-cache-ref object))
825      (%instance-ref
826       object
827       (case slot-name
828         [(direct-supers)       0]
829         [(direct-slots)        1]
830         [(cpl)                 2]
831         [(slots)               3]
832         [(nfields)             4]
833         [(field-initializers)  5]
834         [(getters-n-setters)   6]
835         [(name)                7]
836         [else (##sys#error "unknown basic slot-name" slot-name)] ) )
837      (let* ((info   (lookup-slot-info (class-of object) slot-name))
838             (getter (%car info)))
839        (getter object))) )
840
841(define (##tinyclos#slot-set! object slot-name new-value)
842  (let* ((info   (lookup-slot-info (class-of object) slot-name))
843         (setter (%cdr info)) )
844    (setter object new-value)) )
845
846(define slot-ref (getter-with-setter ##tinyclos#slot-ref ##tinyclos#slot-set!))
847(define slot-set! ##tinyclos#slot-set!)
848
849(define (lookup-slot-info class slot-name)
850  (let* ((getters-n-setters
851          (if (eq? class <class>)         ;* This grounds out
852              getters-n-setters-for-class ;* the slot-ref tower.
853              (##tinyclos#slot-ref class 'getters-n-setters)))
854         (entry (assq slot-name getters-n-setters)))
855    (if entry
856        (%cdr entry)
857        (##sys#error "no slot in instances of class" slot-name class) ) ) )
858
859;
860; Given that the early version of MAKE is allowed to call accessors on
861; class metaobjects, the definitions for them come here, before the
862; actual class definitions, which are coming up right afterwards.
863;
864
865(define (class-direct-slots class)
866  (##tinyclos#slot-ref class 'direct-slots) )
867(define (class-direct-supers class)
868  (##tinyclos#slot-ref class 'direct-supers))
869(define (class-slots class)
870  (##tinyclos#slot-ref class 'slots) )
871
872(define (class-name class)
873  (##tinyclos#slot-ref class 'name))
874
875(define (generic-methods generic)
876  (##tinyclos#slot-ref generic 'methods) )
877
878(define (method-specializers method)
879  (##tinyclos#slot-ref method 'specializers) )
880(define (method-procedure method)
881  (##tinyclos#slot-ref method 'procedure) )
882
883(define (class-cpl class)
884  (##tinyclos#slot-ref class 'cpl) )
885
886;;; Inline procedures inside this module only:
887
888(eval-when (compile)
889  (define-syntax define-inline-accessor
890    (syntax-rules ()
891      ((_ name slot)
892       (define-syntax name
893         (syntax-rules ()
894           ((_ class) (slot-ref class 'slot)))))))
895  (define-inline-accessor class-direct-slots direct-slots)
896  (define-inline-accessor class-direct-supers direct-supers)
897  (define-inline-accessor class-slots slots)
898  (define-inline-accessor class-name name)
899  (define-inline-accessor generic-methods methods)
900  (define-inline-accessor method-specializers specializers)
901  (define-inline-accessor method-procedure procedure)
902  (define-inline-accessor class-cpl cpl))
903
904;
905; The next 7 clusters define the 6 initial classes.  It takes 7 to 6
906; because the first and fourth both contribute to <class>.
907;
908
909(define getters-n-setters-for-class       ;see lookup-slot-info
910  (let loop ([lst the-slots-of-a-class] [i 0])
911    (if (null? lst)
912        '()
913        (cons (cons (%car lst)
914                    (cons (lambda (o)   (%instance-ref  o i))
915                          (lambda (o n) (%instance-set! o i n)) ) )
916              (loop (%cdr lst) (add1 i)) ) ) ) )
917
918(define <class> (%allocate-instance #f (length the-slots-of-a-class)))
919(%set-instance-class! <class> <class>)
920
921(define <top>          (make <class>
922                             'direct-supers '()
923                             'direct-slots  '()
924                             'name          "top"))
925
926(define <object>       (make <class>
927                             'direct-supers (list <top>)
928                             'direct-slots  '()
929                             'name          "object"))
930
931;
932; This cluster, together with the first cluster above that defines
933; <class> and sets its class, have the effect of:
934;
935;   (define <class>
936;     (make <class>
937;           'direct-supers (list <object>)
938;           'direct-slots  (list 'direct-supers ...)))
939;
940
941(%instance-set! <class> 0 (list <object>))                  ;d supers
942(%instance-set! <class> 1 (map list the-slots-of-a-class))  ;d slots
943(%instance-set! <class> 2 (list <class> <object> <top>))    ;cpl
944(%instance-set! <class> 3 (map list the-slots-of-a-class))  ;slots
945(%instance-set! <class> 4 (length the-slots-of-a-class))    ;nfields
946(%instance-set! <class> 5 (map (lambda (s)                  ;field-ini..
947                                 (lambda () (void)))
948                               the-slots-of-a-class))
949(%instance-set! <class> 6 '())
950(%instance-set! <class> 7 'class)
951(%instance-cache-set! <class> (%random-fixnum #x10000))
952
953(define <procedure-class> (make <class>
954                                'direct-supers (list <class>)
955                                'direct-slots  '()
956                                'name          "procedure-class"))
957
958(define <entity-class>    (make <class>
959                                'direct-supers (list <procedure-class>)
960                                'direct-slots  '()
961                                'name          "entity-class"))
962
963(define <generic>         (make <entity-class>
964                                'direct-supers (list <object>)
965                                'direct-slots  (list 'methods)
966                                'name          "generic"))
967
968(define <method>          (make <class>
969                                'direct-supers (list <object>)
970                                'direct-slots  (list 'specializers 'procedure)
971                                'name          "method"))
972
973;
974; These are the convenient syntax we expose to the base-level user.
975;
976
977(define (make-class direct-supers direct-slots . name)
978  (make <class>
979        'direct-supers direct-supers
980        'direct-slots  direct-slots
981        'name          (optional name "(anonymous)")) )
982
983(define (make-generic . name)
984  (make <generic>
985        'name         (optional name "(unnamed)")) )
986
987(define (make-method specializers procedure)
988  (make <method>
989        'specializers specializers
990        'procedure    procedure) )
991
992;
993; The initialization protocol
994;
995
996(define initialize (make-generic "initialize"))
997
998;
999; The instance structure protocol.
1000;
1001
1002(define allocate-instance (make-generic "allocate-instance"))
1003(define compute-getter-and-setter (make-generic "compute-getter-and-setter"))
1004
1005;
1006; The class initialization protocol.
1007;
1008
1009(define compute-cpl (make-generic "compute-cpl"))
1010(define compute-slots (make-generic "compute-slots"))
1011
1012;
1013; The generic invocation protocol.
1014;
1015
1016(define compute-apply-generic         (make-generic "compute-apply-generic"))
1017(define compute-methods               (make-generic "compute-methods"))
1018(define compute-method-more-specific? (make-generic "compute-method-more-specific?"))
1019(define compute-apply-methods         (make-generic "compute-apply-methods"))
1020
1021;
1022; The next thing to do is bootstrap generic functions.
1023;
1024
1025(define generic-invocation-generics (list compute-apply-generic
1026                                          compute-methods
1027                                          compute-method-more-specific?
1028                                          compute-apply-methods))
1029
1030(define (add-method generic method)
1031  (##tinyclos#slot-set!
1032    generic
1033    'methods
1034    (let* ([ms1 (method-specializers method)]
1035           [l1 (length ms1)] )
1036      (let filter-in-method ([methods (##tinyclos#slot-ref generic 'methods)])
1037        (if (null? methods)
1038            (list method)
1039            (let* ([mm (%car methods)]
1040                   [ms2 (method-specializers mm)]
1041                   [l2 (length ms2)])
1042              (cond ((> l1 l2)
1043                     (cons mm (filter-in-method (%cdr methods))))
1044                    ((< l1 l2)
1045                     (cons method methods))
1046                    (else
1047                     (let check-method ([ms1 ms1]
1048                                        [ms2 ms2])
1049                       (cond ((and (null? ms1) (null? ms2))
1050                              (cons method (%cdr methods))) ;; skip the method already in the generic
1051                             ((eq? (%car ms1) (%car ms2))
1052                              (check-method (%cdr ms1) (%cdr ms2)))
1053                             (else
1054                              (cons mm (filter-in-method (%cdr methods)))))))))))))
1055    (if (memq generic generic-invocation-generics)
1056        (set! method-cache-tag (vector))
1057        (%entity-cache-set! generic #f) )
1058    (%set-entity-proc! generic (compute-apply-generic generic)) )
1059
1060;
1061; Adding a method calls COMPUTE-APPLY-GENERIC, the result of which calls
1062; the other generics in the generic invocation protocol.  Two, related,
1063; problems come up.  A chicken and egg problem and a infinite regress
1064; problem.
1065;
1066; In order to add our first method to COMPUTE-APPLY-GENERIC, we need
1067; something sitting there, so it can be called.  The first definition
1068; below does that.
1069;
1070; Then, the second definition solves both the infinite regress and the
1071; not having enough of the protocol around to build itself problem the
1072; same way: it special cases invocation of generics in the invocation
1073; protocol.
1074;
1075;
1076
1077(%set-entity-proc! compute-apply-generic
1078     (lambda (generic)             ;The ONE time this is called
1079                                   ;it doesn't get cnm.
1080       (lambda args
1081         (apply (method-procedure (car (generic-methods generic)))
1082                #f args)))) ;But, the ONE time it is run,
1083                            ;it needs to pass a dummy
1084                            ;value for cnm!
1085
1086(let ([symbol-vector (vector 'instance entity-tag)])
1087
1088  ; Compute class ID from object
1089  (define hash-arg-list
1090    (foreign-lambda* unsigned-int ((scheme-object args) (scheme-object svector)) "
1091      C_word tag, h, x;
1092      int n, i, j, len = 0;
1093      for(i = 0; args != C_SCHEME_END_OF_LIST; args = C_block_item(args, 1)) {
1094        x = C_block_item(args, 0);
1095        if(C_immediatep(x)) {
1096          switch(x) {
1097            case C_SCHEME_END_OF_LIST: i += 1; break;
1098            case C_SCHEME_TRUE:
1099            case C_SCHEME_FALSE: i += 3; break;
1100            case C_SCHEME_END_OF_FILE: i += 7; break;
1101            case C_SCHEME_UNDEFINED: i += 5; break;
1102            default:
1103              if(x & C_FIXNUM_BIT) i += 2;
1104              else i += 4;
1105          }
1106        }
1107        else {
1108          h = C_header_bits(x);
1109          switch(h) {
1110          case C_STRUCTURE_TYPE:
1111            tag = C_block_item(x, 0);
1112            if(tag == C_block_item(svector, 0)) { /* instance */
1113              if((tag = C_block_item(C_block_item(x, 1), 2)) != C_SCHEME_FALSE) i += C_unfix(tag);
1114              else i += C_header_size(x) << 4;
1115            }
1116            else i += 17;
1117            break;
1118          case C_CLOSURE_TYPE:
1119            n = C_header_size(x);
1120            if(n > 3 && C_block_item(svector, 1) == C_block_item(x, n - 1)) {
1121              if((tag = C_block_item(C_block_item(C_block_item(x, n - 2), 2), 2)) != C_SCHEME_FALSE) i += C_unfix(tag);
1122              else i += 13;
1123            }
1124            break;
1125          case C_SYMBOL_TYPE: i += 8; break;
1126          case C_BYTEVECTOR_TYPE: i += 16; break;
1127          case C_VECTOR_TYPE: i += 9; break;
1128          case C_PAIR_TYPE: i += 10; break;
1129          case C_FLONUM_TYPE: i += 11; break;
1130          case C_STRING_TYPE: i += 12; break;
1131          case C_PORT_TYPE: i += C_block_item(x, 1) ? 15 : 14; break;
1132          default: i += 255;
1133          }
1134        }
1135        ++len;
1136      }
1137      return((i + len) & (C_METHOD_CACHE_SIZE - 1));") )
1138
1139  (add-method compute-apply-generic
1140    (make-method (list <generic>)
1141      (lambda (call-next-method generic)
1142        (lambda args
1143          (let ([mc (%entity-cache-ref generic)])
1144            (when (or (not mc) (not (eq? method-cache-tag (%car mc))))
1145              (set! mc (make-method-cache))
1146              (%entity-cache-set! generic mc) )
1147            (let* ([classes (and method-caching-enabled (map class-of args))]
1148                   [key (and classes (hash-arg-list args symbol-vector))]
1149                   [e (and classes (method-cache-lookup mc key classes))] )
1150              ;(unless (##sys#immediate? e) (print (%entity-name generic) ": " key))
1151              (if (not (##sys#immediate? e))
1152                  (e args)
1153                  (let ([cam
1154                         (if (and (memq generic generic-invocation-generics)
1155                                  (memq (car args) generic-invocation-generics))
1156                             (let ([proc
1157                                    (method-procedure
1158                                      ; select the first method of one argument
1159                                     (let loop ([lis (generic-methods generic)])
1160                                       (if (null? lis)
1161                                         (##sys#error "cannot find original compute-apply-generic")
1162                                         (let* ([h (%car lis)]
1163                                                [ms (method-specializers h)])
1164                                           (if (= 1 (length ms))
1165                                               h
1166                                               (loop (%cdr lis))))))) ] )
1167                               (lambda (args) (apply proc #f args)) )
1168                             (let ([x (compute-apply-methods generic)]
1169                                   [y ((compute-methods generic) args)] )
1170                               (lambda (args) (x y args)) ) ) ] )
1171                    (when (and e method-caching-enabled)
1172                      (let ([v (%cdr mc)])
1173                        (%vector-set! v e classes)
1174                        (%vector-set! v (add1 e) cam) ) )
1175                    (cam args) ) ) ) ) ) ) ) ) )
1176
1177(add-method compute-methods
1178  (make-method (list <generic>)
1179    (lambda (call-next-method generic)
1180      (lambda (args)
1181        (let ([applicable
1182               (filter-in (lambda (method)
1183                            (let check-applicable ([lst1 (method-specializers method)]
1184                                                   [lst2 args])
1185                              (cond ((null? lst1) #t)
1186                                    ((null? lst2) #f)
1187                                    (else
1188                                      (and (applicable? (%car lst1) (%car lst2))
1189                                           (check-applicable (%cdr lst1) (%cdr lst2)))))))
1190                          (generic-methods generic) ) ] )
1191          (if (or (null? applicable) (null? (%cdr applicable)))
1192              applicable
1193              (let ([cmms (compute-method-more-specific? generic)])
1194                (sort applicable (lambda (m1 m2) (cmms m1 m2 args))) ) ) ) ) ) ) )
1195
1196(add-method compute-method-more-specific?
1197  (make-method (list <generic>)
1198    (lambda (call-next-method generic)
1199      (lambda (m1 m2 args)
1200        (let loop ((specls1 (method-specializers m1))
1201                   (specls2 (method-specializers m2))
1202                   (args args))
1203          (cond-expand
1204           [unsafe
1205            (let ((c1  (%car specls1))
1206                  (c2  (%car specls2))
1207                  (arg (%car args)))
1208              (if (eq? c1 c2)
1209                  (loop (%cdr specls1)
1210                        (%cdr specls2)
1211                        (%cdr args))
1212                  (more-specific? c1 c2 arg))) ]
1213           [else
1214            (cond ((and (null? specls1) (null? specls2))
1215                   (##sys#error "two methods are equally specific" generic))
1216                  #; ; Ok to have diff # of specializers
1217                  ((or (null? specls1) (null? specls2))
1218                   (##sys#error "two methods have different number of specializers" generic))
1219                  ((null? specls1) #f)
1220                  ((null? specls2) #t)
1221                  ((null? args)
1222                   (##sys#error "fewer arguments than specializers" generic))
1223                  (else
1224                   (let ((c1  (%car specls1))
1225                         (c2  (%car specls2))
1226                         (arg (%car args)))
1227                     (if (eq? c1 c2)
1228                         (loop (%cdr specls1)
1229                               (%cdr specls2)
1230                               (%cdr args))
1231                         (more-specific? c1 c2 arg)))) ) ] ) ) ) ) ) )
1232
1233(add-method compute-apply-methods
1234  (make-method (list <generic>)
1235    (lambda (call-next-method generic)
1236      (lambda (methods args)
1237        (letrec ((one-step
1238                  (lambda (tail)
1239                    (lambda ()
1240                      (cond-expand
1241                       [unsafe]
1242                       [else
1243                        (when (null? tail)
1244                          (##sys#error "call-next-method: no methods left" generic) ) ] )
1245                      (apply (method-procedure (%car tail))
1246                             (one-step (%cdr tail)) args)))))
1247          ((one-step methods)))))))
1248
1249(define (applicable? c arg)
1250  (memq c (class-cpl (class-of arg))) )
1251
1252(define (more-specific? c1 c2 arg)
1253  (memq c2 (memq c1 (class-cpl (class-of arg)))) )
1254
1255(add-method initialize
1256  (make-method (list <top>)
1257    (lambda (call-next-method object initargs)
1258      (##sys#error "cannot initialize object" object) ) ) )
1259
1260(add-method initialize
1261  (make-method (list <object>)
1262    (lambda (call-next-method object initargs) object)))
1263
1264(add-method initialize
1265  (make-method (list <class>)
1266    (lambda (call-next-method class initargs)
1267      (call-next-method)
1268      (##tinyclos#slot-set! class 'direct-supers (fast-getl initargs 'direct-supers '()))
1269      (let ([dslots
1270              (map (lambda (s) (if (pair? s) s (list s)))
1271                   (fast-getl initargs 'direct-slots '()))])
1272        (let ([namestr
1273                (let ([name (fast-getl initargs 'name "(anonymous)")])
1274                  (cond [(symbol? name) (##sys#symbol->string name)]
1275                        [(string? name) name]
1276                        [else (##sys#signal-hook #:type-error
1277                                                 'initialize "invalid class name" name) ] ) ) ] )
1278          (##tinyclos#slot-set! class 'direct-slots dslots)
1279          (##tinyclos#slot-set! class 'cpl (compute-cpl class))
1280          (##tinyclos#slot-set! class 'name
1281                                      (let ([namestrlen (%string-length namestr)])
1282                                        (if (and (> namestrlen 0)
1283                                                 (char=? #\< (string-ref namestr 0))
1284                                                 (char=? #\> (string-ref namestr (sub1 namestrlen))) )
1285                                            (substring namestr 1 (sub1 namestrlen))
1286                                            namestr) ))
1287          (let ([slots (compute-slots class)])
1288            (##tinyclos#slot-set! class 'slots slots)
1289            (when (= basic-class-instance-size (%structure-length class))
1290              (%instance-cache-set! class (%random-fixnum #x10000)) )
1291            (let* ([nfields 0]
1292                   [field-initializers '()]
1293                   [allocator
1294                    (lambda (init)
1295                      (let ((f nfields))
1296                        (set! nfields (add1 nfields))
1297                        (set! field-initializers (cons init field-initializers))
1298                        (values (lambda (o)   (get-field  o f))
1299                                (lambda (o n) (set-field! o f n)))))]
1300                   [getters-n-setters
1301                    (map (lambda (slot)
1302                           (cons (%car slot)
1303                                 (call-with-values (lambda ()
1304                                                     (compute-getter-and-setter class slot allocator))
1305                                                   cons) ) )
1306                         slots) ] )
1307              (##tinyclos#slot-set! class 'nfields nfields)
1308              (##tinyclos#slot-set! class 'field-initializers (reverse field-initializers))
1309              (##tinyclos#slot-set! class 'getters-n-setters getters-n-setters))))) ) ) )
1310
1311(add-method initialize
1312  (make-method (list <generic>)
1313    (lambda (call-next-method generic initargs)
1314      (call-next-method)
1315      (unless (%entity? generic)
1316        (##sys#error 'initialize "generic is not an entity") )
1317      (##tinyclos#slot-set! generic 'methods '())
1318      (%set-entity-name! generic (fast-getl initargs 'name "(unnamed)"))
1319      (%set-entity-proc! generic (lambda args (##sys#error "has no methods" generic))))))
1320
1321(add-method initialize
1322  (make-method (list <method>)
1323    (lambda (call-next-method method initargs)
1324      (call-next-method)
1325      (##tinyclos#slot-set! method 'specializers (getl initargs 'specializers))
1326      (##tinyclos#slot-set! method 'procedure    (getl initargs 'procedure)))))
1327
1328(add-method allocate-instance
1329  (make-method (list <class>)
1330    (lambda (call-next-method class)
1331      (let* ((field-initializers (##tinyclos#slot-ref class 'field-initializers))
1332             (new (%allocate-instance class (length field-initializers))))
1333        (let loop ((n 0) (inits field-initializers))
1334          (if (null? inits)
1335              new
1336              (begin
1337                (%instance-set! new n ((%car inits)))
1338                (loop (add1 n) (%cdr inits)))))))))
1339
1340(add-method allocate-instance
1341  (make-method (list <entity-class>)
1342    (lambda (call-next-method class)
1343      (let* ([field-initializers (##tinyclos#slot-ref class 'field-initializers)]
1344             [new (%allocate-entity class (length field-initializers) "(unnamed)") ] )
1345        (let loop ((n 0) (inits field-initializers))
1346          (if (null? inits)
1347              new
1348              (begin
1349                (%entity-set! new n ((%car inits)))
1350                (loop (add1 n) (%cdr inits)))))))))
1351
1352(add-method compute-cpl
1353    (make-method (list <class>)
1354      (lambda (call-next-method class)
1355        (compute-std-cpl class class-direct-supers))))
1356
1357(add-method compute-slots
1358  (make-method (list <class>)
1359    (lambda (call-next-method class)
1360      (let collect ((to-process (apply append (map class-direct-slots (class-cpl class))))
1361                    (result '()))
1362        (if (null? to-process)
1363            (reverse result)
1364            (let* ((current (%car to-process))
1365                   (name (%car current))
1366                   (others '())
1367                   (remaining-to-process
1368                     (filter-in (lambda (o)
1369                                  (if (eq? (%car o) name)
1370                                      (begin
1371                                        (set! others (cons o others))
1372                                        #f)
1373                                      #t))
1374                                (%cdr to-process))))
1375              (collect remaining-to-process
1376                       (cons (append current (apply append (map (lambda (x) (%cdr x)) others)))
1377                             result))))))))
1378
1379(add-method compute-getter-and-setter
1380  (make-method (list <class>)
1381    (lambda (call-next-method class slot allocator)
1382      (allocator (lambda () (void))))))
1383
1384;
1385; Now everything works, both generic functions and classes, so we can
1386; turn on the real MAKE.
1387;
1388
1389(set! make
1390  (lambda (class . initargs)
1391    (let ((instance (allocate-instance class)))
1392      (initialize instance initargs)
1393      instance)))
1394
1395;
1396; Now define what CLOS calls `built in' classes.
1397;
1398
1399(define <primitive-class>
1400  (make <class>
1401        'direct-supers (list <class>)
1402        'direct-slots  '()
1403        'name          "primitive-class"))
1404
1405(define <primitive>
1406  (make <class>
1407        'direct-supers (list <top>)
1408        'direct-slots  '()
1409        'name          "primitive"))
1410
1411(add-method initialize
1412  (make-method (list <primitive>)
1413    (lambda (call-next-method object initargs) object)))
1414
1415(define (make-primitive-class name . sclasses)
1416 (make <primitive-class>
1417       'direct-supers (if (null? sclasses) (list <primitive>) sclasses)
1418       'direct-slots  '()
1419       'name          name) )
1420
1421(define (make-port-class name)
1422  (make <class>
1423        'direct-supers (list <port>)
1424        'direct-slots  '()
1425        'name          name) )
1426
1427(define (make-structure-class name)
1428  (make-primitive-class name <structure>) )
1429
1430(define (make-extended-procedure-class name)
1431  (make-primitive-class name <procedure>) )
1432
1433(define (make-tagged-pointer-class name)
1434  (make <class>
1435        'direct-supers (list <tagged-pointer>)
1436        'direct-slots  '()
1437        'name          name) )
1438
1439(define <void>                  (make-primitive-class "void"))
1440(define <null>                  (make-primitive-class "null"))
1441(define <end-of-file>           (make-primitive-class "end-of-file"))
1442(define <boolean>               (make-primitive-class "boolean"))
1443(define <symbol>                (make-primitive-class "symbol"))
1444(define <char>                  (make-primitive-class "char"))
1445(define <number>                (make-primitive-class "number"))
1446(define <integer>               (make-primitive-class "integer" <number>))
1447(define <exact>                 (make-primitive-class "exact" <integer>))
1448(define <inexact>               (make-primitive-class "inexact" <number>))
1449(define <vector>                (make-primitive-class "vector"))
1450(define <pair>                  (make-primitive-class "pair"))
1451(define <string>                (make-primitive-class "string"))
1452(define <port>                  (make-primitive-class "port"))
1453(define <input-port>            (make-port-class "input-port"))
1454(define <output-port>           (make-port-class "output-port"))
1455(define <procedure>             (make-primitive-class "procedure" <procedure-class>))
1456(define <blob>                  (make-primitive-class "blob"))
1457(define <byte-vector>           (make-primitive-class "byte-vector")) ; DEPRECATED
1458(define <structure>             (make-primitive-class "structure"))
1459(define <environment>           (make-structure-class "environment"))
1460(define <hash-table>            (make-structure-class "hash-table"))
1461(define <promise>               (make-structure-class "promise"))
1462(define <queue>                 (make-structure-class "queue"))
1463(define <condition>             (make-structure-class "condition"))
1464(define <condition-variable>    (make-structure-class "condition-variable"))
1465(define <continuation>          (make-structure-class "continuation"))
1466(define <char-set>              (make-structure-class "char-set"))
1467(define <time>                  (make-structure-class "time"))
1468(define <lock>                  (make-structure-class "lock"))
1469(define <mmap>                  (make-structure-class "mmap"))
1470(define <array>                 (make-structure-class "array"))
1471(define <tcp-listener>          (make-structure-class "tcp-listener"))
1472(define <thread>                (make-structure-class "thread"))
1473(define <mutex>                 (make-structure-class "mutex"))
1474(define <regexp>                (make-structure-class "regexp"))
1475(define <read-table>            (make-structure-class "read-table"))
1476(define <locative>              (make-primitive-class "locative"))
1477(define <pointer>               (make-primitive-class "pointer"))
1478(define <swig-pointer>          (make-primitive-class "swig-pointer" <pointer>))
1479(define <tagged-pointer>        (make-primitive-class "tagged-pointer" <pointer>))
1480(define <u8vector>              (make-primitive-class "u8vector" <vector>))
1481(define <s8vector>              (make-primitive-class "s8vector" <vector>))
1482(define <u16vector>             (make-primitive-class "u16vector" <vector>))
1483(define <s16vector>             (make-primitive-class "s16vector" <vector>))
1484(define <u32vector>             (make-primitive-class "u32vector" <vector>))
1485(define <s32vector>             (make-primitive-class "s32vector" <vector>))
1486(define <f32vector>             (make-primitive-class "f32vector" <vector>))
1487(define <f64vector>             (make-primitive-class "f64vector" <vector>))
1488
1489(define <c++-object>
1490  (make <class>
1491        'direct-supers (list <object>)
1492        'direct-slots  '(this)
1493        'name          "c++-object"))
1494
1495(add-method initialize
1496  (make-method (list <c++-object>)
1497    (lambda (call-next-method obj initargs)
1498      (when (and (pair? initargs) (eq? 'this (%car initargs)))
1499        (##tinyclos#slot-set! obj 'this (cadr initargs)) ) ) ) )
1500
1501(set! method-caching-enabled #t)
1502
1503;;; Utilities:
1504
1505(define (initialize-slots object initargs)
1506  (##sys#check-list initargs 'initialize-slots)
1507  (for-each
1508   (lambda (slot)
1509     (let* ([name   (car slot)]
1510            [value  (fast-getl initargs name not-found-object)] )
1511       (unless (eq? value not-found-object)
1512         (slot-set! object name value))))
1513   (class-slots (class-of object))) )
1514
1515(define print-object (make-generic "print-object"))
1516(define describe-object (make-generic "describe-object"))
1517
1518(add-method print-object
1519  (make-method (list <object>)
1520    (lambda (call-next-method x . port)
1521      (fprintf (optional port ##sys#standard-output) "#<~A>" (class-name (class-of x))) ) ) )
1522
1523(add-method print-object
1524  (make-method (list <primitive>)
1525    (lambda (call-next-method x . port)
1526      (write x (optional port ##sys#standard-output)) ) ) )
1527
1528(add-method print-object
1529  (make-method (list <class>)
1530    (lambda (call-next-method x . port)
1531      (fprintf (optional port ##sys#standard-output) "#<class ~A>" (class-name x)) ) ) )
1532
1533(add-method print-object
1534  (make-method (list <generic>)
1535    (lambda (call-next-method x . port)
1536      (fprintf (optional port ##sys#standard-output) "#<generic ~A>" (%entity-name x)) ) ) )
1537
1538(add-method describe-object
1539  (make-method (list <object>)
1540    (lambda (call-next-method x . port)
1541      (let ([class (class-of x)]
1542            [port (optional port ##sys#standard-output)] )
1543        (fprintf port "instance of class ~A:~%" (class-name class))
1544        (for-each
1545         (lambda (s)
1546           (let ([slot (car s)])
1547             (fprintf port " ~S\t-> ~S~%" slot (slot-ref x slot)) ) )
1548         (class-slots class) ) ) ) ) )
1549
1550(add-method describe-object
1551  (make-method (list <class>)
1552    (lambda (call-next-method x . port)
1553      (fprintf (optional port ##sys#standard-output) "class ~A~%" (class-name x)) ) ) )
1554
1555(add-method describe-object
1556  (make-method (list <generic>)
1557    (lambda (call-next-method x . port)
1558      (fprintf (optional port ##sys#standard-output) "generic ~A~%" (%entity-name x)) ) ) )
1559
1560(define ensure-generic
1561  (let ([make-generic make-generic])
1562    (lambda (x sym)
1563      (if (%entity? x)
1564          x
1565          (make-generic (##sys#symbol->string sym)) ) ) ) )
1566
1567(define add-global-method
1568  (let ([make-method make-method]
1569        [add-method add-method] )
1570    (lambda (val sym specializers proc)
1571      (let ([g (ensure-generic val sym)])
1572        (add-method g (make-method specializers proc))
1573        g) ) ) )
1574
1575(define (instance? x)
1576  (or (%instance? x) (%entity? x)) )
1577
1578(define (subclass? x y)
1579  (if (memq y (compute-cpl x)) #t #f) )
1580
1581(define (instance-of? x class)
1582  (let ([cl (class-of x)])
1583    (or (eq? cl class)
1584        (subclass? cl class) ) ) )
1585
1586(define (make-instance-from-pointer ptr cls)
1587  (and ptr
1588       (not (%null-pointer? ptr))
1589       (make cls 'this ptr) ) )
1590
1591(define (make/copy x . initargs)
1592  (let ([class (class-of x)])
1593    (apply make class
1594                (let ([initlist initargs]
1595                      [inited-slot?
1596                        (lambda (nam)
1597                          (let loop ([testing? #t] [initargs initargs])
1598                            (and (pair? initargs)
1599                                 (or (and testing? (equal? nam (car initargs)))
1600                                     (loop (not testing?) (cdr initargs)) ) ) ) )])
1601                  (for-each
1602                    (lambda (s)
1603                      (let ([nam (car s)])
1604                        (unless (inited-slot? nam)
1605                          (set! initlist (cons nam (cons (slot-ref x nam) initlist))) ) ) )
1606                    (class-slots class))
1607                  initlist ) ) ) )
1608
1609;; Procedural interface for extending the "builtin" class system
1610
1611; Primitive
1612
1613(define (new-primitive-class name pred . sclasses)
1614  (let ((cls (apply make-primitive-class name sclasses)))
1615    (update-primitive-class-of pred cls)
1616    cls ) )
1617
1618(define (delete-primitive-class cls)
1619  (delete-primitive-class-of cls) )
1620
1621; Structure
1622
1623(define (new-structure-class name tag)
1624  (let ((cls (make-structure-class name)))
1625    (update-structure-class-of tag cls)
1626    cls ) )
1627
1628(define (delete-structure-class cls)
1629  (delete-structure-class-of cls) )
1630
1631; Tagged-pointer
1632
1633(define (new-tagged-pointer-class name tag)
1634  (let ((cls (make-tagged-pointer-class name)))
1635    (update-tagged-pointer-class-of tag cls)
1636    cls ) )
1637
1638(define (delete-tagged-pointer-class cls)
1639  (delete-tagged-pointer-class-of cls) )
1640
1641; Procedure
1642
1643(define (new-extended-procedure-class name pred)
1644  (let ((cls (make-extended-procedure-class name)))
1645    (update-extended-procedure-class-of pred cls)
1646    cls ) )
1647
1648(define (delete-extended-procedure-class cls)
1649  (delete-extended-procedure-class-of cls) )
1650
1651)