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