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