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