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