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