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