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