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