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