9114e4640d6314eb410f88739db90b3e76447c28
[bpt/guile.git] / module / oop / goops.scm
1 ;;; installed-scm-file
2
3 ;;;; Copyright (C) 1998,1999,2000-2003,2006,2009-2011,2013-2015 Free Software Foundation, Inc.
4 ;;;; Copyright (C) 1993-1998 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
5 ;;;;
6 ;;;; This library is free software; you can redistribute it and/or
7 ;;;; modify it under the terms of the GNU Lesser General Public
8 ;;;; License as published by the Free Software Foundation; either
9 ;;;; version 3 of the License, or (at your option) any later version.
10 ;;;;
11 ;;;; This library is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;;;; Lesser General Public License for more details.
15 ;;;;
16 ;;;; You should have received a copy of the GNU Lesser General Public
17 ;;;; License along with this library; if not, write to the Free Software
18 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 ;;;;
20 \f
21
22 ;;;;
23 ;;;; This file was based upon stklos.stk from the STk distribution
24 ;;;; version 4.0.1 by Erick Gallesio <eg@unice.fr>.
25 ;;;;
26
27 (define-module (oop goops)
28 #:use-module (srfi srfi-1)
29 #:use-module (ice-9 match)
30 #:use-module (oop goops util)
31 #:use-module (system base target)
32 #:export-syntax (define-class class standard-define-class
33 define-generic define-accessor define-method
34 define-extended-generic define-extended-generics
35 method)
36 #:export ( ;; The root of everything.
37 <top>
38 <class> <object>
39
40 ;; Slot types.
41 <foreign-slot> <protected-slot> <hidden-slot> <opaque-slot>
42 <read-only-slot> <self-slot> <protected-opaque-slot>
43 <protected-hidden-slot> <protected-read-only-slot>
44 <scm-slot> <int-slot> <float-slot> <double-slot>
45
46 ;; Methods are implementations of generic functions.
47 <method> <accessor-method>
48
49 ;; Applicable objects, either procedures or applicable structs.
50 <procedure-class> <applicable>
51 <procedure> <primitive-generic>
52
53 ;; Applicable structs.
54 <applicable-struct-class> <applicable-struct-with-setter-class>
55 <applicable-struct> <applicable-struct-with-setter>
56 <generic> <extended-generic>
57 <generic-with-setter> <extended-generic-with-setter>
58 <accessor> <extended-accessor>
59
60 ;; Types with their own allocated typecodes.
61 <boolean> <char> <list> <pair> <null> <string> <symbol>
62 <vector> <bytevector> <uvec> <foreign> <hashtable>
63 <fluid> <dynamic-state> <frame> <vm> <vm-continuation>
64 <keyword>
65
66 ;; Numbers.
67 <number> <complex> <real> <integer> <fraction>
68
69 ;; Unknown.
70 <unknown>
71
72 ;; Particular SMOB data types. All SMOB types have
73 ;; corresponding classes, which may be obtained via class-of,
74 ;; once you have an instance. Perhaps FIXME to provide a
75 ;; smob-type-name->class procedure.
76 <arbiter> <promise> <thread> <mutex> <condition-variable>
77 <regexp> <hook> <bitvector> <random-state> <async>
78 <directory> <array> <character-set>
79 <dynamic-object> <guardian> <macro>
80
81 ;; Modules.
82 <module>
83
84 ;; Ports.
85 <port> <input-port> <output-port> <input-output-port>
86
87 ;; Like SMOB types, all port types have their own classes,
88 ;; which can be accessed via `class-of' once you have an
89 ;; instance. Here we export bindings just for file ports.
90 <file-port>
91 <file-input-port> <file-output-port> <file-input-output-port>
92
93 is-a? class-of
94 ensure-metaclass ensure-metaclass-with-supers
95 make-class
96 make-generic ensure-generic
97 make-extended-generic
98 make-accessor ensure-accessor
99 add-method!
100 class-slot-ref class-slot-set! slot-unbound slot-missing
101 slot-definition-name slot-definition-options
102 slot-definition-allocation
103
104 slot-definition-getter slot-definition-setter
105 slot-definition-accessor
106 slot-definition-init-value slot-definition-init-form
107 slot-definition-init-thunk slot-definition-init-keyword
108 slot-init-function class-slot-definition
109 method-source
110 compute-cpl compute-std-cpl compute-get-n-set compute-slots
111 compute-getter-method compute-setter-method
112 allocate-instance initialize make-instance make
113 no-next-method no-applicable-method no-method
114 change-class update-instance-for-different-class
115 shallow-clone deep-clone
116 class-redefinition
117 apply-generic apply-method apply-methods
118 compute-applicable-methods %compute-applicable-methods
119 method-more-specific? sort-applicable-methods
120 class-subclasses class-methods
121 goops-error
122 min-fixnum max-fixnum
123
124 ;;; *fixme* Should go into goops.c
125 instance? slot-ref-using-class
126 slot-set-using-class! slot-bound-using-class?
127 slot-exists-using-class? slot-ref slot-set! slot-bound?
128 class-name class-direct-supers class-direct-subclasses
129 class-direct-methods class-direct-slots class-precedence-list
130 class-slots
131 generic-function-name
132 generic-function-methods method-generic-function
133 method-specializers method-formals
134 primitive-generic-generic enable-primitive-generic!
135 method-procedure accessor-method-slot-definition
136 slot-exists? make find-method get-keyword)
137 #:no-backtrace)
138
139 ;; XXX FIXME: figure out why the 'eval-when's in this file must use
140 ;; 'compile' and must avoid 'expand', but only in 2.2, and only when
141 ;; compiling something that imports goops, e.g. (ice-9 occam-channel),
142 ;; before (oop goops) itself has been compiled.
143
144 ;; First initialize the builtin part of GOOPS
145 (eval-when (compile load eval)
146 (load-extension (string-append "libguile-" (effective-version))
147 "scm_init_goops_builtins"))
148
149 (eval-when (compile load eval)
150 (use-modules ((language tree-il primitives) :select (add-interesting-primitive!)))
151 (add-interesting-primitive! 'class-of))
152
153 (define-syntax macro-fold-left
154 (syntax-rules ()
155 ((_ folder seed ()) seed)
156 ((_ folder seed (head . tail))
157 (macro-fold-left folder (folder head seed) tail))))
158
159 (define-syntax macro-fold-right
160 (syntax-rules ()
161 ((_ folder seed ()) seed)
162 ((_ folder seed (head . tail))
163 (folder head (macro-fold-right folder seed tail)))))
164
165 (define-syntax fold-<class>-slots
166 (lambda (x)
167 (define slots
168 '((layout <protected-read-only-slot>)
169 (flags <hidden-slot>)
170 (self <self-slot>)
171 (instance-finalizer <hidden-slot>)
172 (print)
173 (name <protected-hidden-slot>)
174 (reserved-0 <hidden-slot>)
175 (reserved-1 <hidden-slot>)
176 (redefined)
177 (direct-supers)
178 (direct-slots)
179 (direct-subclasses)
180 (direct-methods)
181 (cpl)
182 (slots)
183 (getters-n-setters)
184 (nfields)))
185 (syntax-case x ()
186 ((_ fold visit seed)
187 ;; The datum->syntax makes it as if the identifiers in `slots'
188 ;; were present in the initial form, which allows them to be used
189 ;; as (components of) introduced identifiers.
190 #`(fold visit seed #,(datum->syntax #'visit slots))))))
191
192 ;; Define class-index-layout to 0, class-index-flags to 1, and so on.
193 (let-syntax ((define-class-index
194 (lambda (x)
195 (define (id-append ctx a b)
196 (datum->syntax ctx (symbol-append (syntax->datum a)
197 (syntax->datum b))))
198 (define (tail-length tail)
199 (syntax-case tail ()
200 ((begin) 0)
201 ((visit head tail) (1+ (tail-length #'tail)))))
202 (syntax-case x ()
203 ((_ (name . _) tail)
204 #`(begin
205 (define #,(id-append #'name #'class-index- #'name)
206 #,(tail-length #'tail))
207 tail))))))
208 (fold-<class>-slots macro-fold-left define-class-index (begin)))
209
210 (define-syntax-rule (define-class-accessor name docstring field)
211 (define (name obj)
212 docstring
213 (let ((val obj))
214 (unless (class? val)
215 (scm-error 'wrong-type-arg #f "Not a class: ~S"
216 (list val) #f))
217 (struct-ref val field))))
218
219 (define-class-accessor class-name
220 "Return the class name of @var{obj}."
221 class-index-name)
222 (define-class-accessor class-direct-supers
223 "Return the direct superclasses of the class @var{obj}."
224 class-index-direct-supers)
225 (define-class-accessor class-direct-slots
226 "Return the direct slots of the class @var{obj}."
227 class-index-direct-slots)
228 (define-class-accessor class-direct-subclasses
229 "Return the direct subclasses of the class @var{obj}."
230 class-index-direct-subclasses)
231 (define-class-accessor class-direct-methods
232 "Return the direct methods of the class @var{obj}."
233 class-index-direct-methods)
234 (define-class-accessor class-precedence-list
235 "Return the class precedence list of the class @var{obj}."
236 class-index-cpl)
237 (define-class-accessor class-slots
238 "Return the slot list of the class @var{obj}."
239 class-index-slots)
240
241 ;;
242 ;; is-a?
243 ;;
244 (define (is-a? obj class)
245 (and (memq class (class-precedence-list (class-of obj))) #t))
246
247
248 ;;; The standard class precedence list computation algorithm
249 ;;;
250 ;;; Correct behaviour:
251 ;;;
252 ;;; (define-class food ())
253 ;;; (define-class fruit (food))
254 ;;; (define-class spice (food))
255 ;;; (define-class apple (fruit))
256 ;;; (define-class cinnamon (spice))
257 ;;; (define-class pie (apple cinnamon))
258 ;;; => cpl (pie) = pie apple fruit cinnamon spice food object top
259 ;;;
260 ;;; (define-class d ())
261 ;;; (define-class e ())
262 ;;; (define-class f ())
263 ;;; (define-class b (d e))
264 ;;; (define-class c (e f))
265 ;;; (define-class a (b c))
266 ;;; => cpl (a) = a b d c e f object top
267 ;;;
268
269 (define (compute-std-cpl c get-direct-supers)
270 (define (only-non-null lst)
271 (filter (lambda (l) (not (null? l))) lst))
272
273 (define (merge-lists reversed-partial-result inputs)
274 (cond
275 ((every null? inputs)
276 (reverse! reversed-partial-result))
277 (else
278 (let* ((candidate (lambda (c)
279 (and (not (any (lambda (l)
280 (memq c (cdr l)))
281 inputs))
282 c)))
283 (candidate-car (lambda (l)
284 (and (not (null? l))
285 (candidate (car l)))))
286 (next (any candidate-car inputs)))
287 (if (not next)
288 (goops-error "merge-lists: Inconsistent precedence graph"))
289 (let ((remove-next (lambda (l)
290 (if (eq? (car l) next)
291 (cdr l)
292 l))))
293 (merge-lists (cons next reversed-partial-result)
294 (only-non-null (map remove-next inputs))))))))
295 (let ((c-direct-supers (get-direct-supers c)))
296 (merge-lists (list c)
297 (only-non-null (append (map class-precedence-list
298 c-direct-supers)
299 (list c-direct-supers))))))
300
301 ;; Bootstrap version.
302 (define (compute-cpl class)
303 (compute-std-cpl class class-direct-supers))
304
305 (define (build-slots-list dslots cpl)
306 (define (check-cpl slots class-slots)
307 (when (or-map (lambda (slot-def) (assq (car slot-def) slots))
308 class-slots)
309 (scm-error 'misc-error #f
310 "a predefined <class> inherited field cannot be redefined"
311 '() '())))
312 (define (remove-duplicate-slots slots)
313 (let lp ((slots (reverse slots)) (res '()) (seen '()))
314 (cond
315 ((null? slots) res)
316 ((memq (caar slots) seen)
317 (lp (cdr slots) res seen))
318 (else
319 (lp (cdr slots) (cons (car slots) res) (cons (caar slots) seen))))))
320 (let* ((class-slots (and (memq <class> cpl)
321 (struct-ref <class> class-index-slots))))
322 (when class-slots
323 (check-cpl dslots class-slots))
324 (let lp ((cpl (cdr cpl)) (res dslots) (class-slots '()))
325 (if (null? cpl)
326 (remove-duplicate-slots (append class-slots res))
327 (let* ((head (car cpl))
328 (cpl (cdr cpl))
329 (new-slots (struct-ref head class-index-direct-slots)))
330 (cond
331 ((not class-slots)
332 (lp cpl (append new-slots res) class-slots))
333 ((eq? head <class>)
334 ;; Move class slots to the head of the list.
335 (lp cpl res new-slots))
336 (else
337 (check-cpl new-slots class-slots)
338 (lp cpl (append new-slots res) class-slots))))))))
339
340 (define (%compute-getters-n-setters slots)
341 (define (compute-init-thunk options)
342 (cond
343 ((kw-arg-ref options #:init-value) => (lambda (val) (lambda () val)))
344 ((kw-arg-ref options #:init-thunk))
345 (else #f)))
346 (let lp ((slots slots) (n 0))
347 (match slots
348 (() '())
349 (((name . options) . slots)
350 (cons (cons name (cons (compute-init-thunk options) n))
351 (lp slots (1+ n)))))))
352
353 (define (%compute-layout slots getters-n-setters nfields is-class?)
354 (define (instance-allocated? g-n-s)
355 (match g-n-s
356 ((name init-thunk . (? exact-integer? index)) #t)
357 ((name init-thunk getter setter index size) #t)
358 (_ #f)))
359
360 (define (allocated-index g-n-s)
361 (match g-n-s
362 ((name init-thunk . (? exact-integer? index)) index)
363 ((name init-thunk getter setter index size) index)))
364
365 (define (allocated-size g-n-s)
366 (match g-n-s
367 ((name init-thunk . (? exact-integer? index)) 1)
368 ((name init-thunk getter setter index size) size)))
369
370 (define (slot-protection-and-kind options)
371 (define (subclass? class parent)
372 (memq parent (class-precedence-list class)))
373 (let ((type (kw-arg-ref options #:class)))
374 (if (and type (subclass? type <foreign-slot>))
375 (values (cond
376 ((subclass? type <self-slot>) #\s)
377 ((subclass? type <protected-slot>) #\p)
378 (else #\u))
379 (cond
380 ((subclass? type <opaque-slot>) #\o)
381 ((subclass? type <read-only-slot>) #\r)
382 ((subclass? type <hidden-slot>) #\h)
383 (else #\w)))
384 (values #\p #\w))))
385
386 (let ((layout (make-string (* nfields 2))))
387 (let lp ((n 0) (slots slots) (getters-n-setters getters-n-setters))
388 (match getters-n-setters
389 (()
390 (unless (= n nfields) (error "bad nfields"))
391 (unless (null? slots) (error "inconsistent g-n-s/slots"))
392 (when is-class?
393 (let ((class-layout (struct-ref <class> class-index-layout)))
394 (unless (string-prefix? (symbol->string class-layout) layout)
395 (error "bad layout for class"))))
396 layout)
397 ((g-n-s . getters-n-setters)
398 (match slots
399 (((name . options) . slots)
400 (cond
401 ((instance-allocated? g-n-s)
402 (unless (< n nfields) (error "bad nfields"))
403 (unless (= n (allocated-index g-n-s)) (error "bad allocation"))
404 (call-with-values (lambda () (slot-protection-and-kind options))
405 (lambda (protection kind)
406 (let init ((n n) (size (allocated-size g-n-s)))
407 (cond
408 ((zero? size) (lp n slots getters-n-setters))
409 (else
410 (string-set! layout (* n 2) protection)
411 (string-set! layout (1+ (* n 2)) kind)
412 (init (1+ n) (1- size))))))))
413 (else
414 (lp n slots getters-n-setters))))))))))
415
416 (define (%prep-layout! class)
417 (let* ((is-class? (and (memq <class> (struct-ref class class-index-cpl)) #t))
418 (layout (%compute-layout
419 (struct-ref class class-index-slots)
420 (struct-ref class class-index-getters-n-setters)
421 (struct-ref class class-index-nfields)
422 is-class?)))
423 (%init-layout! class layout)))
424
425 (define (make-standard-class class name dsupers dslots)
426 (let ((z (make-struct/no-tail class)))
427 (struct-set! z class-index-direct-supers dsupers)
428 (let* ((cpl (compute-cpl z))
429 (dslots (map (lambda (slot)
430 (if (pair? slot) slot (list slot)))
431 dslots))
432 (slots (build-slots-list dslots cpl))
433 (nfields (length slots))
434 (g-n-s (%compute-getters-n-setters slots)))
435 (struct-set! z class-index-name name)
436 (struct-set! z class-index-direct-slots dslots)
437 (struct-set! z class-index-direct-subclasses '())
438 (struct-set! z class-index-direct-methods '())
439 (struct-set! z class-index-cpl cpl)
440 (struct-set! z class-index-slots slots)
441 (struct-set! z class-index-nfields nfields)
442 (struct-set! z class-index-getters-n-setters g-n-s)
443 (struct-set! z class-index-redefined #f)
444 (for-each (lambda (super)
445 (let ((subclasses
446 (struct-ref super class-index-direct-subclasses)))
447 (struct-set! super class-index-direct-subclasses
448 (cons z subclasses))))
449 dsupers)
450 (%prep-layout! z)
451 (%inherit-magic! z dsupers)
452 z)))
453
454 (define <class>
455 (let-syntax ((cons-dslot
456 ;; The specialized slot classes have not been defined
457 ;; yet; initialize <class> with unspecialized slots.
458 (syntax-rules ()
459 ((_ (name) tail) (cons (list 'name) tail))
460 ((_ (name class) tail) (cons (list 'name) tail))))
461 (cons-layout
462 ;; A simple way to compute class layout for the concrete
463 ;; types used in <class>.
464 (syntax-rules (<protected-read-only-slot> <self-slot>
465 <hidden-slot> <protected-hidden-slot>)
466 ((_ (name) tail)
467 (string-append "pw" tail))
468 ((_ (name <protected-read-only-slot>) tail)
469 (string-append "pr" tail))
470 ((_ (name <self-slot>) tail)
471 (string-append "sr" tail))
472 ((_ (name <hidden-slot>) tail)
473 (string-append "uh" tail))
474 ((_ (name <protected-hidden-slot>) tail)
475 (string-append "ph" tail)))))
476 (let* ((dslots (fold-<class>-slots macro-fold-right cons-dslot '()))
477 (layout (fold-<class>-slots macro-fold-right cons-layout ""))
478 (<class> (%make-root-class layout)))
479 ;; The `direct-supers', `direct-slots', `cpl', `slots', and
480 ;; `getters-n-setters' fields will be updated later.
481 (struct-set! <class> class-index-name '<class>)
482 (struct-set! <class> class-index-direct-supers '())
483 (struct-set! <class> class-index-direct-slots dslots)
484 (struct-set! <class> class-index-direct-subclasses '())
485 (struct-set! <class> class-index-direct-methods '())
486 (struct-set! <class> class-index-cpl '())
487 (struct-set! <class> class-index-slots dslots)
488 (struct-set! <class> class-index-nfields (length dslots))
489 (struct-set! <class> class-index-getters-n-setters
490 (%compute-getters-n-setters dslots))
491 (struct-set! <class> class-index-redefined #f)
492 <class>)))
493
494 (define-syntax define-standard-class
495 (syntax-rules ()
496 ((define-standard-class name (super ...) #:metaclass meta slot ...)
497 (define name
498 (make-standard-class meta 'name (list super ...) '(slot ...))))
499 ((define-standard-class name (super ...) slot ...)
500 (define-standard-class name (super ...) #:metaclass <class> slot ...))))
501
502 (define-standard-class <top> ())
503 (define-standard-class <object> (<top>))
504
505 ;; <top>, <object>, and <class> were partially initialized. Correct
506 ;; them here.
507 (struct-set! <object> class-index-direct-subclasses (list <class>))
508 (struct-set! <class> class-index-direct-supers (list <object>))
509 (struct-set! <class> class-index-cpl (list <class> <object> <top>))
510
511 (define-standard-class <foreign-slot> (<top>))
512 (define-standard-class <protected-slot> (<foreign-slot>))
513 (define-standard-class <hidden-slot> (<foreign-slot>))
514 (define-standard-class <opaque-slot> (<foreign-slot>))
515 (define-standard-class <read-only-slot> (<foreign-slot>))
516 (define-standard-class <self-slot> (<read-only-slot>))
517 (define-standard-class <protected-opaque-slot> (<protected-slot>
518 <opaque-slot>))
519 (define-standard-class <protected-hidden-slot> (<protected-slot>
520 <hidden-slot>))
521 (define-standard-class <protected-read-only-slot> (<protected-slot>
522 <read-only-slot>))
523 (define-standard-class <scm-slot> (<protected-slot>))
524 (define-standard-class <int-slot> (<foreign-slot>))
525 (define-standard-class <float-slot> (<foreign-slot>))
526 (define-standard-class <double-slot> (<foreign-slot>))
527
528 ;; Finish initialization of <class> with specialized slots.
529 (let-syntax ((visit
530 (syntax-rules ()
531 ((_ (name) tail)
532 (cons (list 'name) tail))
533 ((_ (name class) tail)
534 (cons (list 'name #:class class) tail)))))
535 (let* ((dslots (fold-<class>-slots macro-fold-right visit '()))
536 (g-n-s (%compute-getters-n-setters dslots)))
537 (struct-set! <class> class-index-direct-slots dslots)
538 (struct-set! <class> class-index-slots dslots)
539 (struct-set! <class> class-index-getters-n-setters g-n-s)))
540
541 ;; Applicables and their classes.
542 (define-standard-class <procedure-class> (<class>))
543 (define-standard-class <applicable-struct-class>
544 (<procedure-class>))
545 (define-standard-class <applicable-struct-with-setter-class>
546 (<applicable-struct-class>))
547 (%bless-applicable-struct-vtables! <applicable-struct-class>
548 <applicable-struct-with-setter-class>)
549
550 (define-standard-class <applicable> (<top>))
551 (define-standard-class <applicable-struct> (<object> <applicable>)
552 #:metaclass <applicable-struct-class>
553 procedure)
554 (define-standard-class <applicable-struct-with-setter> (<applicable-struct>)
555 #:metaclass <applicable-struct-with-setter-class>
556 setter)
557 (define-standard-class <generic> (<applicable-struct>)
558 #:metaclass <applicable-struct-class>
559 methods
560 (n-specialized #:init-value 0)
561 (extended-by #:init-value ())
562 effective-methods)
563 (define-standard-class <extended-generic> (<generic>)
564 #:metaclass <applicable-struct-class>
565 (extends #:init-value ()))
566 (define-standard-class <generic-with-setter> (<generic>
567 <applicable-struct-with-setter>)
568 #:metaclass <applicable-struct-with-setter-class>)
569 (define-standard-class <accessor> (<generic-with-setter>)
570 #:metaclass <applicable-struct-with-setter-class>)
571 (define-standard-class <extended-generic-with-setter> (<extended-generic>
572 <generic-with-setter>)
573 #:metaclass <applicable-struct-with-setter-class>)
574 (define-standard-class <extended-accessor> (<accessor>
575 <extended-generic-with-setter>)
576 #:metaclass <applicable-struct-with-setter-class>)
577
578 ;; Methods
579 (define-standard-class <method> (<object>)
580 generic-function
581 specializers
582 procedure
583 formals
584 body
585 make-procedure)
586 (define-standard-class <accessor-method> (<method>)
587 (slot-definition #:init-keyword #:slot-definition))
588
589 ;; Primitive types classes
590 (define-standard-class <boolean> (<top>))
591 (define-standard-class <char> (<top>))
592 (define-standard-class <list> (<top>))
593 (define-standard-class <pair> (<list>))
594 (define-standard-class <null> (<list>))
595 (define-standard-class <string> (<top>))
596 (define-standard-class <symbol> (<top>))
597 (define-standard-class <vector> (<top>))
598 (define-standard-class <foreign> (<top>))
599 (define-standard-class <hashtable> (<top>))
600 (define-standard-class <fluid> (<top>))
601 (define-standard-class <dynamic-state> (<top>))
602 (define-standard-class <frame> (<top>))
603 (define-standard-class <vm-continuation> (<top>))
604 (define-standard-class <bytevector> (<top>))
605 (define-standard-class <uvec> (<bytevector>))
606 (define-standard-class <array> (<top>))
607 (define-standard-class <bitvector> (<top>))
608 (define-standard-class <number> (<top>))
609 (define-standard-class <complex> (<number>))
610 (define-standard-class <real> (<complex>))
611 (define-standard-class <integer> (<real>))
612 (define-standard-class <fraction> (<real>))
613 (define-standard-class <keyword> (<top>))
614 (define-standard-class <unknown> (<top>))
615 (define-standard-class <procedure> (<applicable>)
616 #:metaclass <procedure-class>)
617 (define-standard-class <primitive-generic> (<procedure>)
618 #:metaclass <procedure-class>)
619 (define-standard-class <port> (<top>))
620 (define-standard-class <input-port> (<port>))
621 (define-standard-class <output-port> (<port>))
622 (define-standard-class <input-output-port> (<input-port> <output-port>))
623
624 (define (%invalidate-method-cache! gf)
625 (slot-set! gf 'procedure (delayed-compile gf))
626 (slot-set! gf 'effective-methods '()))
627
628 ;; Boot definition.
629 (define (invalidate-method-cache! gf)
630 (%invalidate-method-cache! gf))
631
632 (define* (get-keyword key l #:optional default)
633 "Determine an associated value for the keyword @var{key} from the list
634 @var{l}. The list @var{l} has to consist of an even number of elements,
635 where, starting with the first, every second element is a keyword,
636 followed by its associated value. If @var{l} does not hold a value for
637 @var{key}, the value @var{default} is returned."
638 (unless (keyword? key)
639 (scm-error 'wrong-type-arg #f "Not a keyword: ~S" (list key) #f))
640 (let lp ((l l))
641 (match l
642 (() default)
643 ((kw arg . l)
644 (unless (keyword? kw)
645 (scm-error 'wrong-type-arg #f "Not a keyword: ~S" (list kw) #f))
646 (if (eq? kw key) arg (lp l))))))
647
648 ;; A simple make which will be redefined later. This version handles
649 ;; only creation of gf, methods and classes (no instances).
650 ;;
651 ;; Since this code will disappear when Goops will be fully booted,
652 ;; no precaution is taken to be efficient.
653 ;;
654 (define (make class . args)
655 (cond
656 ((or (eq? class <generic>) (eq? class <accessor>))
657 (let ((z (make-struct/no-tail class #f '() 0 '())))
658 (set-procedure-property! z 'name (get-keyword #:name args #f))
659 (invalidate-method-cache! z)
660 (when (eq? class <accessor>)
661 (let ((setter (get-keyword #:setter args #f)))
662 (when setter
663 (slot-set! z 'setter setter))))
664 z))
665 (else
666 (let ((z (%allocate-instance class args)))
667 (cond
668 ((or (eq? class <method>) (eq? class <accessor-method>))
669 (for-each (match-lambda
670 ((kw slot default)
671 (slot-set! z slot (get-keyword kw args default))))
672 '((#:generic-function generic-function #f)
673 (#:specializers specializers ())
674 (#:procedure procedure #f)
675 (#:formals formals ())
676 (#:body body ())
677 (#:make-procedure make-procedure #f))))
678 ((memq <class> (class-precedence-list class))
679 (for-each (match-lambda
680 ((kw slot default)
681 (slot-set! z slot (get-keyword kw args default))))
682 '((#:name name ???)
683 (#:dsupers direct-supers ())
684 (#:slots direct-slots ())
685 )))
686 (else
687 (error "boot `make' does not support this class" class)))
688 z))))
689
690 ;; In the future, this function will return the effective slot
691 ;; definition associated with SLOT_NAME. Now it just returns some of
692 ;; the information which will be stored in the effective slot
693 ;; definition.
694 ;;
695 (define (get-slot-value-using-name class obj slot-name)
696 (match (assq slot-name (struct-ref class class-index-getters-n-setters))
697 (#f (slot-missing class obj slot-name))
698 ((name init-thunk . (? exact-integer? index))
699 (struct-ref obj index))
700 ((name init-thunk getter setter . _)
701 (getter obj))))
702
703 (define (set-slot-value-using-name! class obj slot-name value)
704 (match (assq slot-name (struct-ref class class-index-getters-n-setters))
705 (#f (slot-missing class obj slot-name value))
706 ((name init-thunk . (? exact-integer? index))
707 (struct-set! obj index value))
708 ((name init-thunk getter setter . _)
709 (setter obj value))))
710
711 (define (test-slot-existence class obj slot-name)
712 (and (assq slot-name (struct-ref class class-index-getters-n-setters))
713 #t))
714
715 ;; ========================================
716
717 (define (check-slot-args class obj slot-name)
718 (unless (class? class)
719 (scm-error 'wrong-type-arg #f "Not a class: ~S"
720 (list class) #f))
721 (unless (is-a? obj <object>)
722 (scm-error 'wrong-type-arg #f "Not an instance: ~S"
723 (list obj) #f))
724 (unless (symbol? slot-name)
725 (scm-error 'wrong-type-arg #f "Not a symbol: ~S"
726 (list slot-name) #f)))
727
728 (define (slot-ref-using-class class obj slot-name)
729 (check-slot-args class obj slot-name)
730 (let ((val (get-slot-value-using-name class obj slot-name)))
731 (if (unbound? val)
732 (slot-unbound class obj slot-name)
733 val)))
734
735 (define (slot-set-using-class! class obj slot-name value)
736 (check-slot-args class obj slot-name)
737 (set-slot-value-using-name! class obj slot-name value))
738
739 (define (slot-bound-using-class? class obj slot-name)
740 (check-slot-args class obj slot-name)
741 (not (unbound? (get-slot-value-using-name class obj slot-name))))
742
743 (define (slot-exists-using-class? class obj slot-name)
744 (check-slot-args class obj slot-name)
745 (test-slot-existence class obj slot-name))
746
747 ;; Class redefinition protocol:
748 ;;
749 ;; A class is represented by a heap header h1 which points to a
750 ;; malloc:ed memory block m1.
751 ;;
752 ;; When a new version of a class is created, a new header h2 and
753 ;; memory block m2 are allocated. The headers h1 and h2 then switch
754 ;; pointers so that h1 refers to m2 and h2 to m1. In this way, names
755 ;; bound to h1 will point to the new class at the same time as h2 will
756 ;; be a handle which the GC will use to free m1.
757 ;;
758 ;; The `redefined' slot of m1 will be set to point to h1. An old
759 ;; instance will have its class pointer (the CAR of the heap header)
760 ;; pointing to m1. The non-immediate `redefined'-slot in m1 indicates
761 ;; the class modification and the new class pointer can be found via
762 ;; h1.
763 ;;
764
765 ;; In the following interfaces, class-of handles the redefinition
766 ;; protocol. There would seem to be some thread-unsafety though as the
767 ;; { class, object data } pair needs to be accessed atomically, not the
768 ;; { class, object } pair.
769
770 (define (slot-ref obj slot-name)
771 "Return the value from @var{obj}'s slot with the nam var{slot_name}."
772 (slot-ref-using-class (class-of obj) obj slot-name))
773
774 (define (slot-set! obj slot-name value)
775 "Set the slot named @var{slot_name} of @var{obj} to @var{value}."
776 (slot-set-using-class! (class-of obj) obj slot-name value))
777
778 (define (slot-bound? obj slot-name)
779 "Return the value from @var{obj}'s slot with the nam var{slot_name}."
780 (slot-bound-using-class? (class-of obj) obj slot-name))
781
782 (define (slot-exists? obj slot-name)
783 "Return @code{#t} if @var{obj} has a slot named @var{slot_name}."
784 (slot-exists-using-class? (class-of obj) obj slot-name))
785
786 (define (method-generic-function obj)
787 "Return the generic function for the method @var{obj}."
788 (unless (is-a? obj <method>)
789 (scm-error 'wrong-type-arg #f "Not a method: ~S"
790 (list obj) #f))
791 (slot-ref obj 'generic-function))
792
793 (define (method-specializers obj)
794 "Return specializers of the method @var{obj}."
795 (unless (is-a? obj <method>)
796 (scm-error 'wrong-type-arg #f "Not a method: ~S"
797 (list obj) #f))
798 (slot-ref obj 'specializers))
799
800 (define (method-procedure obj)
801 "Return the procedure of the method @var{obj}."
802 (unless (is-a? obj <method>)
803 (scm-error 'wrong-type-arg #f "Not a method: ~S"
804 (list obj) #f))
805 (slot-ref obj 'procedure))
806
807 (define *dispatch-module* (current-module))
808
809 ;;;
810 ;;; Generic functions have an applicable-methods cache associated with
811 ;;; them. Every distinct set of types that is dispatched through a
812 ;;; generic adds an entry to the cache. This cache gets compiled out to
813 ;;; a dispatch procedure. In steady-state, this dispatch procedure is
814 ;;; never recompiled; but during warm-up there is some churn, both to
815 ;;; the cache and to the dispatch procedure.
816 ;;;
817 ;;; So what is the deal if warm-up happens in a multithreaded context?
818 ;;; There is indeed a window between missing the cache for a certain set
819 ;;; of arguments, and then updating the cache with the newly computed
820 ;;; applicable methods. One of the updaters is liable to lose their new
821 ;;; entry.
822 ;;;
823 ;;; This is actually OK though, because a subsequent cache miss for the
824 ;;; race loser will just cause memoization to try again. The cache will
825 ;;; eventually be consistent. We're not mutating the old part of the
826 ;;; cache, just consing on the new entry.
827 ;;;
828 ;;; It doesn't even matter if the dispatch procedure and the cache are
829 ;;; inconsistent -- most likely the type-set that lost the dispatch
830 ;;; procedure race will simply re-trigger a memoization, but since the
831 ;;; winner isn't in the effective-methods cache, it will likely also
832 ;;; re-trigger a memoization, and the cache will finally be consistent.
833 ;;; As you can see there is a possibility for ping-pong effects, but
834 ;;; it's unlikely given the shortness of the window between slot-set!
835 ;;; invocations. We could add a mutex, but it is strictly unnecessary,
836 ;;; and would add runtime cost and complexity.
837 ;;;
838
839 (define (emit-linear-dispatch gf-sym nargs methods free rest?)
840 (define (gen-syms n stem)
841 (let lp ((n (1- n)) (syms '()))
842 (if (< n 0)
843 syms
844 (lp (1- n) (cons (gensym stem) syms)))))
845 (let* ((args (gen-syms nargs "a"))
846 (types (gen-syms nargs "t")))
847 (let lp ((methods methods)
848 (free free)
849 (exp `(cache-miss ,gf-sym
850 ,(if rest?
851 `(cons* ,@args rest)
852 `(list ,@args)))))
853 (cond
854 ((null? methods)
855 (values `(,(if rest? `(,@args . rest) args)
856 (let ,(map (lambda (t a)
857 `(,t (class-of ,a)))
858 types args)
859 ,exp))
860 free))
861 (else
862 ;; jeez
863 (let preddy ((free free)
864 (types types)
865 (specs (vector-ref (car methods) 1))
866 (checks '()))
867 (if (null? types)
868 (let ((m-sym (gensym "p")))
869 (lp (cdr methods)
870 (acons (vector-ref (car methods) 3)
871 m-sym
872 free)
873 `(if (and . ,checks)
874 ,(if rest?
875 `(apply ,m-sym ,@args rest)
876 `(,m-sym . ,args))
877 ,exp)))
878 (let ((var (assq-ref free (car specs))))
879 (if var
880 (preddy free
881 (cdr types)
882 (cdr specs)
883 (cons `(eq? ,(car types) ,var)
884 checks))
885 (let ((var (gensym "c")))
886 (preddy (acons (car specs) var free)
887 (cdr types)
888 (cdr specs)
889 (cons `(eq? ,(car types) ,var)
890 checks))))))))))))
891
892 (define (compute-dispatch-procedure gf cache)
893 (define (scan)
894 (let lp ((ls cache) (nreq -1) (nrest -1))
895 (cond
896 ((null? ls)
897 (collate (make-vector (1+ nreq) '())
898 (make-vector (1+ nrest) '())))
899 ((vector-ref (car ls) 2) ; rest
900 (lp (cdr ls) nreq (max nrest (vector-ref (car ls) 0))))
901 (else ; req
902 (lp (cdr ls) (max nreq (vector-ref (car ls) 0)) nrest)))))
903 (define (collate req rest)
904 (let lp ((ls cache))
905 (cond
906 ((null? ls)
907 (emit req rest))
908 ((vector-ref (car ls) 2) ; rest
909 (let ((n (vector-ref (car ls) 0)))
910 (vector-set! rest n (cons (car ls) (vector-ref rest n)))
911 (lp (cdr ls))))
912 (else ; req
913 (let ((n (vector-ref (car ls) 0)))
914 (vector-set! req n (cons (car ls) (vector-ref req n)))
915 (lp (cdr ls)))))))
916 (define (emit req rest)
917 (let ((gf-sym (gensym "g")))
918 (define (emit-rest n clauses free)
919 (if (< n (vector-length rest))
920 (let ((methods (vector-ref rest n)))
921 (cond
922 ((null? methods)
923 (emit-rest (1+ n) clauses free))
924 ;; FIXME: hash dispatch
925 (else
926 (call-with-values
927 (lambda ()
928 (emit-linear-dispatch gf-sym n methods free #t))
929 (lambda (clause free)
930 (emit-rest (1+ n) (cons clause clauses) free))))))
931 (emit-req (1- (vector-length req)) clauses free)))
932 (define (emit-req n clauses free)
933 (if (< n 0)
934 (comp `(lambda ,(map cdr free)
935 (case-lambda ,@clauses))
936 (map car free))
937 (let ((methods (vector-ref req n)))
938 (cond
939 ((null? methods)
940 (emit-req (1- n) clauses free))
941 ;; FIXME: hash dispatch
942 (else
943 (call-with-values
944 (lambda ()
945 (emit-linear-dispatch gf-sym n methods free #f))
946 (lambda (clause free)
947 (emit-req (1- n) (cons clause clauses) free))))))))
948
949 (emit-rest 0
950 (if (or (zero? (vector-length rest))
951 (null? (vector-ref rest 0)))
952 (list `(args (cache-miss ,gf-sym args)))
953 '())
954 (acons gf gf-sym '()))))
955 (define (comp exp vals)
956 ;; When cross-compiling Guile itself, the native Guile must generate
957 ;; code for the host.
958 (with-target %host-type
959 (lambda ()
960 (let ((p ((@ (system base compile) compile) exp
961 #:env *dispatch-module*
962 #:from 'scheme
963 #:opts '(#:partial-eval? #f #:cse? #f))))
964 (apply p vals)))))
965
966 ;; kick it.
967 (scan))
968
969 ;; o/~ ten, nine, eight
970 ;; sometimes that's just how it goes
971 ;; three, two, one
972 ;;
973 ;; get out before it blows o/~
974 ;;
975 (define timer-init 30)
976 (define (delayed-compile gf)
977 (let ((timer timer-init))
978 (lambda args
979 (set! timer (1- timer))
980 (cond
981 ((zero? timer)
982 (let ((dispatch (compute-dispatch-procedure
983 gf (slot-ref gf 'effective-methods))))
984 (slot-set! gf 'procedure dispatch)
985 (apply dispatch args)))
986 (else
987 ;; interestingly, this catches recursive compilation attempts as
988 ;; well; in that case, timer is negative
989 (cache-dispatch gf args))))))
990
991 (define (cache-dispatch gf args)
992 (define (map-until n f ls)
993 (if (or (zero? n) (null? ls))
994 '()
995 (cons (f (car ls)) (map-until (1- n) f (cdr ls)))))
996 (define (equal? x y) ; can't use the stock equal? because it's a generic...
997 (cond ((pair? x) (and (pair? y)
998 (eq? (car x) (car y))
999 (equal? (cdr x) (cdr y))))
1000 ((null? x) (null? y))
1001 (else #f)))
1002 (if (slot-ref gf 'n-specialized)
1003 (let ((types (map-until (slot-ref gf 'n-specialized) class-of args)))
1004 (let lp ((cache (slot-ref gf 'effective-methods)))
1005 (cond ((null? cache)
1006 (cache-miss gf args))
1007 ((equal? (vector-ref (car cache) 1) types)
1008 (apply (vector-ref (car cache) 3) args))
1009 (else (lp (cdr cache))))))
1010 (cache-miss gf args)))
1011
1012 (define (cache-miss gf args)
1013 (apply (memoize-method! gf args) args))
1014
1015 (define (memoize-effective-method! gf args applicable)
1016 (define (first-n ls n)
1017 (if (or (zero? n) (null? ls))
1018 '()
1019 (cons (car ls) (first-n (cdr ls) (- n 1)))))
1020 (define (parse n ls)
1021 (cond ((null? ls)
1022 (memoize n #f (map class-of args)))
1023 ((= n (slot-ref gf 'n-specialized))
1024 (memoize n #t (map class-of (first-n args n))))
1025 (else
1026 (parse (1+ n) (cdr ls)))))
1027 (define (memoize len rest? types)
1028 (let* ((cmethod (compute-cmethod applicable types))
1029 (cache (cons (vector len types rest? cmethod)
1030 (slot-ref gf 'effective-methods))))
1031 (slot-set! gf 'effective-methods cache)
1032 (slot-set! gf 'procedure (delayed-compile gf))
1033 cmethod))
1034 (parse 0 args))
1035
1036 ;;;
1037 ;;; Compiling next methods into method bodies
1038 ;;;
1039
1040 ;;; So, for the reader: there basic idea is that, given that the
1041 ;;; semantics of `next-method' depend on the concrete types being
1042 ;;; dispatched, why not compile a specific procedure to handle each type
1043 ;;; combination that we see at runtime.
1044 ;;;
1045 ;;; In theory we can do much better than a bytecode compilation, because
1046 ;;; we know the *exact* types of the arguments. It's ideal for native
1047 ;;; compilation. A task for the future.
1048 ;;;
1049 ;;; I think this whole generic application mess would benefit from a
1050 ;;; strict MOP.
1051
1052 (define (compute-cmethod methods types)
1053 (let ((make-procedure (slot-ref (car methods) 'make-procedure)))
1054 (if make-procedure
1055 (make-procedure
1056 (if (null? (cdr methods))
1057 (lambda args
1058 (no-next-method (method-generic-function (car methods)) args))
1059 (compute-cmethod (cdr methods) types)))
1060 (method-procedure (car methods)))))
1061
1062 ;;;
1063 ;;; Memoization
1064 ;;;
1065
1066 (define (memoize-method! gf args)
1067 (let ((applicable ((if (eq? gf compute-applicable-methods)
1068 %compute-applicable-methods
1069 compute-applicable-methods)
1070 gf args)))
1071 (cond (applicable
1072 (memoize-effective-method! gf args applicable))
1073 (else
1074 (no-applicable-method gf args)))))
1075
1076 (set-procedure-property! memoize-method! 'system-procedure #t)
1077
1078 (define no-applicable-method
1079 (make <generic> #:name 'no-applicable-method))
1080
1081 (%goops-early-init)
1082
1083 ;; Then load the rest of GOOPS
1084
1085 \f
1086 ;; FIXME: deprecate.
1087 (define min-fixnum (- (expt 2 29)))
1088 (define max-fixnum (- (expt 2 29) 1))
1089
1090 ;;
1091 ;; goops-error
1092 ;;
1093 (define (goops-error format-string . args)
1094 (scm-error 'goops-error #f format-string args '()))
1095
1096 ;;;
1097 ;;; {Meta classes}
1098 ;;;
1099
1100 (define ensure-metaclass-with-supers
1101 (let ((table-of-metas '()))
1102 (lambda (meta-supers)
1103 (let ((entry (assoc meta-supers table-of-metas)))
1104 (if entry
1105 ;; Found a previously created metaclass
1106 (cdr entry)
1107 ;; Create a new meta-class which inherit from "meta-supers"
1108 (let ((new (make <class> #:dsupers meta-supers
1109 #:slots '()
1110 #:name (gensym "metaclass"))))
1111 (set! table-of-metas (cons (cons meta-supers new) table-of-metas))
1112 new))))))
1113
1114 (define (ensure-metaclass supers)
1115 (if (null? supers)
1116 <class>
1117 (let* ((all-metas (map (lambda (x) (class-of x)) supers))
1118 (all-cpls (append-map (lambda (m)
1119 (cdr (class-precedence-list m)))
1120 all-metas))
1121 (needed-metas '()))
1122 ;; Find the most specific metaclasses. The new metaclass will be
1123 ;; a subclass of these.
1124 (for-each
1125 (lambda (meta)
1126 (if (and (not (member meta all-cpls))
1127 (not (member meta needed-metas)))
1128 (set! needed-metas (append needed-metas (list meta)))))
1129 all-metas)
1130 ;; Now return a subclass of the metaclasses we found.
1131 (if (null? (cdr needed-metas))
1132 (car needed-metas) ; If there's only one, just use it.
1133 (ensure-metaclass-with-supers needed-metas)))))
1134
1135 ;;;
1136 ;;; {Classes}
1137 ;;;
1138
1139 ;;; (define-class NAME (SUPER ...) SLOT-DEFINITION ... OPTION ...)
1140 ;;;
1141 ;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
1142 ;;; OPTION ::= KEYWORD VALUE
1143 ;;;
1144
1145 (define (make-class supers slots . options)
1146 (let* ((name (get-keyword #:name options (make-unbound)))
1147 (supers (if (not (or-map (lambda (class)
1148 (memq <object>
1149 (class-precedence-list class)))
1150 supers))
1151 (append supers (list <object>))
1152 supers))
1153 (metaclass (or (get-keyword #:metaclass options #f)
1154 (ensure-metaclass supers))))
1155
1156 ;; Verify that all direct slots are different and that we don't inherit
1157 ;; several time from the same class
1158 (let ((tmp1 (find-duplicate supers))
1159 (tmp2 (find-duplicate (map slot-definition-name slots))))
1160 (if tmp1
1161 (goops-error "make-class: super class ~S is duplicate in class ~S"
1162 tmp1 name))
1163 (if tmp2
1164 (goops-error "make-class: slot ~S is duplicate in class ~S"
1165 tmp2 name)))
1166
1167 ;; Everything seems correct, build the class
1168 (apply make metaclass
1169 #:dsupers supers
1170 #:slots slots
1171 #:name name
1172 options)))
1173
1174 ;;; (class (SUPER ...) SLOT-DEFINITION ... OPTION ...)
1175 ;;;
1176 ;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
1177 ;;; OPTION ::= KEYWORD VALUE
1178 ;;;
1179 (define-syntax class
1180 (lambda (x)
1181 (define (parse-options options)
1182 (syntax-case options ()
1183 (() #'())
1184 ((kw arg . options) (keyword? (syntax->datum #'kw))
1185 (with-syntax ((options (parse-options #'options)))
1186 (syntax-case #'kw ()
1187 (#:init-form
1188 #'(kw 'arg #:init-thunk (lambda () arg) . options))
1189 (_
1190 #'(kw arg . options)))))))
1191 (define (check-valid-kwargs args)
1192 (syntax-case args ()
1193 (() #'())
1194 ((kw arg . args) (keyword? (syntax->datum #'kw))
1195 #`(kw arg . #,(check-valid-kwargs #'args)))))
1196 (define (parse-slots-and-kwargs args)
1197 (syntax-case args ()
1198 (()
1199 #'(() ()))
1200 ((kw . _) (keyword? (syntax->datum #'kw))
1201 #`(() #,(check-valid-kwargs args)))
1202 (((name option ...) args ...)
1203 (with-syntax (((slots kwargs) (parse-slots-and-kwargs #'(args ...)))
1204 ((option ...) (parse-options #'(option ...))))
1205 #'(((list 'name option ...) . slots) kwargs)))
1206 ((name args ...) (symbol? (syntax->datum #'name))
1207 (with-syntax (((slots kwargs) (parse-slots-and-kwargs #'(args ...))))
1208 #'(('(name) . slots) kwargs)))))
1209 (syntax-case x ()
1210 ((class (super ...) arg ...)
1211 (with-syntax ((((slot-def ...) (option ...))
1212 (parse-slots-and-kwargs #'(arg ...))))
1213 #'(make-class (list super ...)
1214 (list slot-def ...)
1215 option ...))))))
1216
1217 (define-syntax define-class-pre-definition
1218 (lambda (x)
1219 (syntax-case x ()
1220 ((_ (k arg rest ...) out ...)
1221 (keyword? (syntax->datum #'k))
1222 (case (syntax->datum #'k)
1223 ((#:getter #:setter)
1224 #'(define-class-pre-definition (rest ...)
1225 out ...
1226 (if (or (not (defined? 'arg))
1227 (not (is-a? arg <generic>)))
1228 (toplevel-define!
1229 'arg
1230 (ensure-generic (if (defined? 'arg) arg #f) 'arg)))))
1231 ((#:accessor)
1232 #'(define-class-pre-definition (rest ...)
1233 out ...
1234 (if (or (not (defined? 'arg))
1235 (not (is-a? arg <accessor>)))
1236 (toplevel-define!
1237 'arg
1238 (ensure-accessor (if (defined? 'arg) arg #f) 'arg)))))
1239 (else
1240 #'(define-class-pre-definition (rest ...) out ...))))
1241 ((_ () out ...)
1242 #'(begin out ...)))))
1243
1244 ;; Some slot options require extra definitions to be made. In
1245 ;; particular, we want to make sure that the generic function objects
1246 ;; which represent accessors exist before `make-class' tries to add
1247 ;; methods to them.
1248 (define-syntax define-class-pre-definitions
1249 (lambda (x)
1250 (syntax-case x ()
1251 ((_ () out ...)
1252 #'(begin out ...))
1253 ((_ (slot rest ...) out ...)
1254 (keyword? (syntax->datum #'slot))
1255 #'(begin out ...))
1256 ((_ (slot rest ...) out ...)
1257 (identifier? #'slot)
1258 #'(define-class-pre-definitions (rest ...)
1259 out ...))
1260 ((_ ((slotname slotopt ...) rest ...) out ...)
1261 #'(define-class-pre-definitions (rest ...)
1262 out ... (define-class-pre-definition (slotopt ...)))))))
1263
1264 (define-syntax-rule (define-class name supers slot ...)
1265 (begin
1266 (define-class-pre-definitions (slot ...))
1267 (if (and (defined? 'name)
1268 (is-a? name <class>)
1269 (memq <object> (class-precedence-list name)))
1270 (class-redefinition name
1271 (class supers slot ... #:name 'name))
1272 (toplevel-define! 'name (class supers slot ... #:name 'name)))))
1273
1274 (define-syntax-rule (standard-define-class arg ...)
1275 (define-class arg ...))
1276
1277 ;;;
1278 ;;; {Generic functions and accessors}
1279 ;;;
1280
1281 ;; Apparently the desired semantics are that we extend previous
1282 ;; procedural definitions, but that if `name' was already a generic, we
1283 ;; overwrite its definition.
1284 (define-syntax define-generic
1285 (lambda (x)
1286 (syntax-case x ()
1287 ((define-generic name) (symbol? (syntax->datum #'name))
1288 #'(define name
1289 (if (and (defined? 'name) (is-a? name <generic>))
1290 (make <generic> #:name 'name)
1291 (ensure-generic (if (defined? 'name) name #f) 'name)))))))
1292
1293 (define-syntax define-extended-generic
1294 (lambda (x)
1295 (syntax-case x ()
1296 ((define-extended-generic name val) (symbol? (syntax->datum #'name))
1297 #'(define name (make-extended-generic val 'name))))))
1298
1299 (define-syntax define-extended-generics
1300 (lambda (x)
1301 (define (id-append ctx a b)
1302 (datum->syntax ctx (symbol-append (syntax->datum a) (syntax->datum b))))
1303 (syntax-case x ()
1304 ((define-extended-generic (name ...) #:prefix (prefix ...))
1305 (and (and-map symbol? (syntax->datum #'(name ...)))
1306 (and-map symbol? (syntax->datum #'(prefix ...))))
1307 (with-syntax ((((val ...)) (map (lambda (name)
1308 (map (lambda (prefix)
1309 (id-append name prefix name))
1310 #'(prefix ...)))
1311 #'(name ...))))
1312 #'(begin
1313 (define-extended-generic name (list val ...))
1314 ...))))))
1315
1316 (define* (make-generic #:optional name)
1317 (make <generic> #:name name))
1318
1319 (define* (make-extended-generic gfs #:optional name)
1320 (let* ((gfs (if (list? gfs) gfs (list gfs)))
1321 (gws? (any (lambda (gf) (is-a? gf <generic-with-setter>)) gfs)))
1322 (let ((ans (if gws?
1323 (let* ((sname (and name (make-setter-name name)))
1324 (setters
1325 (append-map (lambda (gf)
1326 (if (is-a? gf <generic-with-setter>)
1327 (list (ensure-generic (setter gf)
1328 sname))
1329 '()))
1330 gfs))
1331 (es (make <extended-generic-with-setter>
1332 #:name name
1333 #:extends gfs
1334 #:setter (make <extended-generic>
1335 #:name sname
1336 #:extends setters))))
1337 (extended-by! setters (setter es))
1338 es)
1339 (make <extended-generic>
1340 #:name name
1341 #:extends gfs))))
1342 (extended-by! gfs ans)
1343 ans)))
1344
1345 (define (extended-by! gfs eg)
1346 (for-each (lambda (gf)
1347 (slot-set! gf 'extended-by
1348 (cons eg (slot-ref gf 'extended-by))))
1349 gfs)
1350 (invalidate-method-cache! eg))
1351
1352 (define (not-extended-by! gfs eg)
1353 (for-each (lambda (gf)
1354 (slot-set! gf 'extended-by
1355 (delq! eg (slot-ref gf 'extended-by))))
1356 gfs)
1357 (invalidate-method-cache! eg))
1358
1359 (define* (ensure-generic old-definition #:optional name)
1360 (cond ((is-a? old-definition <generic>) old-definition)
1361 ((procedure-with-setter? old-definition)
1362 (make <generic-with-setter>
1363 #:name name
1364 #:default (procedure old-definition)
1365 #:setter (setter old-definition)))
1366 ((procedure? old-definition)
1367 (if (generic-capability? old-definition) old-definition
1368 (make <generic> #:name name #:default old-definition)))
1369 (else (make <generic> #:name name))))
1370
1371 ;; same semantics as <generic>
1372 (define-syntax-rule (define-accessor name)
1373 (define name
1374 (cond ((not (defined? 'name)) (ensure-accessor #f 'name))
1375 ((is-a? name <accessor>) (make <accessor> #:name 'name))
1376 (else (ensure-accessor name 'name)))))
1377
1378 (define (make-setter-name name)
1379 (string->symbol (string-append "setter:" (symbol->string name))))
1380
1381 (define* (make-accessor #:optional name)
1382 (make <accessor>
1383 #:name name
1384 #:setter (make <generic>
1385 #:name (and name (make-setter-name name)))))
1386
1387 (define* (ensure-accessor proc #:optional name)
1388 (cond ((and (is-a? proc <accessor>)
1389 (is-a? (setter proc) <generic>))
1390 proc)
1391 ((is-a? proc <generic-with-setter>)
1392 (upgrade-accessor proc (setter proc)))
1393 ((is-a? proc <generic>)
1394 (upgrade-accessor proc (make-generic name)))
1395 ((procedure-with-setter? proc)
1396 (make <accessor>
1397 #:name name
1398 #:default (procedure proc)
1399 #:setter (ensure-generic (setter proc) name)))
1400 ((procedure? proc)
1401 (ensure-accessor (if (generic-capability? proc)
1402 (make <generic> #:name name #:default proc)
1403 (ensure-generic proc name))
1404 name))
1405 (else
1406 (make-accessor name))))
1407
1408 (define (upgrade-accessor generic setter)
1409 (let ((methods (slot-ref generic 'methods))
1410 (gws (make (if (is-a? generic <extended-generic>)
1411 <extended-generic-with-setter>
1412 <accessor>)
1413 #:name (generic-function-name generic)
1414 #:extended-by (slot-ref generic 'extended-by)
1415 #:setter setter)))
1416 (if (is-a? generic <extended-generic>)
1417 (let ((gfs (slot-ref generic 'extends)))
1418 (not-extended-by! gfs generic)
1419 (slot-set! gws 'extends gfs)
1420 (extended-by! gfs gws)))
1421 ;; Steal old methods
1422 (for-each (lambda (method)
1423 (slot-set! method 'generic-function gws))
1424 methods)
1425 (slot-set! gws 'methods methods)
1426 (invalidate-method-cache! gws)
1427 gws))
1428
1429 ;;;
1430 ;;; {Methods}
1431 ;;;
1432
1433 ;; Note: `a' and `b' can have unequal lengths (i.e. one can be one
1434 ;; element longer than the other when we have a dotted parameter
1435 ;; list). For instance, with the call
1436 ;;
1437 ;; (M 1)
1438 ;;
1439 ;; with
1440 ;;
1441 ;; (define-method M (a . l) ....)
1442 ;; (define-method M (a) ....)
1443 ;;
1444 ;; we consider that the second method is more specific.
1445 ;;
1446 ;; Precondition: `a' and `b' are methods and are applicable to `types'.
1447 (define (%method-more-specific? a b types)
1448 (let lp ((a-specializers (method-specializers a))
1449 (b-specializers (method-specializers b))
1450 (types types))
1451 (cond
1452 ;; (a) less specific than (a b ...) or (a . b)
1453 ((null? a-specializers) #t)
1454 ;; (a b ...) or (a . b) less specific than (a)
1455 ((null? b-specializers) #f)
1456 ;; (a . b) less specific than (a b ...)
1457 ((not (pair? a-specializers)) #f)
1458 ;; (a b ...) more specific than (a . b)
1459 ((not (pair? b-specializers)) #t)
1460 (else
1461 (let ((a-specializer (car a-specializers))
1462 (b-specializer (car b-specializers))
1463 (a-specializers (cdr a-specializers))
1464 (b-specializers (cdr b-specializers))
1465 (type (car types))
1466 (types (cdr types)))
1467 (if (eq? a-specializer b-specializer)
1468 (lp a-specializers b-specializers types)
1469 (let lp ((cpl (class-precedence-list type)))
1470 (let ((elt (car cpl)))
1471 (cond
1472 ((eq? a-specializer elt) #t)
1473 ((eq? b-specializer elt) #f)
1474 (else (lp (cdr cpl))))))))))))
1475
1476 (define (%sort-applicable-methods methods types)
1477 (sort methods (lambda (a b) (%method-more-specific? a b types))))
1478
1479 (define (generic-function-methods obj)
1480 "Return the methods of the generic function @var{obj}."
1481 (define (fold-upward method-lists gf)
1482 (cond
1483 ((is-a? gf <extended-generic>)
1484 (let lp ((method-lists method-lists) (gfs (slot-ref gf 'extends)))
1485 (match gfs
1486 (() method-lists)
1487 ((gf . gfs)
1488 (lp (fold-upward (cons (slot-ref gf 'methods) method-lists) gf)
1489 gfs)))))
1490 (else method-lists)))
1491 (define (fold-downward method-lists gf)
1492 (let lp ((method-lists (cons (slot-ref gf 'methods) method-lists))
1493 (gfs (slot-ref gf 'extended-by)))
1494 (match gfs
1495 (() method-lists)
1496 ((gf . gfs)
1497 (lp (fold-downward method-lists gf) gfs)))))
1498 (unless (is-a? obj <generic>)
1499 (scm-error 'wrong-type-arg #f "Not a generic: ~S"
1500 (list obj) #f))
1501 (concatenate (fold-downward (fold-upward '() obj) obj)))
1502
1503 (define (%compute-applicable-methods gf args)
1504 (define (method-applicable? m types)
1505 (let lp ((specs (method-specializers m)) (types types))
1506 (cond
1507 ((null? specs) (null? types))
1508 ((not (pair? specs)) #t)
1509 ((null? types) #f)
1510 (else
1511 (and (memq (car specs) (class-precedence-list (car types)))
1512 (lp (cdr specs) (cdr types)))))))
1513 (let ((n (length args))
1514 (types (map class-of args)))
1515 (let lp ((methods (generic-function-methods gf))
1516 (applicable '()))
1517 (if (null? methods)
1518 (and (not (null? applicable))
1519 (%sort-applicable-methods applicable types))
1520 (let ((m (car methods)))
1521 (lp (cdr methods)
1522 (if (method-applicable? m types)
1523 (cons m applicable)
1524 applicable)))))))
1525
1526 (define compute-applicable-methods %compute-applicable-methods)
1527
1528 (define (toplevel-define! name val)
1529 (module-define! (current-module) name val))
1530
1531 (define-syntax define-method
1532 (syntax-rules (setter)
1533 ((_ ((setter name) . args) body ...)
1534 (begin
1535 (if (or (not (defined? 'name))
1536 (not (is-a? name <accessor>)))
1537 (toplevel-define! 'name
1538 (ensure-accessor
1539 (if (defined? 'name) name #f) 'name)))
1540 (add-method! (setter name) (method args body ...))))
1541 ((_ (name . args) body ...)
1542 (begin
1543 ;; FIXME: this code is how it always was, but it's quite cracky:
1544 ;; it will only define the generic function if it was undefined
1545 ;; before (ok), or *was defined to #f*. The latter is crack. But
1546 ;; there are bootstrap issues about fixing this -- change it to
1547 ;; (is-a? name <generic>) and see.
1548 (if (or (not (defined? 'name))
1549 (not name))
1550 (toplevel-define! 'name (make <generic> #:name 'name)))
1551 (add-method! name (method args body ...))))))
1552
1553 (define-syntax method
1554 (lambda (x)
1555 (define (parse-args args)
1556 (let lp ((ls args) (formals '()) (specializers '()))
1557 (syntax-case ls ()
1558 (((f s) . rest)
1559 (and (identifier? #'f) (identifier? #'s))
1560 (lp #'rest
1561 (cons #'f formals)
1562 (cons #'s specializers)))
1563 ((f . rest)
1564 (identifier? #'f)
1565 (lp #'rest
1566 (cons #'f formals)
1567 (cons #'<top> specializers)))
1568 (()
1569 (list (reverse formals)
1570 (reverse (cons #''() specializers))))
1571 (tail
1572 (identifier? #'tail)
1573 (list (append (reverse formals) #'tail)
1574 (reverse (cons #'<top> specializers)))))))
1575
1576 (define (find-free-id exp referent)
1577 (syntax-case exp ()
1578 ((x . y)
1579 (or (find-free-id #'x referent)
1580 (find-free-id #'y referent)))
1581 (x
1582 (identifier? #'x)
1583 (let ((id (datum->syntax #'x referent)))
1584 (and (free-identifier=? #'x id) id)))
1585 (_ #f)))
1586
1587 (define (compute-procedure formals body)
1588 (syntax-case body ()
1589 ((body0 ...)
1590 (with-syntax ((formals formals))
1591 #'(lambda formals body0 ...)))))
1592
1593 (define (->proper args)
1594 (let lp ((ls args) (out '()))
1595 (syntax-case ls ()
1596 ((x . xs) (lp #'xs (cons #'x out)))
1597 (() (reverse out))
1598 (tail (reverse (cons #'tail out))))))
1599
1600 (define (compute-make-procedure formals body next-method)
1601 (syntax-case body ()
1602 ((body ...)
1603 (with-syntax ((next-method next-method))
1604 (syntax-case formals ()
1605 ((formal ...)
1606 #'(lambda (real-next-method)
1607 (lambda (formal ...)
1608 (let ((next-method (lambda args
1609 (if (null? args)
1610 (real-next-method formal ...)
1611 (apply real-next-method args)))))
1612 body ...))))
1613 (formals
1614 (with-syntax (((formal ...) (->proper #'formals)))
1615 #'(lambda (real-next-method)
1616 (lambda formals
1617 (let ((next-method (lambda args
1618 (if (null? args)
1619 (apply real-next-method formal ...)
1620 (apply real-next-method args)))))
1621 body ...))))))))))
1622
1623 (define (compute-procedures formals body)
1624 ;; So, our use of this is broken, because it operates on the
1625 ;; pre-expansion source code. It's equivalent to just searching
1626 ;; for referent in the datums. Ah well.
1627 (let ((id (find-free-id body 'next-method)))
1628 (if id
1629 ;; return a make-procedure
1630 (values #'#f
1631 (compute-make-procedure formals body id))
1632 (values (compute-procedure formals body)
1633 #'#f))))
1634
1635 (syntax-case x ()
1636 ((_ args) #'(method args (if #f #f)))
1637 ((_ args body0 body1 ...)
1638 (with-syntax (((formals (specializer ...)) (parse-args #'args)))
1639 (call-with-values
1640 (lambda ()
1641 (compute-procedures #'formals #'(body0 body1 ...)))
1642 (lambda (procedure make-procedure)
1643 (with-syntax ((procedure procedure)
1644 (make-procedure make-procedure))
1645 #'(make <method>
1646 #:specializers (cons* specializer ...)
1647 #:formals 'formals
1648 #:body '(body0 body1 ...)
1649 #:make-procedure make-procedure
1650 #:procedure procedure)))))))))
1651
1652 ;;;
1653 ;;; {add-method!}
1654 ;;;
1655
1656 (define (add-method-in-classes! m)
1657 ;; Add method in all the classes which appears in its specializers list
1658 (for-each* (lambda (x)
1659 (let ((dm (class-direct-methods x)))
1660 (unless (memq m dm)
1661 (struct-set! x class-index-direct-methods (cons m dm)))))
1662 (method-specializers m)))
1663
1664 (define (remove-method-in-classes! m)
1665 ;; Remove method in all the classes which appears in its specializers list
1666 (for-each* (lambda (x)
1667 (struct-set! x
1668 class-index-direct-methods
1669 (delv! m (class-direct-methods x))))
1670 (method-specializers m)))
1671
1672 (define (compute-new-list-of-methods gf new)
1673 (let ((new-spec (method-specializers new))
1674 (methods (slot-ref gf 'methods)))
1675 (let loop ((l methods))
1676 (if (null? l)
1677 (cons new methods)
1678 (if (equal? (method-specializers (car l)) new-spec)
1679 (begin
1680 ;; This spec. list already exists. Remove old method from dependents
1681 (remove-method-in-classes! (car l))
1682 (set-car! l new)
1683 methods)
1684 (loop (cdr l)))))))
1685
1686 (define (method-n-specializers m)
1687 (length* (slot-ref m 'specializers)))
1688
1689 (define (calculate-n-specialized gf)
1690 (fold (lambda (m n) (max n (method-n-specializers m)))
1691 0
1692 (generic-function-methods gf)))
1693
1694 (define (invalidate-method-cache! gf)
1695 (%invalidate-method-cache! gf)
1696 (slot-set! gf 'n-specialized (calculate-n-specialized gf))
1697 (for-each (lambda (gf) (invalidate-method-cache! gf))
1698 (slot-ref gf 'extended-by)))
1699
1700 (define internal-add-method!
1701 (method ((gf <generic>) (m <method>))
1702 (slot-set! m 'generic-function gf)
1703 (slot-set! gf 'methods (compute-new-list-of-methods gf m))
1704 (invalidate-method-cache! gf)
1705 (add-method-in-classes! m)
1706 *unspecified*))
1707
1708 (define-generic add-method!)
1709
1710 ((method-procedure internal-add-method!) add-method! internal-add-method!)
1711
1712 (define-method (add-method! (proc <procedure>) (m <method>))
1713 (if (generic-capability? proc)
1714 (begin
1715 (enable-primitive-generic! proc)
1716 (add-method! proc m))
1717 (next-method)))
1718
1719 (define-method (add-method! (pg <primitive-generic>) (m <method>))
1720 (add-method! (primitive-generic-generic pg) m))
1721
1722 (define-method (add-method! obj (m <method>))
1723 (goops-error "~S is not a valid generic function" obj))
1724
1725 ;;;
1726 ;;; {Access to meta objects}
1727 ;;;
1728
1729 ;;;
1730 ;;; Methods
1731 ;;;
1732 (define-method (method-source (m <method>))
1733 (let* ((spec (map* class-name (slot-ref m 'specializers)))
1734 (src (procedure-source (slot-ref m 'procedure))))
1735 (and src
1736 (let ((args (cadr src))
1737 (body (cddr src)))
1738 (cons 'method
1739 (cons (map* list args spec)
1740 body))))))
1741
1742 (define-method (method-formals (m <method>))
1743 (slot-ref m 'formals))
1744
1745 ;;;
1746 ;;; Slots
1747 ;;;
1748 (define slot-definition-name car)
1749
1750 (define slot-definition-options cdr)
1751
1752 (define (slot-definition-allocation s)
1753 (get-keyword #:allocation (cdr s) #:instance))
1754
1755 (define (slot-definition-getter s)
1756 (get-keyword #:getter (cdr s) #f))
1757
1758 (define (slot-definition-setter s)
1759 (get-keyword #:setter (cdr s) #f))
1760
1761 (define (slot-definition-accessor s)
1762 (get-keyword #:accessor (cdr s) #f))
1763
1764 (define (slot-definition-init-value s)
1765 ;; can be #f, so we can't use #f as non-value
1766 (get-keyword #:init-value (cdr s) (make-unbound)))
1767
1768 (define (slot-definition-init-form s)
1769 (get-keyword #:init-form (cdr s) (make-unbound)))
1770
1771 (define (slot-definition-init-thunk s)
1772 (get-keyword #:init-thunk (cdr s) #f))
1773
1774 (define (slot-definition-init-keyword s)
1775 (get-keyword #:init-keyword (cdr s) #f))
1776
1777 (define (class-slot-definition class slot-name)
1778 (assq slot-name (class-slots class)))
1779
1780 (define (slot-init-function class slot-name)
1781 (cadr (assq slot-name (struct-ref class class-index-getters-n-setters))))
1782
1783 (define (accessor-method-slot-definition obj)
1784 "Return the slot definition of the accessor @var{obj}."
1785 (slot-ref obj 'slot-definition))
1786
1787
1788 ;;;
1789 ;;; {Standard methods used by the C runtime}
1790 ;;;
1791
1792 ;;; Methods to compare objects
1793 ;;;
1794
1795 ;; Have to do this in a strange order because equal? is used in the
1796 ;; add-method! implementation; we need to make sure that when the
1797 ;; primitive is extended, that the generic has a method. =
1798 (define g-equal? (make-generic 'equal?))
1799 ;; When this generic gets called, we will have already checked eq? and
1800 ;; eqv? -- the purpose of this generic is to extend equality. So by
1801 ;; default, there is no extension, thus the #f return.
1802 (add-method! g-equal? (method (x y) #f))
1803 (set-primitive-generic! equal? g-equal?)
1804
1805 ;;;
1806 ;;; methods to display/write an object
1807 ;;;
1808
1809 ; Code for writing objects must test that the slots they use are
1810 ; bound. Otherwise a slot-unbound method will be called and will
1811 ; conduct to an infinite loop.
1812
1813 ;; Write
1814 (define (display-address o file)
1815 (display (number->string (object-address o) 16) file))
1816
1817 (define-method (write o file)
1818 (display "#<instance " file)
1819 (display-address o file)
1820 (display #\> file))
1821
1822 (define write-object (primitive-generic-generic write))
1823
1824 (define-method (write (o <object>) file)
1825 (let ((class (class-of o)))
1826 (if (slot-bound? class 'name)
1827 (begin
1828 (display "#<" file)
1829 (display (class-name class) file)
1830 (display #\space file)
1831 (display-address o file)
1832 (display #\> file))
1833 (next-method))))
1834
1835 (define-method (write (class <class>) file)
1836 (let ((meta (class-of class)))
1837 (if (and (slot-bound? class 'name)
1838 (slot-bound? meta 'name))
1839 (begin
1840 (display "#<" file)
1841 (display (class-name meta) file)
1842 (display #\space file)
1843 (display (class-name class) file)
1844 (display #\space file)
1845 (display-address class file)
1846 (display #\> file))
1847 (next-method))))
1848
1849 (define-method (write (gf <generic>) file)
1850 (let ((meta (class-of gf)))
1851 (if (and (slot-bound? meta 'name)
1852 (slot-bound? gf 'methods))
1853 (begin
1854 (display "#<" file)
1855 (display (class-name meta) file)
1856 (let ((name (generic-function-name gf)))
1857 (if name
1858 (begin
1859 (display #\space file)
1860 (display name file))))
1861 (display " (" file)
1862 (display (length (generic-function-methods gf)) file)
1863 (display ")>" file))
1864 (next-method))))
1865
1866 (define-method (write (o <method>) file)
1867 (let ((meta (class-of o)))
1868 (if (and (slot-bound? meta 'name)
1869 (slot-bound? o 'specializers))
1870 (begin
1871 (display "#<" file)
1872 (display (class-name meta) file)
1873 (display #\space file)
1874 (display (map* (lambda (spec)
1875 (if (slot-bound? spec 'name)
1876 (slot-ref spec 'name)
1877 spec))
1878 (method-specializers o))
1879 file)
1880 (display #\space file)
1881 (display-address o file)
1882 (display #\> file))
1883 (next-method))))
1884
1885 ;; Display (do the same thing as write by default)
1886 (define-method (display o file)
1887 (write-object o file))
1888
1889 ;;;
1890 ;;; Handling of duplicate bindings in the module system
1891 ;;;
1892
1893 (define (find-subclass super name)
1894 (let lp ((classes (class-direct-subclasses super)))
1895 (cond
1896 ((null? classes)
1897 (error "class not found" name))
1898 ((and (slot-bound? (car classes) 'name)
1899 (eq? (class-name (car classes)) name))
1900 (car classes))
1901 (else
1902 (lp (cdr classes))))))
1903
1904 ;; A record type.
1905 (define <module> (find-subclass <top> '<module>))
1906
1907 (define-method (merge-generics (module <module>)
1908 (name <symbol>)
1909 (int1 <module>)
1910 (val1 <top>)
1911 (int2 <module>)
1912 (val2 <top>)
1913 (var <top>)
1914 (val <top>))
1915 #f)
1916
1917 (define-method (merge-generics (module <module>)
1918 (name <symbol>)
1919 (int1 <module>)
1920 (val1 <generic>)
1921 (int2 <module>)
1922 (val2 <generic>)
1923 (var <top>)
1924 (val <boolean>))
1925 (and (not (eq? val1 val2))
1926 (make-variable (make-extended-generic (list val2 val1) name))))
1927
1928 (define-method (merge-generics (module <module>)
1929 (name <symbol>)
1930 (int1 <module>)
1931 (val1 <generic>)
1932 (int2 <module>)
1933 (val2 <generic>)
1934 (var <top>)
1935 (gf <extended-generic>))
1936 (and (not (memq val2 (slot-ref gf 'extends)))
1937 (begin
1938 (slot-set! gf
1939 'extends
1940 (cons val2 (delq! val2 (slot-ref gf 'extends))))
1941 (slot-set! val2
1942 'extended-by
1943 (cons gf (delq! gf (slot-ref val2 'extended-by))))
1944 (invalidate-method-cache! gf)
1945 var)))
1946
1947 (module-define! duplicate-handlers 'merge-generics merge-generics)
1948
1949 (define-method (merge-accessors (module <module>)
1950 (name <symbol>)
1951 (int1 <module>)
1952 (val1 <top>)
1953 (int2 <module>)
1954 (val2 <top>)
1955 (var <top>)
1956 (val <top>))
1957 #f)
1958
1959 (define-method (merge-accessors (module <module>)
1960 (name <symbol>)
1961 (int1 <module>)
1962 (val1 <accessor>)
1963 (int2 <module>)
1964 (val2 <accessor>)
1965 (var <top>)
1966 (val <top>))
1967 (merge-generics module name int1 val1 int2 val2 var val))
1968
1969 (module-define! duplicate-handlers 'merge-accessors merge-accessors)
1970
1971 ;;;
1972 ;;; slot access
1973 ;;;
1974
1975 (define (class-slot-g-n-s class slot-name)
1976 (let* ((this-slot (assq slot-name (struct-ref class class-index-slots)))
1977 (getters-n-setters (struct-ref class class-index-getters-n-setters))
1978 (g-n-s (cddr (or (assq slot-name getters-n-setters)
1979 (slot-missing class slot-name)))))
1980 (if (not (memq (slot-definition-allocation this-slot)
1981 '(#:class #:each-subclass)))
1982 (slot-missing class slot-name))
1983 g-n-s))
1984
1985 (define (class-slot-ref class slot)
1986 (let ((x ((car (class-slot-g-n-s class slot)) #f)))
1987 (if (unbound? x)
1988 (slot-unbound class slot)
1989 x)))
1990
1991 (define (class-slot-set! class slot value)
1992 ((cadr (class-slot-g-n-s class slot)) #f value))
1993
1994 (define-method (slot-unbound (c <class>) (o <object>) s)
1995 (goops-error "Slot `~S' is unbound in object ~S" s o))
1996
1997 (define-method (slot-unbound (c <class>) s)
1998 (goops-error "Slot `~S' is unbound in class ~S" s c))
1999
2000 (define-method (slot-unbound (o <object>))
2001 (goops-error "Unbound slot in object ~S" o))
2002
2003 (define-method (slot-missing (c <class>) (o <object>) s)
2004 (goops-error "No slot with name `~S' in object ~S" s o))
2005
2006 (define-method (slot-missing (c <class>) s)
2007 (goops-error "No class slot with name `~S' in class ~S" s c))
2008
2009
2010 (define-method (slot-missing (c <class>) (o <object>) s value)
2011 (slot-missing c o s))
2012
2013 ;;; Methods for the possible error we can encounter when calling a gf
2014
2015 (define-method (no-next-method (gf <generic>) args)
2016 (goops-error "No next method when calling ~S\nwith arguments ~S" gf args))
2017
2018 (define-method (no-applicable-method (gf <generic>) args)
2019 (goops-error "No applicable method for ~S in call ~S"
2020 gf (cons (generic-function-name gf) args)))
2021
2022 (define-method (no-method (gf <generic>) args)
2023 (goops-error "No method defined for ~S" gf))
2024
2025 ;;;
2026 ;;; {Cloning functions (from rdeline@CS.CMU.EDU)}
2027 ;;;
2028
2029 (define-method (shallow-clone (self <object>))
2030 (let ((clone (%allocate-instance (class-of self) '()))
2031 (slots (map slot-definition-name
2032 (class-slots (class-of self)))))
2033 (for-each (lambda (slot)
2034 (if (slot-bound? self slot)
2035 (slot-set! clone slot (slot-ref self slot))))
2036 slots)
2037 clone))
2038
2039 (define-method (deep-clone (self <object>))
2040 (let ((clone (%allocate-instance (class-of self) '()))
2041 (slots (map slot-definition-name
2042 (class-slots (class-of self)))))
2043 (for-each (lambda (slot)
2044 (if (slot-bound? self slot)
2045 (slot-set! clone slot
2046 (let ((value (slot-ref self slot)))
2047 (if (instance? value)
2048 (deep-clone value)
2049 value)))))
2050 slots)
2051 clone))
2052
2053 ;;;
2054 ;;; {Class redefinition utilities}
2055 ;;;
2056
2057 ;;; (class-redefinition OLD NEW)
2058 ;;;
2059
2060 ;;; Has correct the following conditions:
2061
2062 ;;; Methods
2063 ;;;
2064 ;;; 1. New accessor specializers refer to new header
2065 ;;;
2066 ;;; Classes
2067 ;;;
2068 ;;; 1. New class cpl refers to the new class header
2069 ;;; 2. Old class header exists on old super classes direct-subclass lists
2070 ;;; 3. New class header exists on new super classes direct-subclass lists
2071
2072 (define-method (class-redefinition (old <class>) (new <class>))
2073 ;; Work on direct methods:
2074 ;; 1. Remove accessor methods from the old class
2075 ;; 2. Patch the occurences of new in the specializers by old
2076 ;; 3. Displace the methods from old to new
2077 (remove-class-accessors! old) ;; -1-
2078 (let ((methods (class-direct-methods new)))
2079 (for-each (lambda (m)
2080 (update-direct-method! m new old)) ;; -2-
2081 methods)
2082 (struct-set! new
2083 class-index-direct-methods
2084 (append methods (class-direct-methods old))))
2085
2086 ;; Substitute old for new in new cpl
2087 (set-car! (struct-ref new class-index-cpl) old)
2088
2089 ;; Remove the old class from the direct-subclasses list of its super classes
2090 (for-each (lambda (c) (struct-set! c class-index-direct-subclasses
2091 (delv! old (class-direct-subclasses c))))
2092 (class-direct-supers old))
2093
2094 ;; Replace the new class with the old in the direct-subclasses of the supers
2095 (for-each (lambda (c)
2096 (struct-set! c class-index-direct-subclasses
2097 (cons old (delv! new (class-direct-subclasses c)))))
2098 (class-direct-supers new))
2099
2100 ;; Swap object headers
2101 (%modify-class old new)
2102
2103 ;; Now old is NEW!
2104
2105 ;; Redefine all the subclasses of old to take into account modification
2106 (for-each
2107 (lambda (c)
2108 (update-direct-subclass! c new old))
2109 (class-direct-subclasses new))
2110
2111 ;; Invalidate class so that subsequent instances slot accesses invoke
2112 ;; change-object-class
2113 (struct-set! new class-index-redefined old)
2114 (%invalidate-class new) ;must come after slot-set!
2115
2116 old)
2117
2118 ;;;
2119 ;;; remove-class-accessors!
2120 ;;;
2121
2122 (define-method (remove-class-accessors! (c <class>))
2123 (for-each (lambda (m)
2124 (if (is-a? m <accessor-method>)
2125 (let ((gf (slot-ref m 'generic-function)))
2126 ;; remove the method from its GF
2127 (slot-set! gf 'methods
2128 (delq1! m (slot-ref gf 'methods)))
2129 (invalidate-method-cache! gf)
2130 ;; remove the method from its specializers
2131 (remove-method-in-classes! m))))
2132 (class-direct-methods c)))
2133
2134 ;;;
2135 ;;; update-direct-method!
2136 ;;;
2137
2138 (define-method (update-direct-method! (m <method>)
2139 (old <class>)
2140 (new <class>))
2141 (let loop ((l (method-specializers m)))
2142 ;; Note: the <top> in dotted list is never used.
2143 ;; So we can work as if we had only proper lists.
2144 (if (pair? l)
2145 (begin
2146 (if (eqv? (car l) old)
2147 (set-car! l new))
2148 (loop (cdr l))))))
2149
2150 ;;;
2151 ;;; update-direct-subclass!
2152 ;;;
2153
2154 (define-method (update-direct-subclass! (c <class>)
2155 (old <class>)
2156 (new <class>))
2157 (class-redefinition c
2158 (make-class (class-direct-supers c)
2159 (class-direct-slots c)
2160 #:name (class-name c)
2161 #:metaclass (class-of c))))
2162
2163 ;;;
2164 ;;; {Utilities for INITIALIZE methods}
2165 ;;;
2166
2167 ;;; compute-slot-accessors
2168 ;;;
2169 (define (compute-slot-accessors class slots)
2170 (for-each
2171 (lambda (s g-n-s)
2172 (let ((getter-function (slot-definition-getter s))
2173 (setter-function (slot-definition-setter s))
2174 (accessor (slot-definition-accessor s)))
2175 (if getter-function
2176 (add-method! getter-function
2177 (compute-getter-method class g-n-s)))
2178 (if setter-function
2179 (add-method! setter-function
2180 (compute-setter-method class g-n-s)))
2181 (if accessor
2182 (begin
2183 (add-method! accessor
2184 (compute-getter-method class g-n-s))
2185 (add-method! (setter accessor)
2186 (compute-setter-method class g-n-s))))))
2187 slots (struct-ref class class-index-getters-n-setters)))
2188
2189 (define-method (compute-getter-method (class <class>) slotdef)
2190 (let ((init-thunk (cadr slotdef))
2191 (g-n-s (cddr slotdef)))
2192 (make <accessor-method>
2193 #:specializers (list class)
2194 #:procedure (cond ((pair? g-n-s)
2195 (make-generic-bound-check-getter (car g-n-s)))
2196 (init-thunk
2197 (standard-get g-n-s))
2198 (else
2199 (bound-check-get g-n-s)))
2200 #:slot-definition slotdef)))
2201
2202 (define-method (compute-setter-method (class <class>) slotdef)
2203 (let ((g-n-s (cddr slotdef)))
2204 (make <accessor-method>
2205 #:specializers (list class <top>)
2206 #:procedure (if (pair? g-n-s)
2207 (cadr g-n-s)
2208 (standard-set g-n-s))
2209 #:slot-definition slotdef)))
2210
2211 (define (make-generic-bound-check-getter proc)
2212 (lambda (o)
2213 (let ((val (proc o)))
2214 (if (unbound? val)
2215 (slot-unbound o)
2216 val))))
2217
2218 ;;; Pre-generate getters and setters for the first 20 slots.
2219 (define-syntax define-standard-accessor-method
2220 (lambda (stx)
2221 (define num-standard-pre-cache 20)
2222 (syntax-case stx ()
2223 ((_ ((proc n) arg ...) body)
2224 #`(define proc
2225 (let ((cache (vector #,@(map (lambda (n*)
2226 #`(lambda (arg ...)
2227 (let ((n #,n*))
2228 body)))
2229 (iota num-standard-pre-cache)))))
2230 (lambda (n)
2231 (if (< n #,num-standard-pre-cache)
2232 (vector-ref cache n)
2233 (lambda (arg ...) body)))))))))
2234
2235 (define-standard-accessor-method ((bound-check-get n) o)
2236 (let ((x (struct-ref o n)))
2237 (if (unbound? x)
2238 (slot-unbound o)
2239 x)))
2240
2241 (define-standard-accessor-method ((standard-get n) o)
2242 (struct-ref o n))
2243
2244 (define-standard-accessor-method ((standard-set n) o v)
2245 (struct-set! o n v))
2246
2247 ;;; compute-getters-n-setters
2248 ;;;
2249 (define (compute-getters-n-setters class slots)
2250
2251 (define (compute-slot-init-function name s)
2252 (or (let ((thunk (slot-definition-init-thunk s)))
2253 (and thunk
2254 (if (thunk? thunk)
2255 thunk
2256 (goops-error "Bad init-thunk for slot `~S' in ~S: ~S"
2257 name class thunk))))
2258 (let ((init (slot-definition-init-value s)))
2259 (and (not (unbound? init))
2260 (lambda () init)))))
2261
2262 (define (verify-accessors slot l)
2263 (cond ((integer? l))
2264 ((not (and (list? l) (= (length l) 2)))
2265 (goops-error "Bad getter and setter for slot `~S' in ~S: ~S"
2266 slot class l))
2267 (else
2268 (let ((get (car l))
2269 (set (cadr l)))
2270 (if (not (procedure? get))
2271 (goops-error "Bad getter closure for slot `~S' in ~S: ~S"
2272 slot class get))
2273 (if (not (procedure? set))
2274 (goops-error "Bad setter closure for slot `~S' in ~S: ~S"
2275 slot class set))))))
2276
2277 (map (lambda (s)
2278 ;; The strange treatment of nfields is due to backward compatibility.
2279 (let* ((index (slot-ref class 'nfields))
2280 (g-n-s (compute-get-n-set class s))
2281 (size (- (slot-ref class 'nfields) index))
2282 (name (slot-definition-name s)))
2283 ;; NOTE: The following is interdependent with C macros
2284 ;; defined above goops.c:scm_sys_prep_layout_x.
2285 ;;
2286 ;; For simple instance slots, we have the simplest form
2287 ;; '(name init-function . index)
2288 ;; For other slots we have
2289 ;; '(name init-function getter setter . alloc)
2290 ;; where alloc is:
2291 ;; '(index size) for instance allocated slots
2292 ;; '() for other slots
2293 (verify-accessors name g-n-s)
2294 (case (slot-definition-allocation s)
2295 ((#:each-subclass #:class)
2296 (unless (and (zero? size) (pair? g-n-s))
2297 (error "Class-allocated slots should not reserve fields"))
2298 ;; Don't initialize the slot; that's handled when the slot
2299 ;; is allocated, in compute-get-n-set.
2300 (cons name (cons #f g-n-s)))
2301 (else
2302 (cons name
2303 (cons (compute-slot-init-function name s)
2304 (if (or (integer? g-n-s)
2305 (zero? size))
2306 g-n-s
2307 (append g-n-s (list index size)))))))))
2308 slots))
2309
2310 ;;; compute-cpl
2311 ;;;
2312
2313 ;; Replace the bootstrap compute-cpl with this definition.
2314 (define compute-cpl
2315 (make <generic> #:name 'compute-cpl))
2316
2317 (define-method (compute-cpl (class <class>))
2318 (compute-std-cpl class class-direct-supers))
2319
2320 ;;; compute-get-n-set
2321 ;;;
2322 (define-method (compute-get-n-set (class <class>) s)
2323 (define (class-slot-init-value)
2324 (let ((thunk (slot-definition-init-thunk s)))
2325 (if thunk
2326 (thunk)
2327 (slot-definition-init-value s))))
2328
2329 (case (slot-definition-allocation s)
2330 ((#:instance) ;; Instance slot
2331 ;; get-n-set is just its offset
2332 (let ((already-allocated (struct-ref class class-index-nfields)))
2333 (struct-set! class class-index-nfields (+ already-allocated 1))
2334 already-allocated))
2335
2336 ((#:class) ;; Class slot
2337 ;; Class-slots accessors are implemented as 2 closures around
2338 ;; a Scheme variable. As instance slots, class slots must be
2339 ;; unbound at init time.
2340 (let ((name (slot-definition-name s)))
2341 (if (memq name (map slot-definition-name (class-direct-slots class)))
2342 ;; This slot is direct; create a new shared variable
2343 (make-closure-variable class (class-slot-init-value))
2344 ;; Slot is inherited. Find its definition in superclass
2345 (let loop ((l (cdr (class-precedence-list class))))
2346 (let ((r (assoc name
2347 (struct-ref (car l)
2348 class-index-getters-n-setters))))
2349 (if r
2350 (cddr r)
2351 (loop (cdr l))))))))
2352
2353 ((#:each-subclass) ;; slot shared by instances of direct subclass.
2354 ;; (Thomas Buerger, April 1998)
2355 (make-closure-variable class (class-slot-init-value)))
2356
2357 ((#:virtual) ;; No allocation
2358 ;; slot-ref and slot-set! function must be given by the user
2359 (let ((get (get-keyword #:slot-ref (slot-definition-options s) #f))
2360 (set (get-keyword #:slot-set! (slot-definition-options s) #f)))
2361 (if (not (and get set))
2362 (goops-error "You must supply a #:slot-ref and a #:slot-set! in ~S"
2363 s))
2364 (list get set)))
2365 (else (next-method))))
2366
2367 (define (make-closure-variable class value)
2368 (list (lambda (o) value)
2369 (lambda (o v) (set! value v))))
2370
2371 (define-method (compute-get-n-set (o <object>) s)
2372 (goops-error "Allocation \"~S\" is unknown" (slot-definition-allocation s)))
2373
2374 (define-method (compute-slots (class <class>))
2375 (build-slots-list (class-direct-slots class)
2376 (class-precedence-list class)))
2377
2378 ;;;
2379 ;;; {Initialize}
2380 ;;;
2381
2382 (define *unbound* (make-unbound))
2383
2384 ;; FIXME: This could be much more efficient.
2385 (define (%initialize-object obj initargs)
2386 "Initialize the object @var{obj} with the given arguments
2387 var{initargs}."
2388 (unless (instance? obj)
2389 (scm-error 'wrong-type-arg #f "Not an object: ~S"
2390 (list obj) #f))
2391 (unless (even? (length initargs))
2392 (scm-error 'wrong-type-arg #f "Initargs has odd length: ~S"
2393 (list initargs) #f))
2394 (let ((class (class-of obj)))
2395 (define (get-initarg kw)
2396 (if kw
2397 (get-keyword kw initargs *unbound*)
2398 *unbound*))
2399 (let lp ((get-n-set (struct-ref class class-index-getters-n-setters))
2400 (slots (struct-ref class class-index-slots)))
2401 (match slots
2402 (() obj)
2403 (((name . options) . slots)
2404 (match get-n-set
2405 (((_ init-thunk . _) . get-n-set)
2406 (let ((initarg (get-initarg (get-keyword #:init-keyword options))))
2407 (cond
2408 ((not (unbound? initarg))
2409 (slot-set! obj name initarg))
2410 (init-thunk
2411 (slot-set! obj name (init-thunk)))))
2412 (lp get-n-set slots))))))))
2413
2414 (define-method (initialize (object <object>) initargs)
2415 (%initialize-object object initargs))
2416
2417 (define-method (initialize (class <class>) initargs)
2418 (next-method)
2419 (let ((dslots (get-keyword #:slots initargs '()))
2420 (supers (get-keyword #:dsupers initargs '())))
2421 (let ((name (get-keyword #:name initargs '???)))
2422 (struct-set! class class-index-name name))
2423 (struct-set! class class-index-direct-supers supers)
2424 (struct-set! class class-index-direct-slots dslots)
2425 (struct-set! class class-index-direct-subclasses '())
2426 (struct-set! class class-index-direct-methods '())
2427 (struct-set! class class-index-cpl (compute-cpl class))
2428 (struct-set! class class-index-redefined #f)
2429 (let ((slots (compute-slots class)))
2430 (struct-set! class class-index-slots slots)
2431 (struct-set! class class-index-nfields 0)
2432 (let ((getters-n-setters (compute-getters-n-setters class slots)))
2433 (struct-set! class class-index-getters-n-setters getters-n-setters))
2434 ;; Build getters - setters - accessors
2435 (compute-slot-accessors class slots))
2436
2437 ;; Update the "direct-subclasses" of each inherited classes
2438 (for-each (lambda (x)
2439 (let ((dsubs (struct-ref x class-index-direct-subclasses)))
2440 (struct-set! x class-index-direct-subclasses
2441 (cons class dsubs))))
2442 supers)
2443
2444 ;; Support for the underlying structs:
2445
2446 ;; Set the layout slot
2447 (%prep-layout! class)
2448 ;; Inherit class flags (invisible on scheme level) from supers
2449 (%inherit-magic! class supers)))
2450
2451 (define (initialize-object-procedure object initargs)
2452 (let ((proc (get-keyword #:procedure initargs #f)))
2453 (cond ((not proc))
2454 ((pair? proc)
2455 (apply slot-set! object 'procedure proc))
2456 (else
2457 (slot-set! object 'procedure proc)))))
2458
2459 (define-method (initialize (applicable-struct <applicable-struct>) initargs)
2460 (next-method)
2461 (initialize-object-procedure applicable-struct initargs))
2462
2463 (define-method (initialize (applicable-struct <applicable-struct-with-setter>)
2464 initargs)
2465 (next-method)
2466 (slot-set! applicable-struct 'setter (get-keyword #:setter initargs #f)))
2467
2468 (define-method (initialize (generic <generic>) initargs)
2469 (let ((previous-definition (get-keyword #:default initargs #f))
2470 (name (get-keyword #:name initargs #f)))
2471 (next-method)
2472 (slot-set! generic 'methods (if (is-a? previous-definition <procedure>)
2473 (list (method args
2474 (apply previous-definition args)))
2475 '()))
2476 (if name
2477 (set-procedure-property! generic 'name name))
2478 (invalidate-method-cache! generic)))
2479
2480 (define-method (initialize (eg <extended-generic>) initargs)
2481 (next-method)
2482 (slot-set! eg 'extends (get-keyword #:extends initargs '())))
2483
2484 (define dummy-procedure (lambda args *unspecified*))
2485
2486 (define-method (initialize (method <method>) initargs)
2487 (next-method)
2488 (slot-set! method 'generic-function (get-keyword #:generic-function initargs #f))
2489 (slot-set! method 'specializers (get-keyword #:specializers initargs '()))
2490 (slot-set! method 'procedure
2491 (get-keyword #:procedure initargs #f))
2492 (slot-set! method 'formals (get-keyword #:formals initargs '()))
2493 (slot-set! method 'body (get-keyword #:body initargs '()))
2494 (slot-set! method 'make-procedure (get-keyword #:make-procedure initargs #f)))
2495
2496
2497 ;;;
2498 ;;; {Change-class}
2499 ;;;
2500
2501 (define (change-object-class old-instance old-class new-class)
2502 (let ((new-instance (allocate-instance new-class '())))
2503 ;; Initialize the slots of the new instance
2504 (for-each (lambda (slot)
2505 (if (and (slot-exists-using-class? old-class old-instance slot)
2506 (eq? (slot-definition-allocation
2507 (class-slot-definition old-class slot))
2508 #:instance)
2509 (slot-bound-using-class? old-class old-instance slot))
2510 ;; Slot was present and allocated in old instance; copy it
2511 (slot-set-using-class!
2512 new-class
2513 new-instance
2514 slot
2515 (slot-ref-using-class old-class old-instance slot))
2516 ;; slot was absent; initialize it with its default value
2517 (let ((init (slot-init-function new-class slot)))
2518 (if init
2519 (slot-set-using-class!
2520 new-class
2521 new-instance
2522 slot
2523 (apply init '()))))))
2524 (map slot-definition-name (class-slots new-class)))
2525 ;; Exchange old and new instance in place to keep pointers valid
2526 (%modify-instance old-instance new-instance)
2527 ;; Allow class specific updates of instances (which now are swapped)
2528 (update-instance-for-different-class new-instance old-instance)
2529 old-instance))
2530
2531
2532 (define-method (update-instance-for-different-class (old-instance <object>)
2533 (new-instance
2534 <object>))
2535 ;;not really important what we do, we just need a default method
2536 new-instance)
2537
2538 (define-method (change-class (old-instance <object>) (new-class <class>))
2539 (change-object-class old-instance (class-of old-instance) new-class))
2540
2541 ;;;
2542 ;;; {make}
2543 ;;;
2544 ;;; A new definition which overwrites the previous one which was built-in
2545 ;;;
2546
2547 (define-method (allocate-instance (class <class>) initargs)
2548 (%allocate-instance class initargs))
2549
2550 (define-method (make-instance (class <class>) . initargs)
2551 (let ((instance (allocate-instance class initargs)))
2552 (initialize instance initargs)
2553 instance))
2554
2555 (define make make-instance)
2556
2557 ;;;
2558 ;;; {apply-generic}
2559 ;;;
2560 ;;; Protocol for calling standard generic functions. This protocol is
2561 ;;; not used for real <generic> functions (in this case we use a
2562 ;;; completely C hard-coded protocol). Apply-generic is used by
2563 ;;; goops for calls to subclasses of <generic> and <generic-with-setter>.
2564 ;;; The code below is similar to the first MOP described in AMOP. In
2565 ;;; particular, it doesn't used the currified approach to gf
2566 ;;; call. There are 2 reasons for that:
2567 ;;; - the protocol below is exposed to mimic completely the one written in C
2568 ;;; - the currified protocol would be imho inefficient in C.
2569 ;;;
2570
2571 (define-method (apply-generic (gf <generic>) args)
2572 (if (null? (slot-ref gf 'methods))
2573 (no-method gf args))
2574 (let ((methods (compute-applicable-methods gf args)))
2575 (if methods
2576 (apply-methods gf (sort-applicable-methods gf methods args) args)
2577 (no-applicable-method gf args))))
2578
2579 ;; compute-applicable-methods is bound to %compute-applicable-methods.
2580 ;; *fixme* use let
2581 (define %%compute-applicable-methods
2582 (make <generic> #:name 'compute-applicable-methods))
2583
2584 (define-method (%%compute-applicable-methods (gf <generic>) args)
2585 (%compute-applicable-methods gf args))
2586
2587 (set! compute-applicable-methods %%compute-applicable-methods)
2588
2589 (define-method (sort-applicable-methods (gf <generic>) methods args)
2590 (%sort-applicable-methods methods (map class-of args)))
2591
2592 (define-method (method-more-specific? (m1 <method>) (m2 <method>) targs)
2593 (%method-more-specific? m1 m2 targs))
2594
2595 (define-method (apply-method (gf <generic>) methods build-next args)
2596 (apply (method-procedure (car methods))
2597 (build-next (cdr methods) args)
2598 args))
2599
2600 (define-method (apply-methods (gf <generic>) (l <list>) args)
2601 (letrec ((next (lambda (procs args)
2602 (lambda new-args
2603 (let ((a (if (null? new-args) args new-args)))
2604 (if (null? procs)
2605 (no-next-method gf a)
2606 (apply-method gf procs next a)))))))
2607 (apply-method gf l next args)))
2608
2609 ;; We don't want the following procedure to turn up in backtraces:
2610 (for-each (lambda (proc)
2611 (set-procedure-property! proc 'system-procedure #t))
2612 (list slot-unbound
2613 slot-missing
2614 no-next-method
2615 no-applicable-method
2616 no-method
2617 ))
2618
2619 ;;;
2620 ;;; {<composite-metaclass> and <active-metaclass>}
2621 ;;;
2622
2623 ;(autoload "active-slot" <active-metaclass>)
2624 ;(autoload "composite-slot" <composite-metaclass>)
2625 ;(export <composite-metaclass> <active-metaclass>)
2626
2627 ;;;
2628 ;;; {Tools}
2629 ;;;
2630
2631 ;; list2set
2632 ;;
2633 ;; duplicate the standard list->set function but using eq instead of
2634 ;; eqv which really sucks a lot, uselessly here
2635 ;;
2636 (define (list2set l)
2637 (let loop ((l l)
2638 (res '()))
2639 (cond
2640 ((null? l) res)
2641 ((memq (car l) res) (loop (cdr l) res))
2642 (else (loop (cdr l) (cons (car l) res))))))
2643
2644 (define (class-subclasses c)
2645 (letrec ((allsubs (lambda (c)
2646 (cons c (mapappend allsubs
2647 (class-direct-subclasses c))))))
2648 (list2set (cdr (allsubs c)))))
2649
2650 (define (class-methods c)
2651 (list2set (mapappend class-direct-methods
2652 (cons c (class-subclasses c)))))
2653
2654 ;;;
2655 ;;; {Final initialization}
2656 ;;;
2657
2658 ;; Tell C code that the main bulk of Goops has been loaded
2659 (%goops-loaded)
2660
2661
2662 \f
2663
2664 ;;;
2665 ;;; {SMOB and port classes}
2666 ;;;
2667
2668 (define <arbiter> (find-subclass <top> '<arbiter>))
2669 (define <promise> (find-subclass <top> '<promise>))
2670 (define <thread> (find-subclass <top> '<thread>))
2671 (define <mutex> (find-subclass <top> '<mutex>))
2672 (define <condition-variable> (find-subclass <top> '<condition-variable>))
2673 (define <regexp> (find-subclass <top> '<regexp>))
2674 (define <hook> (find-subclass <top> '<hook>))
2675 (define <bitvector> (find-subclass <top> '<bitvector>))
2676 (define <random-state> (find-subclass <top> '<random-state>))
2677 (define <async> (find-subclass <top> '<async>))
2678 (define <directory> (find-subclass <top> '<directory>))
2679 (define <array> (find-subclass <top> '<array>))
2680 (define <character-set> (find-subclass <top> '<character-set>))
2681 (define <dynamic-object> (find-subclass <top> '<dynamic-object>))
2682 (define <guardian> (find-subclass <applicable> '<guardian>))
2683 (define <macro> (find-subclass <top> '<macro>))
2684
2685 (define (define-class-subtree class)
2686 (define! (class-name class) class)
2687 (for-each define-class-subtree (class-direct-subclasses class)))
2688
2689 (define-class-subtree (find-subclass <port> '<file-port>))