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