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