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