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