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