OSDN Git Service

Merge branch 'binutils'
[pf3gnuchains/pf3gnuchains3x.git] / cgen / cos.scm
1 ; Cgen's Object System.
2 ; Copyright (C) 2000 Red Hat, Inc.
3 ; This file is part of CGEN.
4 ; See file COPYING.CGEN for details.
5 ;
6 ; When Guile has an official object implementation that is stable, things will
7 ; be switched over then.  Until such time, there's no point in getting hyper
8 ; (although doing so is certainly fun, but only to a point).
9 ; If the Guile team decides there won't be any official object system
10 ; (which isn't unreasonable) then we'll pick the final object system then.
11 ; Until such time, there are better things to do than trying to build a
12 ; better object system.  If this is important enough to you, help the Guile
13 ; team finish the module(/object?) system.
14 ;
15 ; Classes look like:
16 ;
17 ; #(class-tag
18 ;   class-name
19 ;   parent-name-list
20 ;   elm-alist
21 ;   method-alist
22 ;   full-elm-initial-list
23 ;   full-method-alist ; ??? not currently used
24 ;   class-descriptor)
25 ;
26 ; PARENT-NAME-LIST is a list of the names of parent classes (the inheritance
27 ; tree).
28 ;
29 ; ELM-ALIST is an alist of (symbol private? vector-index . initial-value)
30 ; for this class only.
31 ; Values can be looked up by name, via elm-make-[gs]etter routines, or
32 ; methods can use elm-get/set! for speed.
33 ; Various Lisp (or Lisp-like) OOP systems (e.g. CLOS, Dylan) call these
34 ; "slots".  Maybe for consistency "slot" would be a better name.  Some might
35 ; confuse that with intentions at directions.  Given that something better
36 ; will eventually happen, being deliberately different is useful.
37 ;
38 ; METHOD-ALIST is an alist of (symbol . (virtual? . procedure)) for this
39 ; class only.
40 ;
41 ; FULL-ELM-INITIAL-LIST is the elements of the flattened inheritance tree.
42 ; Initially it is #f meaning it hasn't been computed yet.
43 ; It is computed when the class is first instantiated.  During development,
44 ; it can be reset to #f after some module has been reloaded (requires all
45 ; object instantiation happens later of course).
46 ;
47 ; FULL-METHOD-ALIST is an alist of the methods of the flattened inheritance
48 ; tree.  Each element is (symbol . (parent-list-entry . method)).
49 ; Initially it is #f meaning it hasn't been computed yet.
50 ; It is computed when the class is first instantiated.  During development,
51 ; it can be reset to #f after some module has been reloaded (requires all
52 ; object instantiation happens later of course).
53 ;
54 ; CLASS-DESCRIPTOR is the processed form of parent-name-list.
55 ; There is an entry for the class and one for each parent (recursively):
56 ; (class mi? (base-offset . delta) child-backpointer (parent1-entry) ...).
57 ; mi? is #t if the class or any parent class has multiple inheritance.
58 ; This is used by the element access routines.
59 ; base-offset is the offset in the element vector of the baseclass (or first
60 ; baseclass in the mi case).
61 ; delta is the offset from base-offset of the class's own elements
62 ; (as opposed to elements in any parent class).
63 ; child-backpointer is #f in the top level object.
64 ; ??? child->subclass, parent->superclass?
65 ; Initially the class-descriptor is #f meaning it hasn't been computed yet.
66 ; It is computed when the class is first instantiated.  During development,
67 ; it can be reset to #f after some module has been reloaded (requires all
68 ; object instantiation to happen later of course).
69 ;
70 ; An object is a vector of 2 elements: #(object-elements class-descriptor).
71 ; ??? Things would be simpler if objects were a pair but that makes eval'ing
72 ; them trickier.  Vectors are nice in that they're self-evaluating, though
73 ; due to the self-referencing, which Guile 1.2 can't handle, apps have to
74 ; be careful.
75 ; ??? We could use smobs/records/whatever but the difference isn't big enough
76 ; for me to care at this point in time.
77 ;
78 ; `object-elements' looks like:
79 ;
80 ; #(object-tag
81 ;   class
82 ;   element1
83 ;   element2
84 ;   ...)
85 ;
86 ; CLASS is the class the object is an instance of.
87 ;
88 ; User visible procs:
89 ;
90 ; (class-make name parents elements methods) -> class
91 ;
92 ; Create a class.  The result is then passed back by procedures requiring
93 ; a class argument.  Note however that PARENTS is a list of class names,
94 ; not the class data type.  This allows reloading the definition of a
95 ; parent class without having to reload any subclasses.  To implement this
96 ; classes are recorded internally, and `object-init!' must be called if any
97 ; class has been redefined.
98 ;
99 ; (class-list) -> list of all defined classes
100 ;
101 ; (class-name class) -> name of CLASS
102 ;
103 ; (class-lookup class-name) -> class
104 ;
105 ; (class-instance? class object) -> #t if OBJECT is an instance of CLASS
106 ;
107 ; (object-class object) -> class of OBJECT
108 ;
109 ; (object-class-name object) -> class name of OBJECT
110 ;
111 ; (send object method-name . args) -> result of invoking METHOD-NAME
112 ;
113 ; (send-next object method-name . args) -> result of invoking next METHOD-NAME
114 ;
115 ; (new class) -> instantiate CLASS
116 ;
117 ; The object is initialized with values specified when CLASS
118 ; (and its parent classes) was defined.
119 ;
120 ; (vmake class . args) -> instantiate class and initialize it with 'vmake!
121 ;
122 ; This is shorthand for (send (new class) 'vmake! args).
123 ; ARGS is a list of option names and arguments (a la CLOS).
124 ; ??? Not implemented yet.
125 ;
126 ; (method-vmake! object . args) -> modify OBJECT from ARGS
127 ;
128 ; This is the standard 'vmake! method, available for use by user-written
129 ; 'vmake! methods.
130 ; ??? Not implemented yet.
131 ;
132 ; (make class . args) -> instantiate CLASS and initialize it with 'make!
133 ;
134 ; This is shorthand for (send (new class) 'make! arg1 ...).
135 ; This is a positional form of `new'.
136 ;
137 ; (method-make-make! class elm1-name elm2-name ...) -> unspecified
138 ;
139 ; Create a 'make! method that sets the specified elements.
140 ;
141 ; (object-copy object) -> copy of OBJ
142 ;
143 ; ??? Whether to discard the parent or keep it and retain specialization
144 ; is undecided.
145 ;
146 ; (object-copy-top object) -> copy of OBJECT with spec'n discarded
147 ;
148 ; (object-parent object parent-path) -> parent object in OBJECT via PARENT-PATH
149 ;
150 ; (class? foo) -> return #t if FOO is a class
151 ;
152 ; (object? foo) -> return #t if FOO is an object
153 ;
154 ; (method-make! class name lambda) -> unspecified
155 ;
156 ; Add method NAME to CLASS.
157 ;
158 ; (method-make-virtual! class name lambda) -> unspecified
159 ;
160 ; Add virtual method NAME to CLASS.
161 ;
162 ; (method-make-forward! class elm-name methods) -> unspecified
163 ;
164 ; Add METHODS to CLASS that pass the "message" onto the object in element
165 ; ELM-NAME.
166 ;                                 
167 ; (method-make-virtual-forward! class elm-name methods) -> unspecified
168 ;
169 ; Add virtual METHODS to CLASS that pass the "message" onto the object in
170 ; element ELM-NAME.
171 ;
172 ; (elm-get object elm-name) -> value of element ELM-NAME in OBJ
173 ;
174 ; Can only be used in methods.
175 ;
176 ; (elm-set! object elm-name new-value) -> unspecified
177 ;
178 ; Set element ELM-NAME in OBJECT to NEW-VALUE.
179 ; Can only be used in methods.
180 ;
181 ; (elm-make-getter class elm-name) -> lambda
182 ;
183 ; Return lambda to get the value of ELM-NAME in CLASS.
184 ;
185 ; (elm-make-setter class elm-name) -> lambda
186 ;
187 ; Return lambda to set the value of ELM-NAME in CLASS.
188 ;
189 ; Conventions used in this file:
190 ; - procs/vars internal to this file are prefixed with "-"
191 ;   [Of course this could all be put in a module; later if ever since
192 ;   once Guile has its own official object system we'll convert.  Note that
193 ;   it currently does not.]
194 ; - except for a few exceptions, public procs begin with one of
195 ;   class-, object-, elm-, method-.
196 ;   The exceptions are make, new, parent, send.
197 \f
198 (define -class-tag "class")
199 (define -object-tag "object")
200
201 ; List of all classes.
202
203 (define -class-list '())
204
205 ; ??? Were written as a procedures for Hobbit's sake (I think).
206 (define -object-unspecified #:unspecified)
207 (define -object-unbound #:unbound)
208
209 ; Associative list of classes to be traced.
210
211 (define -object-debug-classes #f)
212
213 ; Associative list of elements to be traced.
214
215 (define -object-debug-elements #f)
216
217 ; Associative list of messages to be traced.
218
219 (define -object-debug-methods #f)
220
221 ; True if error messages are verbose and debugging messages are printed.
222
223 (define -object-verbose? #f)
224
225 ; Cover fn to set verbosity.
226
227 (define (object-set-verbose! verbose?)
228   (set! -object-verbose? verbose?)
229 )
230
231 ; Signal error if not class/object.
232
233 (define (-class-check maybe-class proc-name . extra-text)
234   (if (not (class? maybe-class))
235       (apply -object-error
236              (append! (list proc-name maybe-class "not a class")
237                       extra-text)))
238   -object-unspecified
239 )
240 (define (-object-check-name maybe-name proc-name . extra-text)
241   (if (not (symbol? maybe-name))
242       (apply -object-error
243              (append! (list proc-name maybe-name) extra-text)))
244   -object-unspecified
245 )
246 (define (-object-check maybe-object proc-name . extra-text)
247   (if (not (object? maybe-object))
248       (apply -object-error
249              (append! (list proc-name maybe-object "not an object")
250                       extra-text)))
251   -object-unspecified
252 )
253
254 ; X is any arbitrary Scheme data.
255 (define (-object-error proc-name x . text)
256   (error (string-append proc-name ": " (apply string-append text)
257                         (if (object? x)
258                             (string-append
259                              " (class: " (-object-class-name x)
260                              (if (method-present? x 'get-name)
261                                  (string-append ", name: "
262                                                 (send x 'get-name))
263                                  "")
264                              ")")
265                             "")
266                         "")
267          x)
268 )
269 \f
270 ; Low level class operations.
271
272 ; Return boolean indicating if X is a class.
273
274 (define (class? class)
275   (and (vector? class) (eq? -class-tag (vector-ref class 0)))
276 )
277
278 ; Accessors.
279
280 (define (-class-name class) (vector-ref class 1))
281 (define (-class-parents class) (vector-ref class 2))
282 (define (-class-elements class) (vector-ref class 3))
283 (define (-class-methods class) (vector-ref class 4))
284 (define (-class-all-initial-values class) (vector-ref class 5))
285 (define (-class-all-methods class) (vector-ref class 6))
286 (define (-class-class-desc class) (vector-ref class 7))
287
288 (define (-class-set-parents! class parents)
289   (vector-set! class 2 parents)
290 )
291
292 (define (-class-set-elements! class elm-alist)
293   (vector-set! class 3 elm-alist)
294 )
295
296 (define (-class-set-methods! class method-alist)
297   (vector-set! class 4 method-alist)
298 )
299
300 (define (-class-set-all-initial-values! class init-list)
301   (vector-set! class 5 init-list)
302 )
303
304 (define (-class-set-all-methods! class all-meth-list)
305   (vector-set! class 6 all-meth-list)
306 )
307
308 (define (-class-set-class-desc! class parent-list)
309   (vector-set! class 7 parent-list)
310 )
311
312 ; Make a class.
313 ; The new definition overrides any existing definition.
314
315 (define (-class-make! name parents elements methods)
316   (let ((class (vector -class-tag name parents elements methods #f #f #f))
317         (list-entry (assq name -class-list)))
318     (if list-entry
319         (set-cdr! list-entry class)
320         (set! -class-list (acons name class -class-list)))
321     class)
322 )
323
324 ; Lookup a class given its name.
325 ; The result is the class or #f if not found.
326
327 (define (class-lookup name) (assq-ref -class-list name))
328
329 ; Return a list of all direct parent classes of CLASS.
330
331 (define (-class-parent-classes class)
332   ; -class-parents returns the names, we want the actual classes.
333   (let loop ((parents (-class-parents class))
334              (result '()))
335     (if (null? parents)
336         (reverse! result)
337         (let ((parent (class-lookup (car parents))))
338           (if (not parent)
339               ; The proc name we pass here is made up as we don't
340               ; want it to be the name of an internal proc.
341               (-object-error "class" (car parents) "not a class"))
342           (loop (cdr parents) (cons parent result)))))
343 )
344
345 ; Cover proc of -class-name for the outside world to use.
346 ; The result is the name of the class or #f if CLASS is not a class.
347 ; We could issue an error here, but to be consistent with object-class-name
348 ; we don't.
349
350 (define (class-name class)
351   (if (class? class)
352       (-class-name class)
353       #f)
354 )
355
356 ; Return a boolean indicating if CLASS or any parent class has
357 ; multiple inheritance.
358
359 (define (-class-mi? class)
360   (-class-desc-mi? (-class-class-desc class))
361 )
362 \f
363 ; Class descriptor utilities.
364 ; A class-descriptor is:
365 ; (class mi? (base-offset . delta) child-backpointer (parent1-entry) ...)
366
367 ;(define (-class-desc-make class offset bkptr parents)
368 ;   (append (list class offset bkptr) parents)
369 ;)
370 (define (-class-desc? maybe-class-desc)
371   (and (pair? maybe-class-desc)
372        (class? (car maybe-class-desc)))
373 )
374 (define -class-desc-class car)
375 (define -class-desc-mi? cadr)
376 (define -class-desc-offset caddr)
377 (define -class-desc-offset-base caaddr)
378 (define -class-desc-offset-delta cdaddr)
379 (define -class-desc-child cadddr)
380 (define -class-desc-parents cddddr)
381 ; Note that this is an assq on the classes themselves, not their names.
382 ; The result is the parent's class-descriptor.
383 (define -class-desc-lookup-parent assq)
384
385 ; Compute the class descriptor of CLASS.
386 ; OFFSET is the beginning offset in the element vector.
387 ; We can assume the parents of CLASS have already been initialized.
388 ;
389 ; A class-descriptor is:
390 ; (class mi? (base-offset . delta) child-backpointer (parent1-entry) ...)
391 ; MI? is a boolean indicating if multiple inheritance is present.
392 ; BASE-OFFSET is the offset into the object vector of the baseclass's elements
393 ; (or first baseclass in the mi case).
394 ; DELTA is the offset from BASE-OFFSET of the class's own elements.
395 ; CHILD is the backlink to the direct child class or #f for the top class.
396 ; ??? Is the use of `top' backwards from traditional usage?
397
398 (define (-class-compute-class-desc class offset child)
399
400   ; OFFSET must be global to the calculation because it is continually
401   ; incremented as we recurse down through the hierarchy (actually, as we
402   ; traverse back up).  At any point in time it is the offset from the start
403   ; of the element vector of the next class's elements.
404   ; Object elements are laid out using a depth first traversal of the
405   ; inheritance tree.
406
407   (define (compute1 class child base-offset)
408
409     ; Build the result first, then build our parents so that our parents have
410     ; the right value for the CHILD-BACKPOINTER field.
411     ; Use a bogus value for mi? and offset for the moment.
412     ; The correct values are set later.
413
414     (let ((result (list class #f (cons 999 999) child))
415           (mi? (> (length (-class-parents class)) 1)))
416
417       ; Recurse on the parents.
418       ; We use `append!' here as the location of `result' is now fixed so
419       ; that our parent's child-backpointer remains stable.
420
421       (append! result
422                (let loop ((parents (-class-parents class))
423                           (parent-descs '())
424                           (base-offset base-offset))
425                  (if (null? parents)
426                      (reverse! parent-descs)
427                      (let ((parent (class-lookup (car parents))))
428                        (if (not parent)
429                            ; The proc name we pass here is made up as we don't
430                            ; want it to be the name of an internal proc.
431                            (-object-error "class" (car parents) "not a class"))
432                        (if (and (not mi?)
433                                 (-class-mi? parent))
434                            (set! mi? #t))
435                        (let ((parent-desc (compute1 parent result base-offset)))
436                          (loop (cdr parents)
437                                (cons parent-desc parent-descs)
438                                offset))))))
439
440       (list-set! result 1 mi?)
441       (list-set! result 2 (cons base-offset (- offset base-offset)))
442       (set! offset (+ offset (length (-class-elements class))))
443       result))
444
445   (compute1 class child offset)
446 )
447
448 ; Return the top level class-descriptor of CLASS-DESC.
449
450 (define (-class-desc-top class-desc)
451   (if (-class-desc-child class-desc)
452       (-class-desc-top (-class-desc-child class-desc))
453       class-desc)
454 )
455
456 ; Pretty print a class descriptor.
457
458 (define (class-desc-dump class-desc)
459   (let* ((cep (current-error-port))
460          (top-desc (-class-desc-top class-desc))
461          (spaces (lambda (n port)
462                    (display (make-string n #\space) port)))
463          (writeln (lambda (indent port . args)
464                     (spaces indent port)
465                     (for-each (lambda (arg) (display arg port))
466                               args)
467                     (newline port)))
468          )
469     (letrec ((dump (lambda (cd indent)
470                      (writeln indent cep "Class: "
471                               (-class-name (-class-desc-class cd)))
472                      (writeln indent cep "  mi?:         "
473                               (-class-desc-mi? cd))
474                      (writeln indent cep "  base offset: "
475                               (-class-desc-offset-base cd))
476                      (writeln indent cep "  delta:       "
477                               (-class-desc-offset-delta cd))
478                      (writeln indent cep "  child:       "
479                               (if (-class-desc-child cd)
480                                   (-class-name (-class-desc-class
481                                                 (-class-desc-child cd)))
482                                   "-top-"))
483                      (for-each (lambda (parent-cd) (dump parent-cd (+ indent 4)))
484                                (-class-desc-parents cd))
485                      )))
486       (display "Top level class: " cep)
487       (display (-class-name (-class-desc-class top-desc)) cep)
488       (newline cep)
489       (dump class-desc 0)
490       ))
491 )
492 \f
493 ; Low level object utilities.
494
495 ; Make an object.
496 ; All elements get initial (or unbound) values.
497
498 (define (-object-make! class)
499   (-class-check-init! class)
500   (vector (apply vector (append! (list -object-tag class)
501                                  (-class-all-initial-values class)))
502           (-class-class-desc class))
503 )
504
505 ; Make an object using VALUES.
506 ; VALUES must specify all elements in the class (and parent classes).
507
508 (define (-object-make-with-values! class class-desc values)
509   (-class-check-init! class)
510   (vector (apply vector (append! (list -object-tag class) values))
511           class-desc)
512 )
513
514 ; Copy an object.
515 ; If TOP?, the copy is of the top level object with any specialization
516 ; discarded.
517 ; WARNING: A shallow copy is currently done on the elements!
518
519 (define (-object-copy obj top?)
520   (if top?
521       (vector (-object-vector-copy (-object-elements obj))
522               (-class-class-desc (-object-top-class obj)))
523       (vector (-object-vector-copy (-object-elements obj))
524               (-object-class-desc obj)))
525 )
526
527 ; Specialize an object to be one from a parent class.
528 ; The result is the same object, but with a different view (confined to
529 ; a particular parent class).
530
531 (define (-object-specialize obj class-desc)
532   (vector (-object-elements obj) class-desc)
533 )
534
535 ; Accessors.
536
537 (define (-object-elements obj) (vector-ref obj 0))
538 (define (-object-class-desc obj) (vector-ref obj 1))
539 (define (-object-class obj) (-class-desc-class (-object-class-desc obj)))
540 (define (-object-class-name obj) (-class-name (-object-class obj)))
541 (define (-object-top-class obj) (vector-ref (-object-elements obj) 1))
542
543 (define (-object-elm-get obj class-desc elm-base-offset)
544   (vector-ref (-object-elements obj)
545               (+ (-class-desc-offset-base class-desc) elm-base-offset))
546 )
547
548 (define (-object-elm-set! obj class-desc elm-base-offset new-val)
549   (vector-set! (-object-elements obj)
550                (+ (-class-desc-offset-base class-desc) elm-base-offset)
551                new-val)
552   -object-unspecified
553 )
554
555 ; Return a boolean indicating of OBJ has multiple-inheritance.
556
557 (define (-object-mi? obj)
558   (-class-mi? (-object-top-class obj))
559 )
560
561 ; Return boolean indicating if X is an object.
562
563 (define (object? obj)
564   (and (vector? obj)
565        (= (vector-length obj) 2)
566        (vector? (vector-ref obj 0))
567        (eq? -object-tag (vector-ref (vector-ref obj 0) 0))
568        (-class-desc? (vector-ref obj 1)))
569 )
570
571 ; Return the class of an object.
572
573 (define (object-class obj)
574   (-object-check obj "object-class")
575   (-object-class obj)
576 )
577
578 ; Cover proc of -object-class-name for the outside world to use.
579 ; The result is the name of the class or #f if OBJ is not an object.
580
581 (define (object-class-name obj)
582   (if (object? obj)
583       (-object-class-name obj)
584       #f)
585 )
586 \f
587 ; Class operations.
588
589 ; Return the list of initial values for CLASS.
590 ; The result does not include parent classes.
591
592 (define (-class-my-initial-values class)
593   (map cadr (-class-elements class))
594 )
595
596 ; Initialize class if not already done.
597 ; FIXME: Need circularity check.  Later.
598
599 (define (-class-check-init! class)
600   ; This should be fast the second time through, so don't do any
601   ; computation until we know it's necessary.
602
603   (if (not (-class-all-initial-values class))
604
605       (begin
606
607         ; First pass ensures all parents are initialized.
608         (for-each -class-check-init!
609                   (-class-parent-classes class))
610
611         ; Next pass initializes the initial value list.
612         (letrec ((get-inits
613                   (lambda (class)
614                     (let ((parents (-class-parent-classes class)))
615                       (append (apply append (map get-inits parents))
616                               (-class-my-initial-values class))))))
617
618           (let* ((parents (-class-parent-classes class))
619                  (inits (append (apply append (map get-inits parents))
620                                 (-class-my-initial-values class))))
621             (-class-set-all-initial-values! class inits)))
622
623         ; Next pass initializes the class's class-descriptor.
624         ; Object elements begin at offset 2 in the element vector.
625         (-class-set-class-desc! class
626                                 (-class-compute-class-desc class 2 #f))
627         ))
628
629   -object-unspecified
630 )
631
632 ; Make a class.
633 ;
634 ; PARENTS is a list of names of parent classes.  The parents need not
635 ; exist yet, though they must exist when the class is first instantiated.
636 ; ELMS is a either a list of either element names or name/value pairs.
637 ; Elements without initial values are marked as "unbound".
638 ; METHODS is an initial alist of methods.  More methods can be added with
639 ; method-make!.
640
641 (define (class-make name parents elms methods)
642   (let ((elm-list #f))
643
644     ; Mark elements without initial values as unbound, and
645     ; compute indices into the element vector (relative to the class's
646     ; offset).
647     ; Elements are recorded as (symbol initial-value private? . vector-index)
648     ; FIXME: For now all elements are marked as "public".
649     (let loop ((elm-list-tmp '()) (index 0) (elms elms))
650       (if (null? elms)
651           (set! elm-list (reverse! elm-list-tmp)) ; done
652           (if (pair? (car elms))
653               (loop (acons (caar elms)
654                            (cons (cdar elms) (cons #f index))
655                            elm-list-tmp)
656                     (+ index 1)
657                     (cdr elms))
658               (loop (acons (car elms)
659                            (cons -object-unbound (cons #f index))
660                            elm-list-tmp)
661                     (+ index 1)
662                     (cdr elms)))))
663
664     (let ((result (-class-make! name parents elm-list methods)))
665
666       ; Create the standard `make!' method.
667       ; The caller can override afterwards if desired.
668       ; Note that if there are any parent classes then we don't know the names
669       ; of all of the elements yet, that is only known after the class has been
670       ; initialized which only happens when the class is first instantiated.
671       ; This method won't be called until that happens though so we're safe.
672       ; This is written without knowledge of the names, it just initializes
673       ; all elements.
674       (method-make! result 'make!
675                     (lambda args
676                       (let ((self (car args)))
677                         ; Ensure exactly all of the elements are provided.
678                         (if (not (= (length args)
679                                     (- (vector-length (-object-elements self)) 1)))
680                             (-object-error "make!" "" "wrong number of arguments to method `make!'"))
681                         (-object-make-with-values! (-object-top-class self)
682                                                    (-object-class-desc self)
683                                                    (cdr args)))))
684
685       result))
686 )
687
688 ; Create an object of a class CLASS.
689
690 (define (new class)
691   (-class-check class "new")
692
693   (if -object-verbose?
694       (display (string-append "Instantiating class " (-class-name class) ".\n")
695                (current-error-port)))
696
697   (-object-make! class)
698 )
699
700 ; Make a copy of OBJ.
701 ; WARNING: A shallow copy is done on the elements!
702
703 (define (object-copy obj)
704   (-object-check obj "object-copy")
705   (-object-copy obj #f)
706 )
707
708 ; Make a copy of OBJ.
709 ; This makes a copy of top level object, with any specialization discarded.
710 ; WARNING: A shallow copy is done on the elements!
711
712 (define (object-copy-top obj)
713   (-object-check obj "object-copy-top")
714   (-object-copy obj #t)
715 )
716
717 ; Utility to define a standard `make!' method.
718 ; A standard make! method is one in which all it does is initialize
719 ; fields from args.
720
721 (define (method-make-make! class args)
722   (let ((lambda-expr
723          (append (list 'lambda (cons 'self args))
724                  (map (lambda (elm) (list 'elm-set! 'self
725                                           (list 'quote elm) elm))
726                       args)
727                  '(self))))
728     (method-make! class 'make! (eval1 lambda-expr))
729     )
730 )
731
732 ; The "standard" way to invoke `make!' is (send (new class) 'make! ...).
733 ; This puts all that in a cover function.
734
735 (define (make class . operands)
736   (apply send (append (cons (new class) '()) '(make!) operands))
737 )
738
739 ; Return #t if class X is a subclass of BASE-NAME.
740
741 (define (-class-subclass? base-name x)
742   (if (eq? base-name (-class-name x))
743       #t
744       (let loop ((parents (-class-parents x)))
745         (if (null? parents)
746             #f
747             (if (-class-subclass? base-name (class-lookup (car parents)))
748                 #t
749                 (loop (cdr parents))))))
750 )
751
752 ; Return #t if OBJECT is an instance of CLASS.
753 ; This does not signal an error if OBJECT is not an object as this is
754 ; intended to be used in class predicates.
755
756 (define (class-instance? class object)
757   (-class-check class "class-instance?")
758   (if (object? object)
759       (-class-subclass? (-class-name class) (-object-class object))
760       #f)
761 )
762 \f
763 ; Element operations.
764
765 ; Lookup an element in a class-desc.
766 ; The result is (class-desc . (private? . elm-offset)) or #f if not found.
767 ; ??? We could define accessors of the result but knowledge of its format
768 ; is restricted to this section of the source.
769
770 (define (-class-lookup-element class-desc elm-name)
771   (let* ((class (-class-desc-class class-desc))
772          (elm (assq elm-name (-class-elements class))))
773     (if elm
774         (cons class-desc (cddr elm))
775         (let loop ((parents (-class-desc-parents class-desc)))
776           (if (null? parents)
777               #f
778               (let ((elm (-class-lookup-element (car parents) elm-name)))
779                 (if elm
780                     elm
781                     (loop (cdr parents)))))
782           ))
783     )
784 )
785
786 ; Given the result of -class-lookup-element, return the element's delta
787 ; from base-offset.
788
789 (define (-elm-delta index)
790   (+ (-class-desc-offset-delta (car index))
791      (cddr index))
792 )
793
794 ; Return a boolean indicating if ELM is bound in OBJ.
795
796 (define (elm-bound? obj elm)
797   (-object-check obj "elm-bound?")
798   (let* ((index (-class-lookup-element (-object-class-desc obj) elm))
799          (val (-object-elm-get obj (car index) (-elm-delta index))))
800     (not (eq? val -object-unbound)))
801 )
802
803 ; Subroutine of elm-get.
804
805 (define (-elm-make-method-getter self name)
806   (-object-check self "elm-get")
807   (let ((index (-class-lookup-element (-object-class-desc self) name)))
808     (if index
809         (procedure->memoizing-macro
810          (lambda (exp env)
811            `(lambda (obj)
812               (-object-elm-get obj (-object-class-desc obj)
813                                ,(-elm-delta index)))))
814         (-object-error "elm-get" self "element not present: " name)))
815 )
816
817 ; Get an element from an object.
818 ; If OBJ is `self' then the caller is required to be a method and we emit
819 ; memoized code.  Otherwise we do things the slow way.
820 ; ??? There must be a better way.
821 ; What this does is turn
822 ; (elm-get self 'foo)
823 ; into
824 ; ((-elm-make-method-get self 'foo) self)
825 ; Note the extra set of parens.  -elm-make-method-get then does the lookup of
826 ; foo and returns a memoizing macro that returns the code to perform the
827 ; operation with O(1).  Cute, but I'm hoping there's an easier/better way.
828
829 (defmacro elm-get (self name)
830   (if (eq? self 'self)
831       `(((-elm-make-method-getter ,self ,name)) ,self)
832       `(elm-xget ,self ,name))
833 )
834
835 ; Subroutine of elm-set!.
836
837 (define (-elm-make-method-setter self name)
838   (-object-check self "elm-set!")
839   (let ((index (-class-lookup-element (-object-class-desc self) name)))
840     (if index
841         (procedure->memoizing-macro
842          (lambda (exp env)
843            `(lambda (obj new-val)
844               (-object-elm-set! obj (-object-class-desc obj)
845                                 ,(-elm-delta index) new-val))))
846         (-object-error "elm-set!" self "element not present: " name)))
847 )
848
849 ; Set an element in an object.
850 ; This can only be used by methods.
851 ; See the comments for `elm-get'!
852
853 (defmacro elm-set! (self name new-val)
854   (if (eq? self 'self)
855       `(((-elm-make-method-setter ,self ,name)) ,self ,new-val)
856       `(elm-xset! ,self ,name ,new-val))
857 )
858
859 ; Get an element from an object.
860 ; This is for invoking from outside a method, and without having to
861 ; use elm-make-getter.  It should be used sparingly.
862
863 (define (elm-xget obj name)
864   (-object-check obj "elm-xget")
865   (let ((index (-class-lookup-element (-object-class-desc obj) name)))
866     ; FIXME: check private?
867     (if index
868         (-object-elm-get obj (car index) (-elm-delta index))
869         (-object-error "elm-xget" obj "element not present: " name)))
870 )
871
872 ; Set an element in an object.
873 ; This is for invoking from outside a method, and without having to
874 ; use elm-make-setter.  It should be used sparingly.
875
876 (define (elm-xset! obj name new-val)
877   (-object-check obj "elm-xset!")
878   (let ((index (-class-lookup-element (-object-class-desc obj) name)))
879     ; FIXME: check private?
880     (if index
881         (-object-elm-set! obj (car index) (-elm-delta index) new-val)
882         (-object-error "elm-xset!" obj "element not present: " name)))
883 )
884
885 ; Return a boolean indicating if object OBJ has element NAME.
886
887 (define (elm-present? obj name)
888   (-object-check obj "elm-present?")
889   (->bool (-class-lookup-element (-object-class-desc obj) name))
890 )
891
892 ; Return lambda to get element NAME in CLASS.
893 ; FIXME: validate name.
894
895 (define (elm-make-getter class name)
896   (-class-check class "elm-make-getter")
897   ; We use delay here as we can't assume parent classes have been
898   ; initialized yet.
899   (let ((fast-index (delay (-class-lookup-element
900                             (-class-class-desc class) name))))
901     (lambda (obj)
902       ; ??? Should be able to use fast-index in mi case.
903       ; ??? Need to involve CLASS in lookup.
904       (let ((index (if (-object-mi? obj)
905                        (-class-lookup-element (-object-class-desc obj) name)
906                        (force fast-index))))
907       (-object-elm-get obj (car index) (-elm-delta index)))))
908 )
909
910 ; Return lambda to set element NAME in CLASS.
911 ; FIXME: validate name.
912
913 (define (elm-make-setter class name)
914   (-class-check class "elm-make-setter")
915   ; We use delay here as we can't assume parent classes have been
916   ; initialized yet.
917   (let ((fast-index (delay (-class-lookup-element
918                             (-class-class-desc class) name))))
919     (lambda (obj newval)
920       ; ??? Should be able to use fast-index in mi case.
921       ; ??? Need to involve CLASS in lookup.
922       (let ((index (if (-object-mi? obj)
923                        (-class-lookup-element (-object-class-desc obj) name)
924                        (force fast-index))))
925         (-object-elm-set! obj (car index) (-elm-delta index) newval))))
926 )
927
928 ; Return a list of all elements in OBJ.
929
930 (define (elm-list obj)
931   (cddr (vector->list (-object-elements obj)))
932 )
933 \f
934 ; Method operations.
935
936 ; Lookup the next method in a class.
937 ; This means begin the search in the parents.
938 ; ??? What should this do for virtual methods.  At present we treat them as
939 ; non-virtual.
940
941 (define (-method-lookup-next class-desc method-name)
942   (let loop ((parents (-class-desc-parents class-desc)))
943     (if (null? parents)
944         #f
945         (let ((meth (-method-lookup (car parents) method-name #f)))
946           (if meth
947               meth
948               (loop (cdr parents))))))
949 )
950
951 ; Lookup a method in a class.
952 ; The result is (class-desc . method).  If the method is found in a parent
953 ; class, the associated parent class descriptor is returned.  If the method is
954 ; a virtual method, the appropriate subclass's class descriptor is returned.
955 ; VIRTUAL? is #t if virtual methods are to be treated as such.
956 ; Otherwise they're treated as normal methods.
957 ;
958 ; FIXME: We don't yet implement the method cache.
959
960 (define (-method-lookup class-desc method-name virtual?)
961   (if -object-verbose?
962       (display (string-append "Looking up method " method-name " in "
963                               (-class-name (-class-desc-class class-desc)) ".\n")
964                (current-error-port)))
965
966   (let ((meth (assq method-name (-class-methods (-class-desc-class class-desc)))))
967     (if meth
968         (if (and virtual? (cadr meth)) ; virtual?
969             ; Traverse back up the inheritance chain looking for overriding
970             ; methods.  The closest one to the top is the one to use.
971             (let loop ((child (-class-desc-child class-desc))
972                        (goal-class-desc class-desc)
973                        (goal-meth meth))
974               (if child
975                   (begin
976                     (if -object-verbose?
977                         (display (string-append "Looking up virtual method "
978                                                 method-name " in "
979                                                 (-class-name (-class-desc-class child))
980                                                 ".\n")
981                                  (current-error-port)))
982                     (let ((meth (assq method-name (-class-methods (-class-desc-class child)))))
983                       (if meth
984                           ; Method found, update goal object and method.
985                           (loop (-class-desc-child child) child meth)
986                           ; Method not found at this level.
987                           (loop (-class-desc-child child) goal-class-desc goal-meth))))
988                   ; Went all the way up to the top.
989                   (cons goal-class-desc (cddr goal-meth))))
990             ; Non-virtual, done.
991             (cons class-desc (cddr meth)))
992         ; Method not found, search parents.
993         (-method-lookup-next class-desc method-name)))
994 )
995
996 ; Return a boolean indicating if object OBJ has method NAME.
997
998 (define (method-present? obj name)
999   (-object-check obj "method-present?")
1000   (->bool (-method-lookup (-object-class-desc obj) name #f))
1001 )
1002
1003 ; Return method NAME of CLASS or #f if not present.
1004 ; ??? Assumes CLASS has been initialized.
1005
1006 (define (method-proc class name)
1007   (-class-check class "method-proc")
1008   (let ((meth (-method-lookup (-class-class-desc class) name #t)))
1009     (if meth
1010         (cdr meth)
1011         #f))
1012 )
1013
1014 ; Add a method to a class.
1015 ; FIXME: ensure method-name is a symbol
1016
1017 (define (method-make! class method-name method)
1018   (-class-check class "method-make!")
1019   (if (not (procedure? method))
1020       (-object-error "method-make!" method "method must be a procedure"))
1021   (-class-set-methods! class (acons method-name
1022                                     (cons #f method)
1023                                     (-class-methods class)))
1024   -object-unspecified
1025 )
1026
1027 ; Add a virtual method to a class.
1028 ; FIXME: ensure method-name is a symbol
1029
1030 (define (method-make-virtual! class method-name method)
1031   (-class-check class "method-make-virtual!")
1032   (if (not (procedure? method))
1033       (-object-error "method-make-virtual!" method "method must be a procedure"))
1034   (-class-set-methods! class (acons method-name
1035                                     (cons #t method)
1036                                     (-class-methods class)))
1037   -object-unspecified
1038 )
1039
1040 ; Utility to create "forwarding" methods.
1041 ; METHODS are forwarded to class member ELM-NAME, assumed to be an object.
1042 ; The created methods take a variable number of arguments.
1043 ; Argument length checking will be done by the receiving method.
1044 ; FIXME: ensure elm-name is a symbol
1045
1046 (define (method-make-forward! class elm-name methods)
1047   (for-each (lambda (method-name)
1048               (method-make!
1049                class method-name
1050                (eval1 `(lambda args
1051                          (apply send
1052                                 (cons (elm-get (car args)
1053                                                (quote ,elm-name))
1054                                       (cons (quote ,method-name)
1055                                             (cdr args))))))))
1056             methods)
1057   -object-unspecified
1058 )
1059
1060 ; Same as method-make-forward! but creates virtual methods.
1061 ; FIXME: ensure elm-name is a symbol
1062
1063 (define (method-make-virtual-forward! class elm-name methods)
1064   (for-each (lambda (method-name)
1065               (method-make-virtual!
1066                class method-name
1067                (eval1 `(lambda args
1068                          (apply send
1069                                 (cons (elm-get (car args)
1070                                                (quote ,elm-name))
1071                                       (cons (quote ,method-name)
1072                                             (cdr args))))))))
1073             methods)
1074   -object-unspecified
1075 )
1076
1077 ; Utility of send, send-next.
1078
1079 (define (-object-method-notify obj method-name maybe-next)
1080   (set! -object-verbose? #f)
1081   (display (string-append "Sending " maybe-next method-name " to"
1082                           (if (method-present? obj 'get-name)
1083                               (let ((name (send obj 'get-name)))
1084                                 (if (or (symbol? name) (string? name))
1085                                     (string-append " object " name)
1086                                     ""))
1087                               "")
1088                           " class " (object-class-name obj) ".\n")
1089            (current-error-port))
1090   (set! -object-verbose? #t)
1091 )
1092
1093 ; Invoke a method in an object.
1094 ; When the method is invoked, the (possible parent class) object in which the
1095 ; method is found is passed to the method.
1096 ; ??? The word `send' comes from "sending messages".  Perhaps should pick
1097 ; a better name for this operation.
1098
1099 (define (send obj method-name . args)
1100   (-object-check obj "send")
1101   (-object-check-name method-name "send" "not a method name")
1102   (if -object-verbose? (-object-method-notify obj method-name ""))
1103
1104   (let ((class-desc.meth (-method-lookup (-object-class-desc obj)
1105                                          method-name #t)))
1106     (if class-desc.meth
1107         (apply (cdr class-desc.meth)
1108                (cons (-object-specialize obj (car class-desc.meth))
1109                      args))
1110         (-object-error "send" obj "method not supported: " method-name)))
1111 )
1112
1113 ; Invoke the next method named METHOD-NAME in the heirarchy of OBJ.
1114 ; i.e. the method that would have been invoked if the calling method
1115 ; didn't exist.
1116 ; This may only be called by a method.
1117 ; ??? Ideally we shouldn't need the METHOD-NAME argument.  It could be
1118 ; removed with a bit of effort, but is it worth it?
1119
1120 (define (send-next obj method-name . args)
1121   (-object-check obj "send-next")
1122   (-object-check-name method-name "send-next" "not a method name")
1123   (if -object-verbose? (-object-method-notify obj method-name "next "))
1124
1125   (let ((class-desc.meth (-method-lookup-next (-object-class-desc obj)
1126                                               method-name)))
1127     (if class-desc.meth
1128         (apply (cdr class-desc.meth)
1129                (cons (-object-specialize obj (car class-desc.meth))
1130                      args))
1131         (-object-error "send-next" obj "method not supported: " method-name)))
1132 )
1133 \f
1134 ; Parent operations.
1135
1136 ; Subroutine of `parent' to lookup a (potentially nested) parent class.
1137 ; The result is the parent's class-descriptor or #f if not found.
1138
1139 (define (-class-parent class-desc parent)
1140   (let* ((parent-descs (-class-desc-parents class-desc))
1141          (desc (-class-desc-lookup-parent parent parent-descs)))
1142     (if desc
1143         desc
1144         (let loop ((parents parent-descs))
1145           (if (null? parents)
1146               #f
1147               (let ((desc (-class-parent (car parents) parent)))
1148                 (if desc
1149                     desc
1150                     (loop (cdr parents))))))))
1151 )
1152
1153 ; Subroutine of `parent' to lookup a parent via a path.
1154 ; PARENT-PATH, a list, is the exact path to the parent class.
1155 ; The result is the parent's class-descriptor or #f if not found.
1156 ; For completeness' sake, if PARENT-PATH is empty, CLASS-DESC is returned.
1157
1158 (define (-class-parent-via-path class-desc parent-path)
1159   (if (null? parent-path)
1160       class-desc
1161       (let ((desc (-class-desc-lookup-parent (car parent-path)
1162                                              (-class-desc-parents class-desc))))
1163         (if desc
1164             (if (null? (cdr parent-path))
1165                 desc
1166                 (-class-parent-via-path (car desc) (cdr parent-path)))
1167             #f)))
1168 )
1169
1170 ; Lookup a parent class of object OBJ.
1171 ; CLASS is either a class or a list of classes.
1172 ; If CLASS is a list, it is a (possibly empty) "path" to the parent.
1173 ; Otherwise it is any parent and is searched for breadth-first.
1174 ; ??? Methinks this should be depth-first.
1175 ; The result is OBJ, specialized to the found parent.
1176
1177 (define (object-parent obj class)
1178   (-object-check obj "object-parent")
1179   (cond ((class? class) #t)
1180         ((list? class) (for-each (lambda (class) (-class-check class
1181                                                                "object-parent"))
1182                                  class))
1183         (else (-object-error "object-parent" class "invalid parent path")))
1184                 
1185   ; Hobbit generates C code that passes the function
1186   ; -class-parent-via-path or -class-parent, not the appropriate
1187   ; SCM object.
1188 ; (let ((result ((if (or (null? class) (pair? class))
1189 ;                    -class-parent-via-path
1190 ;                    -class-parent)
1191 ;                  obj class)))
1192   ; So it's rewritten like this.
1193   (let ((result (if (class? class)
1194                     (-class-parent (-object-class-desc obj) class)
1195                     (-class-parent-via-path (-object-class-desc obj) class))))
1196     (if result
1197         (-object-specialize obj result)
1198         (-object-error "object-parent" obj "parent not present")))
1199   ; FIXME: should print path in error message.
1200 )
1201
1202 ; Make PARENT-NAME a parent of CLASS, cons'd unto the front of the search
1203 ; order.  This is used to add a parent class to a class after it has already
1204 ; been created.  Obviously this isn't something one does willy-nilly.
1205 ; The parent is added to the front of the current parent list (affects
1206 ; method lookup).
1207
1208 (define (class-cons-parent! class parent-name)
1209   (-class-check class "class-cons-parent!")
1210   (-object-check-name parent-name "class-cons-parent!" "not a class name")
1211   (-class-set-parents! class (cons parent-name (-class-parents class)))
1212   -object-unspecified
1213 )
1214
1215 ; Make PARENT-NAME a parent of CLASS, cons'd unto the end of the search order.
1216 ; This is used to add a parent class to a class after it has already been
1217 ; created.  Obviously this isn't something one does willy-nilly.
1218 ; The parent is added to the end of the current parent list (affects
1219 ; method lookup).
1220
1221 (define (class-append-parent! class parent-name)
1222   (-class-check class "class-append-parent!")
1223   (-object-check-name parent-name "class-append-parent!" "not a class name")
1224   (-class-set-parents! obj (append (-class-parents obj) (list parent-name)))
1225   -object-unspecified
1226 )
1227 \f
1228 ; Miscellaneous publically accessible utilities.
1229
1230 ; Reset the object system (delete all classes).
1231
1232 (define (object-reset!)
1233   (set! -class-list '())
1234   -object-unspecified
1235 )
1236
1237 ; Call once to initialize the object system.
1238 ; Only necessary if classes have been modified after objects have been
1239 ; instantiated.  This usually happens during development only.
1240
1241 (define (object-init!)
1242   (for-each (lambda (class)
1243               (-class-set-all-initial-values! class #f)
1244               (-class-set-all-methods! class #f)
1245               (-class-set-class-desc! class #f))
1246             (class-list))
1247   (for-each (lambda (class)
1248               (-class-check-init! class))
1249             (class-list))
1250   -object-unspecified
1251 )
1252
1253 ; Return list of all classes.
1254
1255 (define (class-list) (map cdr -class-list))
1256
1257 ; Utility to map over a class and all its parent classes, recursively.
1258
1259 (define (class-map-over-class proc class)
1260   (cons (proc class)
1261         (map (lambda (class) (class-map-over-class proc class))
1262              (-class-parent-classes class)))
1263 )
1264
1265 ; Return class tree of a class or object.
1266
1267 (define (class-tree class-or-object)
1268   (cond ((class? class-or-object)
1269          (class-map-over-class class-name class-or-object))
1270         ((object? class-or-object)
1271          (class-map-over-class class-name (-object-class class-or-object)))
1272         (else (-object-error "class-tree" class-or-object
1273                              "not a class or object")))
1274 )
1275
1276 ; Return names of each alist.
1277
1278 (define (-class-alist-names class)
1279   (list (-class-name class)
1280         (map car (-class-elements class))
1281         (map car (-class-methods class)))
1282 )
1283
1284 ; Return complete layout of class-or-object.
1285
1286 (define (class-layout class-or-object)
1287   (cond ((class? class-or-object)
1288          (class-map-over-class -class-alist-names class-or-object))
1289         ((object? class-or-object)
1290          (class-map-over-class -class-alist-names (-object-class class-or-object)))
1291         (else (-object-error "class-layout" class-or-object
1292                              "not a class or object")))
1293 )
1294
1295 ; Like assq but based on the `name' element.
1296 ; WARNING: Slow.
1297
1298 (define (object-assq name obj-list)
1299   (find-first (lambda (o) (eq? (elm-xget o 'name) name))
1300               obj-list)
1301 )
1302
1303 ; Like memq but based on the `name' element.
1304 ; WARNING: Slow.
1305
1306 (define (object-memq name obj-list)
1307   (let loop ((r obj-list))
1308     (cond ((null? r) #f)
1309           ((eq? name (elm-xget (car r) 'name)) r)
1310           (else (loop (cdr r)))))
1311 )
1312 \f
1313 ; Misc. internal utilities.
1314
1315 ; We need a fast vector copy operation.
1316 ; If `vector-copy' doesn't exist (which is assumed to be the fast one),
1317 ; provide a simple version.
1318 ; FIXME: Need deep copier instead.
1319
1320 (if (defined? 'vector-copy)
1321     (define -object-vector-copy vector-copy)
1322     (define (-object-vector-copy v) (list->vector (vector->list v)))
1323 )
1324 \f
1325 ; Profiling support
1326
1327 (if (and #f (defined? 'proc-profile))
1328     (begin
1329       (proc-profile elm-get)
1330       (proc-profile elm-xset!)
1331       (proc-profile elm-present?)
1332       (proc-profile -method-lookup)
1333       (proc-profile send)
1334       (proc-profile new)
1335       (proc-profile make)
1336       ))