3 ;;;; Copyright (C) 1998,1999,2000-2003,2006,2009-2011,2013-2015 Free Software Foundation, Inc.
4 ;;;; Copyright (C) 1993-1998 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
6 ;;;; This library is free software; you can redistribute it and/or
7 ;;;; modify it under the terms of the GNU Lesser General Public
8 ;;;; License as published by the Free Software Foundation; either
9 ;;;; version 3 of the License, or (at your option) any later version.
11 ;;;; This library is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;;;; Lesser General Public License for more details.
16 ;;;; You should have received a copy of the GNU Lesser General Public
17 ;;;; License along with this library; if not, write to the Free Software
18 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 ;;;; This file was based upon stklos.stk from the STk distribution
24 ;;;; version 4.0.1 by Erick Gallesio <eg@unice.fr>.
27 (define-module (oop goops)
28 #:use-module (srfi srfi-1)
29 #:use-module (ice-9 match)
30 #:export-syntax (define-class class standard-define-class
31 define-generic define-accessor define-method
32 define-extended-generic define-extended-generics
34 #:export ( ;; The root of everything.
39 <foreign-slot> <protected-slot> <hidden-slot> <opaque-slot>
40 <read-only-slot> <self-slot> <protected-opaque-slot>
41 <protected-hidden-slot> <protected-read-only-slot>
42 <scm-slot> <int-slot> <float-slot> <double-slot>
44 ;; Methods are implementations of generic functions.
45 <method> <accessor-method>
47 ;; Applicable objects, either procedures or applicable structs.
48 <procedure-class> <applicable>
49 <procedure> <primitive-generic>
51 ;; Applicable structs.
52 <applicable-struct-class>
54 <generic> <extended-generic>
55 <generic-with-setter> <extended-generic-with-setter>
56 <accessor> <extended-accessor>
58 ;; Types with their own allocated typecodes.
59 <boolean> <char> <list> <pair> <null> <string> <symbol>
60 <vector> <bytevector> <uvec> <foreign> <hashtable>
61 <fluid> <dynamic-state> <frame> <vm> <vm-continuation>
65 <number> <complex> <real> <integer> <fraction>
70 ;; Particular SMOB data types. All SMOB types have
71 ;; corresponding classes, which may be obtained via class-of,
72 ;; once you have an instance. Perhaps FIXME to provide a
73 ;; smob-type-name->class procedure.
74 <arbiter> <promise> <thread> <mutex> <condition-variable>
75 <regexp> <hook> <bitvector> <random-state> <async>
76 <directory> <array> <character-set>
77 <dynamic-object> <guardian> <macro>
83 <port> <input-port> <output-port> <input-output-port>
85 ;; Like SMOB types, all port types have their own classes,
86 ;; which can be accessed via `class-of' once you have an
87 ;; instance. Here we export bindings just for file ports.
89 <file-input-port> <file-output-port> <file-input-output-port>
92 ensure-metaclass ensure-metaclass-with-supers
94 make-generic ensure-generic
96 make-accessor ensure-accessor
98 class-slot-ref class-slot-set! slot-unbound slot-missing
99 slot-definition-name slot-definition-options
100 slot-definition-allocation
102 slot-definition-getter slot-definition-setter
103 slot-definition-accessor
104 slot-definition-init-value slot-definition-init-form
105 slot-definition-init-thunk slot-definition-init-keyword
106 slot-init-function class-slot-definition
108 compute-cpl compute-std-cpl compute-get-n-set compute-slots
109 compute-getter-method compute-setter-method
110 allocate-instance initialize make-instance make
111 no-next-method no-applicable-method no-method
112 change-class update-instance-for-different-class
113 shallow-clone deep-clone
115 apply-generic apply-method apply-methods
116 compute-applicable-methods %compute-applicable-methods
117 method-more-specific? sort-applicable-methods
118 class-subclasses class-methods
120 min-fixnum max-fixnum
122 ;;; *fixme* Should go into goops.c
123 instance? slot-ref-using-class
124 slot-set-using-class! slot-bound-using-class?
125 slot-exists-using-class? slot-ref slot-set! slot-bound?
126 class-name class-direct-supers class-direct-subclasses
127 class-direct-methods class-direct-slots class-precedence-list
129 generic-function-name
130 generic-function-methods method-generic-function
131 method-specializers method-formals
132 primitive-generic-generic enable-primitive-generic!
133 method-procedure accessor-method-slot-definition
134 slot-exists? make find-method get-keyword)
137 (define *goops-module* (current-module))
139 (eval-when (compile load eval)
140 ;;; The standard class precedence list computation algorithm
142 ;;; Correct behaviour:
144 ;;; (define-class food ())
145 ;;; (define-class fruit (food))
146 ;;; (define-class spice (food))
147 ;;; (define-class apple (fruit))
148 ;;; (define-class cinnamon (spice))
149 ;;; (define-class pie (apple cinnamon))
150 ;;; => cpl (pie) = pie apple fruit cinnamon spice food object top
152 ;;; (define-class d ())
153 ;;; (define-class e ())
154 ;;; (define-class f ())
155 ;;; (define-class b (d e))
156 ;;; (define-class c (e f))
157 ;;; (define-class a (b c))
158 ;;; => cpl (a) = a b d c e f object top
161 (define (compute-std-cpl c get-direct-supers)
162 (define (only-non-null lst)
163 (filter (lambda (l) (not (null? l))) lst))
165 (define (merge-lists reversed-partial-result inputs)
167 ((every null? inputs)
168 (reverse! reversed-partial-result))
170 (let* ((candidate (lambda (c)
171 (and (not (any (lambda (l)
175 (candidate-car (lambda (l)
177 (candidate (car l)))))
178 (next (any candidate-car inputs)))
180 (goops-error "merge-lists: Inconsistent precedence graph"))
181 (let ((remove-next (lambda (l)
182 (if (eq? (car l) next)
185 (merge-lists (cons next reversed-partial-result)
186 (only-non-null (map remove-next inputs))))))))
187 (let ((c-direct-supers (get-direct-supers c)))
188 (merge-lists (list c)
189 (only-non-null (append (map class-precedence-list
191 (list c-direct-supers))))))
193 ;; Bootstrap version.
194 (define (compute-cpl class)
195 (compute-std-cpl class class-direct-supers)))
197 ;; XXX FIXME: figure out why the 'eval-when's in this file must use
198 ;; 'compile' and must avoid 'expand', but only in 2.2, and only when
199 ;; compiling something that imports goops, e.g. (ice-9 occam-channel),
200 ;; before (oop goops) itself has been compiled.
202 ;; First initialize the builtin part of GOOPS
203 (eval-when (compile load eval)
204 (load-extension (string-append "libguile-" (effective-version))
205 "scm_init_goops_builtins"))
207 (eval-when (compile load eval)
208 (use-modules ((language tree-il primitives) :select (add-interesting-primitive!)))
209 (add-interesting-primitive! 'class-of))
211 ;; During boot, the specialized slot classes aren't defined yet, so we
212 ;; initialize <class> with unspecialized slots.
213 (define-syntax-rule (build-<class>-slots specialized?)
214 (let-syntax ((unspecialized-slot (syntax-rules ()
215 ((_ name) (list 'name))))
216 (specialized-slot (syntax-rules ()
219 (list 'name #:class class)
221 (list (specialized-slot layout <protected-read-only-slot>)
222 (specialized-slot flags <hidden-slot>)
223 (specialized-slot self <self-slot>)
224 (specialized-slot instance-finalizer <hidden-slot>)
225 (unspecialized-slot print)
226 (specialized-slot name <protected-hidden-slot>)
227 (specialized-slot reserved-0 <hidden-slot>)
228 (specialized-slot reserved-1 <hidden-slot>)
229 (unspecialized-slot redefined)
230 (specialized-slot h0 <int-slot>)
231 (specialized-slot h1 <int-slot>)
232 (specialized-slot h2 <int-slot>)
233 (specialized-slot h3 <int-slot>)
234 (specialized-slot h4 <int-slot>)
235 (specialized-slot h5 <int-slot>)
236 (specialized-slot h6 <int-slot>)
237 (specialized-slot h7 <int-slot>)
238 (unspecialized-slot direct-supers)
239 (unspecialized-slot direct-slots)
240 (unspecialized-slot direct-subclasses)
241 (unspecialized-slot direct-methods)
242 (unspecialized-slot cpl)
243 (unspecialized-slot default-slot-definition-class)
244 (unspecialized-slot slots)
245 (unspecialized-slot getters-n-setters)
246 (unspecialized-slot nfields))))
248 (eval-when (compile load eval)
249 (define (build-slots-list dslots cpl)
250 (define (check-cpl slots class-slots)
251 (when (or-map (lambda (slot-def) (assq (car slot-def) slots))
253 (scm-error 'misc-error #f
254 "a predefined <class> inherited field cannot be redefined"
256 (define (remove-duplicate-slots slots)
257 (let lp ((slots (reverse slots)) (res '()) (seen '()))
260 ((memq (caar slots) seen)
261 (lp (cdr slots) res seen))
263 (lp (cdr slots) (cons (car slots) res) (cons (caar slots) seen))))))
264 (let* ((class-slots (and (memq <class> cpl) (slot-ref <class> 'slots))))
266 (check-cpl dslots class-slots))
267 (let lp ((cpl (cdr cpl)) (res dslots) (class-slots '()))
269 (remove-duplicate-slots (append class-slots res))
270 (let* ((head (car cpl))
272 (new-slots (slot-ref head 'direct-slots)))
275 (lp cpl (append new-slots res) class-slots))
277 ;; Move class slots to the head of the list.
278 (lp cpl res new-slots))
280 (check-cpl new-slots class-slots)
281 (lp cpl (append new-slots res) class-slots))))))))
283 (define (%compute-getters-n-setters slots)
284 (define (compute-init-thunk options)
286 ((kw-arg-ref options #:init-value) => (lambda (val) (lambda () val)))
287 ((kw-arg-ref options #:init-thunk))
289 (let lp ((slots slots) (n 0))
292 (((name . options) . slots)
293 (cons (cons name (cons (compute-init-thunk options) n))
294 (lp slots (1+ n)))))))
296 (define (%compute-layout slots getters-n-setters nfields is-class?)
297 (define (instance-allocated? g-n-s)
299 ((name init-thunk . (? exact-integer? index)) #t)
300 ((name init-thunk getter setter index size) #t)
303 (define (allocated-index g-n-s)
305 ((name init-thunk . (? exact-integer? index)) index)
306 ((name init-thunk getter setter index size) index)))
308 (define (allocated-size g-n-s)
310 ((name init-thunk . (? exact-integer? index)) 1)
311 ((name init-thunk getter setter index size) size)))
313 (define (slot-protection-and-kind options)
314 (define (subclass? class parent)
315 (memq parent (class-precedence-list class)))
316 (let ((type (kw-arg-ref options #:class)))
317 (if (and type (subclass? type <foreign-slot>))
319 ((subclass? type <self-slot>) #\s)
320 ((subclass? type <protected-slot>) #\p)
323 ((subclass? type <opaque-slot>) #\o)
324 ((subclass? type <read-only-slot>) #\r)
325 ((subclass? type <hidden-slot>) #\h)
329 (let ((layout (make-string (* nfields 2))))
330 (let lp ((n 0) (slots slots) (getters-n-setters getters-n-setters))
331 (match getters-n-setters
333 (unless (= n nfields) (error "bad nfields"))
334 (unless (null? slots) (error "inconsistent g-n-s/slots"))
336 (let ((class-layout (symbol->string (slot-ref <class> 'layout))))
337 (unless (string-prefix? class-layout layout)
338 (error "bad layout for class"))))
340 ((g-n-s . getters-n-setters)
342 (((name . options) . slots)
344 ((instance-allocated? g-n-s)
345 (unless (< n nfields) (error "bad nfields"))
346 (unless (= n (allocated-index g-n-s)) (error "bad allocation"))
347 (call-with-values (lambda () (slot-protection-and-kind options))
348 (lambda (protection kind)
349 (let init ((n n) (size (allocated-size g-n-s)))
351 ((zero? size) (lp n slots getters-n-setters))
353 (string-set! layout (* n 2) protection)
354 (string-set! layout (1+ (* n 2)) kind)
355 (init (1+ n) (1- size))))))))
357 (lp n slots getters-n-setters))))))))))
359 (define (%prep-layout! class)
360 (let* ((is-class? (and (memq <class> (slot-ref class 'cpl)) #t))
361 (layout (%compute-layout (slot-ref class 'slots)
362 (slot-ref class 'getters-n-setters)
363 (slot-ref class 'nfields)
365 (%init-layout! class layout)))
367 (define (make-standard-class class name dsupers dslots)
368 (let ((z (make-struct/no-tail class)))
369 (slot-set! z 'direct-supers dsupers)
370 (let* ((cpl (compute-cpl z))
371 (dslots (map (lambda (slot)
372 (if (pair? slot) slot (list slot)))
374 (slots (build-slots-list dslots cpl))
375 (nfields (length slots))
376 (g-n-s (%compute-getters-n-setters slots)))
377 (slot-set! z 'name name)
378 (slot-set! z 'direct-slots dslots)
379 (slot-set! z 'direct-subclasses '())
380 (slot-set! z 'direct-methods '())
381 (slot-set! z 'cpl cpl)
382 (slot-set! z 'slots slots)
383 (slot-set! z 'nfields nfields)
384 (slot-set! z 'getters-n-setters g-n-s)
385 (slot-set! z 'redefined #f)
386 (for-each (lambda (super)
387 (let ((subclasses (slot-ref super 'direct-subclasses)))
388 (slot-set! super 'direct-subclasses (cons z subclasses))))
391 (%inherit-magic! z dsupers)
395 (let ((dslots (build-<class>-slots #f)))
396 (%make-root-class '<class> dslots (%compute-getters-n-setters dslots))))
398 (define-syntax define-standard-class
400 ((define-standard-class name (super ...) #:metaclass meta slot ...)
402 (make-standard-class meta 'name (list super ...) '(slot ...))))
403 ((define-standard-class name (super ...) slot ...)
404 (define-standard-class name (super ...) #:metaclass <class> slot ...))))
406 (define-standard-class <top> ())
407 (define-standard-class <object> (<top>))
409 ;; <top>, <object>, and <class> were partially initialized. Correct
411 (slot-set! <object> 'direct-subclasses (list <class>))
412 (slot-set! <class> 'direct-supers (list <object>))
413 (slot-set! <class> 'cpl (list <class> <object> <top>))
415 (define-standard-class <foreign-slot> (<top>))
416 (define-standard-class <protected-slot> (<foreign-slot>))
417 (define-standard-class <hidden-slot> (<foreign-slot>))
418 (define-standard-class <opaque-slot> (<foreign-slot>))
419 (define-standard-class <read-only-slot> (<foreign-slot>))
420 (define-standard-class <self-slot> (<read-only-slot>))
421 (define-standard-class <protected-opaque-slot> (<protected-slot>
423 (define-standard-class <protected-hidden-slot> (<protected-slot>
425 (define-standard-class <protected-read-only-slot> (<protected-slot>
427 (define-standard-class <scm-slot> (<protected-slot>))
428 (define-standard-class <int-slot> (<foreign-slot>))
429 (define-standard-class <float-slot> (<foreign-slot>))
430 (define-standard-class <double-slot> (<foreign-slot>))
432 ;; Finish initialization of <class>.
433 (let ((dslots (build-<class>-slots #t)))
434 (slot-set! <class> 'direct-slots dslots)
435 (slot-set! <class> 'slots dslots)
436 (slot-set! <class> 'getters-n-setters (%compute-getters-n-setters dslots)))
438 ;; Applicables and their classes.
439 (define-standard-class <procedure-class> (<class>))
440 (define-standard-class <applicable-struct-class> (<procedure-class>))
441 (%bless-applicable-struct-vtable! <applicable-struct-class>)
442 (define-standard-class <method> (<object>)
449 (define-standard-class <accessor-method> (<method>)
450 (slot-definition #:init-keyword #:slot-definition))
451 (define-standard-class <applicable> (<top>))
452 (define-standard-class <applicable-struct> (<object> <applicable>)
453 #:metaclass <applicable-struct-class>
455 (define-standard-class <generic> (<applicable-struct>)
456 #:metaclass <applicable-struct-class>
458 (n-specialized #:init-value 0)
459 (extended-by #:init-value ())
461 (%bless-pure-generic-vtable! <generic>)
462 (define-standard-class <extended-generic> (<generic>)
463 #:metaclass <applicable-struct-class>
464 (extends #:init-value ()))
465 (%bless-pure-generic-vtable! <extended-generic>)
466 (define-standard-class <generic-with-setter> (<generic>)
467 #:metaclass <applicable-struct-class>
469 (%bless-pure-generic-vtable! <generic-with-setter>)
470 (define-standard-class <accessor> (<generic-with-setter>)
471 #:metaclass <applicable-struct-class>)
472 (%bless-pure-generic-vtable! <accessor>)
473 (define-standard-class <extended-generic-with-setter> (<extended-generic>
474 <generic-with-setter>)
475 #:metaclass <applicable-struct-class>)
476 (%bless-pure-generic-vtable! <extended-generic-with-setter>)
477 (define-standard-class <extended-accessor> (<accessor>
478 <extended-generic-with-setter>)
479 #:metaclass <applicable-struct-class>)
480 (%bless-pure-generic-vtable! <extended-accessor>)
482 ;; Primitive types classes
483 (define-standard-class <boolean> (<top>))
484 (define-standard-class <char> (<top>))
485 (define-standard-class <list> (<top>))
486 ;; Not all pairs are lists, but there is code out there that relies on
487 ;; (is-a? '(1 2 3) <list>) to work. Terrible. How to fix?
488 (define-standard-class <pair> (<list>))
489 (define-standard-class <null> (<list>))
490 (define-standard-class <string> (<top>))
491 (define-standard-class <symbol> (<top>))
492 (define-standard-class <vector> (<top>))
493 (define-standard-class <foreign> (<top>))
494 (define-standard-class <hashtable> (<top>))
495 (define-standard-class <fluid> (<top>))
496 (define-standard-class <dynamic-state> (<top>))
497 (define-standard-class <frame> (<top>))
498 (define-standard-class <vm-continuation> (<top>))
499 (define-standard-class <bytevector> (<top>))
500 (define-standard-class <uvec> (<bytevector>))
501 (define-standard-class <array> (<top>))
502 (define-standard-class <bitvector> (<top>))
503 (define-standard-class <number> (<top>))
504 (define-standard-class <complex> (<number>))
505 (define-standard-class <real> (<complex>))
506 (define-standard-class <integer> (<real>))
507 (define-standard-class <fraction> (<real>))
508 (define-standard-class <keyword> (<top>))
509 (define-standard-class <unknown> (<top>))
510 (define-standard-class <procedure> (<applicable>)
511 #:metaclass <procedure-class>)
512 (define-standard-class <primitive-generic> (<procedure>)
513 #:metaclass <procedure-class>)
514 (define-standard-class <port> (<top>))
515 (define-standard-class <input-port> (<port>))
516 (define-standard-class <output-port> (<port>))
517 (define-standard-class <input-output-port> (<input-port> <output-port>))
520 (eval-when (compile load eval)
523 ;; Then load the rest of GOOPS
524 (use-modules (oop goops util)
530 (eval-when (compile load eval)
531 (define min-fixnum (- (expt 2 29)))
532 (define max-fixnum (- (expt 2 29) 1)))
537 (define (goops-error format-string . args)
538 (scm-error 'goops-error #f format-string args '()))
543 (define (is-a? obj class)
544 (and (memq class (class-precedence-list (class-of obj))) #t))
551 (define ensure-metaclass-with-supers
552 (let ((table-of-metas '()))
553 (lambda (meta-supers)
554 (let ((entry (assoc meta-supers table-of-metas)))
556 ;; Found a previously created metaclass
558 ;; Create a new meta-class which inherit from "meta-supers"
559 (let ((new (make <class> #:dsupers meta-supers
561 #:name (gensym "metaclass"))))
562 (set! table-of-metas (cons (cons meta-supers new) table-of-metas))
565 (define (ensure-metaclass supers)
568 (let* ((all-metas (map (lambda (x) (class-of x)) supers))
569 (all-cpls (append-map (lambda (m)
570 (cdr (class-precedence-list m)))
573 ;; Find the most specific metaclasses. The new metaclass will be
574 ;; a subclass of these.
577 (if (and (not (member meta all-cpls))
578 (not (member meta needed-metas)))
579 (set! needed-metas (append needed-metas (list meta)))))
581 ;; Now return a subclass of the metaclasses we found.
582 (if (null? (cdr needed-metas))
583 (car needed-metas) ; If there's only one, just use it.
584 (ensure-metaclass-with-supers needed-metas)))))
590 ;;; (define-class NAME (SUPER ...) SLOT-DEFINITION ... OPTION ...)
592 ;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
593 ;;; OPTION ::= KEYWORD VALUE
596 (define (kw-do-map mapper f kwargs)
600 ((or (null? (cdr l)) (not (keyword? (car l))))
601 (goops-error "malformed keyword arguments: ~a" kwargs))
602 (else (cons (car l) (keywords (cddr l))))))
604 (if (null? l) '() (cons (cadr l) (args (cddr l)))))
605 ;; let* to check keywords first
606 (let* ((k (keywords kwargs))
610 (define (make-class supers slots . options)
611 (let* ((name (get-keyword #:name options (make-unbound)))
612 (supers (if (not (or-map (lambda (class)
614 (class-precedence-list class)))
616 (append supers (list <object>))
618 (metaclass (or (get-keyword #:metaclass options #f)
619 (ensure-metaclass supers))))
621 ;; Verify that all direct slots are different and that we don't inherit
622 ;; several time from the same class
623 (let ((tmp1 (find-duplicate supers))
624 (tmp2 (find-duplicate (map slot-definition-name slots))))
626 (goops-error "make-class: super class ~S is duplicate in class ~S"
629 (goops-error "make-class: slot ~S is duplicate in class ~S"
632 ;; Everything seems correct, build the class
633 (apply make metaclass
639 ;;; (class (SUPER ...) SLOT-DEFINITION ... OPTION ...)
641 ;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
642 ;;; OPTION ::= KEYWORD VALUE
644 (define-macro (class supers . slots)
645 (define (make-slot-definition-forms slots)
651 ,@(kw-do-map append-map
656 #:init-thunk (lambda () ,arg)))
657 (else (list kw arg))))
662 (if (not (list? supers))
663 (goops-error "malformed superclass list: ~S" supers))
664 (let ((slots (take-while (lambda (x) (not (keyword? x))) slots))
665 (options (or (find-tail keyword? slots) '())))
667 ;; evaluate super class variables
669 ;; evaluate slot definitions, except the slot name!
670 (list ,@(make-slot-definition-forms slots))
671 ;; evaluate class options
674 (define-syntax define-class-pre-definition
677 ((_ (k arg rest ...) out ...)
678 (keyword? (syntax->datum #'k))
679 (case (syntax->datum #'k)
681 #'(define-class-pre-definition (rest ...)
683 (if (or (not (defined? 'arg))
684 (not (is-a? arg <generic>)))
687 (ensure-generic (if (defined? 'arg) arg #f) 'arg)))))
689 #'(define-class-pre-definition (rest ...)
691 (if (or (not (defined? 'arg))
692 (not (is-a? arg <accessor>)))
695 (ensure-accessor (if (defined? 'arg) arg #f) 'arg)))))
697 #'(define-class-pre-definition (rest ...) out ...))))
699 #'(begin out ...)))))
701 ;; Some slot options require extra definitions to be made. In
702 ;; particular, we want to make sure that the generic function objects
703 ;; which represent accessors exist before `make-class' tries to add
705 (define-syntax define-class-pre-definitions
710 ((_ (slot rest ...) out ...)
711 (keyword? (syntax->datum #'slot))
713 ((_ (slot rest ...) out ...)
715 #'(define-class-pre-definitions (rest ...)
717 ((_ ((slotname slotopt ...) rest ...) out ...)
718 #'(define-class-pre-definitions (rest ...)
719 out ... (define-class-pre-definition (slotopt ...)))))))
721 (define-syntax-rule (define-class name supers slot ...)
723 (define-class-pre-definitions (slot ...))
724 (if (and (defined? 'name)
726 (memq <object> (class-precedence-list name)))
727 (class-redefinition name
728 (class supers slot ... #:name 'name))
729 (toplevel-define! 'name (class supers slot ... #:name 'name)))))
731 (define-syntax-rule (standard-define-class arg ...)
732 (define-class arg ...))
735 ;;; {Generic functions and accessors}
738 ;; Apparently the desired semantics are that we extend previous
739 ;; procedural definitions, but that if `name' was already a generic, we
740 ;; overwrite its definition.
741 (define-macro (define-generic name)
742 (if (not (symbol? name))
743 (goops-error "bad generic function name: ~S" name))
745 (if (and (defined? ',name) (is-a? ,name <generic>))
746 (make <generic> #:name ',name)
747 (ensure-generic (if (defined? ',name) ,name #f) ',name))))
749 (define-macro (define-extended-generic name val)
750 (if (not (symbol? name))
751 (goops-error "bad generic function name: ~S" name))
752 `(define ,name (make-extended-generic ,val ',name)))
754 (define-macro (define-extended-generics names . args)
755 (let ((prefixes (get-keyword #:prefix args #f)))
758 ,@(map (lambda (name)
759 `(define-extended-generic ,name
760 (list ,@(map (lambda (prefix)
761 (symbol-append prefix name))
764 (goops-error "no prefixes supplied"))))
766 (define* (make-generic #:optional name)
767 (make <generic> #:name name))
769 (define* (make-extended-generic gfs #:optional name)
770 (let* ((gfs (if (list? gfs) gfs (list gfs)))
771 (gws? (any (lambda (gf) (is-a? gf <generic-with-setter>)) gfs)))
773 (let* ((sname (and name (make-setter-name name)))
775 (append-map (lambda (gf)
776 (if (is-a? gf <generic-with-setter>)
777 (list (ensure-generic (setter gf)
781 (es (make <extended-generic-with-setter>
784 #:setter (make <extended-generic>
786 #:extends setters))))
787 (extended-by! setters (setter es))
789 (make <extended-generic>
792 (extended-by! gfs ans)
795 (define (extended-by! gfs eg)
796 (for-each (lambda (gf)
797 (slot-set! gf 'extended-by
798 (cons eg (slot-ref gf 'extended-by))))
800 (invalidate-method-cache! eg))
802 (define (not-extended-by! gfs eg)
803 (for-each (lambda (gf)
804 (slot-set! gf 'extended-by
805 (delq! eg (slot-ref gf 'extended-by))))
807 (invalidate-method-cache! eg))
809 (define* (ensure-generic old-definition #:optional name)
810 (cond ((is-a? old-definition <generic>) old-definition)
811 ((procedure-with-setter? old-definition)
812 (make <generic-with-setter>
814 #:default (procedure old-definition)
815 #:setter (setter old-definition)))
816 ((procedure? old-definition)
817 (if (generic-capability? old-definition) old-definition
818 (make <generic> #:name name #:default old-definition)))
819 (else (make <generic> #:name name))))
821 ;; same semantics as <generic>
822 (define-syntax-rule (define-accessor name)
824 (cond ((not (defined? 'name)) (ensure-accessor #f 'name))
825 ((is-a? name <accessor>) (make <accessor> #:name 'name))
826 (else (ensure-accessor name 'name)))))
828 (define (make-setter-name name)
829 (string->symbol (string-append "setter:" (symbol->string name))))
831 (define* (make-accessor #:optional name)
834 #:setter (make <generic>
835 #:name (and name (make-setter-name name)))))
837 (define* (ensure-accessor proc #:optional name)
838 (cond ((and (is-a? proc <accessor>)
839 (is-a? (setter proc) <generic>))
841 ((is-a? proc <generic-with-setter>)
842 (upgrade-accessor proc (setter proc)))
843 ((is-a? proc <generic>)
844 (upgrade-accessor proc (make-generic name)))
845 ((procedure-with-setter? proc)
848 #:default (procedure proc)
849 #:setter (ensure-generic (setter proc) name)))
851 (ensure-accessor (if (generic-capability? proc)
852 (make <generic> #:name name #:default proc)
853 (ensure-generic proc name))
856 (make-accessor name))))
858 (define (upgrade-accessor generic setter)
859 (let ((methods (slot-ref generic 'methods))
860 (gws (make (if (is-a? generic <extended-generic>)
861 <extended-generic-with-setter>
863 #:name (generic-function-name generic)
864 #:extended-by (slot-ref generic 'extended-by)
866 (if (is-a? generic <extended-generic>)
867 (let ((gfs (slot-ref generic 'extends)))
868 (not-extended-by! gfs generic)
869 (slot-set! gws 'extends gfs)
870 (extended-by! gfs gws)))
872 (for-each (lambda (method)
873 (slot-set! method 'generic-function gws))
875 (slot-set! gws 'methods methods)
876 (invalidate-method-cache! gws)
883 ;; Note: `a' and `b' can have unequal lengths (i.e. one can be one
884 ;; element longer than the other when we have a dotted parameter
885 ;; list). For instance, with the call
891 ;; (define-method M (a . l) ....)
892 ;; (define-method M (a) ....)
894 ;; we consider that the second method is more specific.
896 ;; Precondition: `a' and `b' are methods and are applicable to `types'.
897 (define (%method-more-specific? a b types)
898 (let lp ((a-specializers (method-specializers a))
899 (b-specializers (method-specializers b))
902 ;; (a) less specific than (a b ...) or (a . b)
903 ((null? a-specializers) #t)
904 ;; (a b ...) or (a . b) less specific than (a)
905 ((null? b-specializers) #f)
906 ;; (a . b) less specific than (a b ...)
907 ((not (pair? a-specializers)) #f)
908 ;; (a b ...) more specific than (a . b)
909 ((not (pair? b-specializers)) #t)
911 (let ((a-specializer (car a-specializers))
912 (b-specializer (car b-specializers))
913 (a-specializers (cdr a-specializers))
914 (b-specializers (cdr b-specializers))
917 (if (eq? a-specializer b-specializer)
918 (lp a-specializers b-specializers types)
919 (let lp ((cpl (class-precedence-list type)))
920 (let ((elt (car cpl)))
922 ((eq? a-specializer elt) #t)
923 ((eq? b-specializer elt) #f)
924 (else (lp (cdr cpl))))))))))))
926 (define (%sort-applicable-methods methods types)
927 (sort methods (lambda (a b) (%method-more-specific? a b types))))
929 (define (%compute-applicable-methods gf args)
930 (define (method-applicable? m types)
931 (let lp ((specs (method-specializers m)) (types types))
933 ((null? specs) (null? types))
934 ((not (pair? specs)) #t)
937 (and (memq (car specs) (class-precedence-list (car types)))
938 (lp (cdr specs) (cdr types)))))))
939 (let ((n (length args))
940 (types (map class-of args)))
941 (let lp ((methods (generic-function-methods gf))
944 (and (not (null? applicable))
945 (%sort-applicable-methods applicable types))
946 (let ((m (car methods)))
948 (if (method-applicable? m types)
952 (define compute-applicable-methods %compute-applicable-methods)
954 (define (toplevel-define! name val)
955 (module-define! (current-module) name val))
957 (define-syntax define-method
958 (syntax-rules (setter)
959 ((_ ((setter name) . args) body ...)
961 (if (or (not (defined? 'name))
962 (not (is-a? name <accessor>)))
963 (toplevel-define! 'name
965 (if (defined? 'name) name #f) 'name)))
966 (add-method! (setter name) (method args body ...))))
967 ((_ (name . args) body ...)
969 ;; FIXME: this code is how it always was, but it's quite cracky:
970 ;; it will only define the generic function if it was undefined
971 ;; before (ok), or *was defined to #f*. The latter is crack. But
972 ;; there are bootstrap issues about fixing this -- change it to
973 ;; (is-a? name <generic>) and see.
974 (if (or (not (defined? 'name))
976 (toplevel-define! 'name (make <generic> #:name 'name)))
977 (add-method! name (method args body ...))))))
979 (define-syntax method
981 (define (parse-args args)
982 (let lp ((ls args) (formals '()) (specializers '()))
985 (and (identifier? #'f) (identifier? #'s))
988 (cons #'s specializers)))
993 (cons #'<top> specializers)))
995 (list (reverse formals)
996 (reverse (cons #''() specializers))))
999 (list (append (reverse formals) #'tail)
1000 (reverse (cons #'<top> specializers)))))))
1002 (define (find-free-id exp referent)
1005 (or (find-free-id #'x referent)
1006 (find-free-id #'y referent)))
1009 (let ((id (datum->syntax #'x referent)))
1010 (and (free-identifier=? #'x id) id)))
1013 (define (compute-procedure formals body)
1014 (syntax-case body ()
1016 (with-syntax ((formals formals))
1017 #'(lambda formals body0 ...)))))
1019 (define (->proper args)
1020 (let lp ((ls args) (out '()))
1022 ((x . xs) (lp #'xs (cons #'x out)))
1024 (tail (reverse (cons #'tail out))))))
1026 (define (compute-make-procedure formals body next-method)
1027 (syntax-case body ()
1029 (with-syntax ((next-method next-method))
1030 (syntax-case formals ()
1032 #'(lambda (real-next-method)
1033 (lambda (formal ...)
1034 (let ((next-method (lambda args
1036 (real-next-method formal ...)
1037 (apply real-next-method args)))))
1040 (with-syntax (((formal ...) (->proper #'formals)))
1041 #'(lambda (real-next-method)
1043 (let ((next-method (lambda args
1045 (apply real-next-method formal ...)
1046 (apply real-next-method args)))))
1049 (define (compute-procedures formals body)
1050 ;; So, our use of this is broken, because it operates on the
1051 ;; pre-expansion source code. It's equivalent to just searching
1052 ;; for referent in the datums. Ah well.
1053 (let ((id (find-free-id body 'next-method)))
1055 ;; return a make-procedure
1057 (compute-make-procedure formals body id))
1058 (values (compute-procedure formals body)
1062 ((_ args) #'(method args (if #f #f)))
1063 ((_ args body0 body1 ...)
1064 (with-syntax (((formals (specializer ...)) (parse-args #'args)))
1067 (compute-procedures #'formals #'(body0 body1 ...)))
1068 (lambda (procedure make-procedure)
1069 (with-syntax ((procedure procedure)
1070 (make-procedure make-procedure))
1072 #:specializers (cons* specializer ...)
1074 #:body '(body0 body1 ...)
1075 #:make-procedure make-procedure
1076 #:procedure procedure)))))))))
1082 (define (add-method-in-classes! m)
1083 ;; Add method in all the classes which appears in its specializers list
1084 (for-each* (lambda (x)
1085 (let ((dm (class-direct-methods x)))
1086 (if (not (memq m dm))
1087 (slot-set! x 'direct-methods (cons m dm)))))
1088 (method-specializers m)))
1090 (define (remove-method-in-classes! m)
1091 ;; Remove method in all the classes which appears in its specializers list
1092 (for-each* (lambda (x)
1095 (delv! m (class-direct-methods x))))
1096 (method-specializers m)))
1098 (define (compute-new-list-of-methods gf new)
1099 (let ((new-spec (method-specializers new))
1100 (methods (slot-ref gf 'methods)))
1101 (let loop ((l methods))
1104 (if (equal? (method-specializers (car l)) new-spec)
1106 ;; This spec. list already exists. Remove old method from dependents
1107 (remove-method-in-classes! (car l))
1112 (define (method-n-specializers m)
1113 (length* (slot-ref m 'specializers)))
1115 (define (calculate-n-specialized gf)
1116 (fold (lambda (m n) (max n (method-n-specializers m)))
1118 (generic-function-methods gf)))
1120 (define (invalidate-method-cache! gf)
1121 (%invalidate-method-cache! gf)
1122 (slot-set! gf 'n-specialized (calculate-n-specialized gf))
1123 (for-each (lambda (gf) (invalidate-method-cache! gf))
1124 (slot-ref gf 'extended-by)))
1126 (define internal-add-method!
1127 (method ((gf <generic>) (m <method>))
1128 (slot-set! m 'generic-function gf)
1129 (slot-set! gf 'methods (compute-new-list-of-methods gf m))
1130 (invalidate-method-cache! gf)
1131 (add-method-in-classes! m)
1134 (define-generic add-method!)
1136 ((method-procedure internal-add-method!) add-method! internal-add-method!)
1138 (define-method (add-method! (proc <procedure>) (m <method>))
1139 (if (generic-capability? proc)
1141 (enable-primitive-generic! proc)
1142 (add-method! proc m))
1145 (define-method (add-method! (pg <primitive-generic>) (m <method>))
1146 (add-method! (primitive-generic-generic pg) m))
1148 (define-method (add-method! obj (m <method>))
1149 (goops-error "~S is not a valid generic function" obj))
1152 ;;; {Access to meta objects}
1158 (define-method (method-source (m <method>))
1159 (let* ((spec (map* class-name (slot-ref m 'specializers)))
1160 (src (procedure-source (slot-ref m 'procedure))))
1162 (let ((args (cadr src))
1165 (cons (map* list args spec)
1168 (define-method (method-formals (m <method>))
1169 (slot-ref m 'formals))
1174 (define slot-definition-name car)
1176 (define slot-definition-options cdr)
1178 (define (slot-definition-allocation s)
1179 (get-keyword #:allocation (cdr s) #:instance))
1181 (define (slot-definition-getter s)
1182 (get-keyword #:getter (cdr s) #f))
1184 (define (slot-definition-setter s)
1185 (get-keyword #:setter (cdr s) #f))
1187 (define (slot-definition-accessor s)
1188 (get-keyword #:accessor (cdr s) #f))
1190 (define (slot-definition-init-value s)
1191 ;; can be #f, so we can't use #f as non-value
1192 (get-keyword #:init-value (cdr s) (make-unbound)))
1194 (define (slot-definition-init-form s)
1195 (get-keyword #:init-form (cdr s) (make-unbound)))
1197 (define (slot-definition-init-thunk s)
1198 (get-keyword #:init-thunk (cdr s) #f))
1200 (define (slot-definition-init-keyword s)
1201 (get-keyword #:init-keyword (cdr s) #f))
1203 (define (class-slot-definition class slot-name)
1204 (assq slot-name (class-slots class)))
1206 (define (slot-init-function class slot-name)
1207 (cadr (assq slot-name (slot-ref class 'getters-n-setters))))
1209 (define (accessor-method-slot-definition obj)
1210 "Return the slot definition of the accessor @var{obj}."
1211 (slot-ref obj 'slot-definition))
1215 ;;; {Standard methods used by the C runtime}
1218 ;;; Methods to compare objects
1221 ;; Have to do this in a strange order because equal? is used in the
1222 ;; add-method! implementation; we need to make sure that when the
1223 ;; primitive is extended, that the generic has a method. =
1224 (define g-equal? (make-generic 'equal?))
1225 ;; When this generic gets called, we will have already checked eq? and
1226 ;; eqv? -- the purpose of this generic is to extend equality. So by
1227 ;; default, there is no extension, thus the #f return.
1228 (add-method! g-equal? (method (x y) #f))
1229 (set-primitive-generic! equal? g-equal?)
1232 ;;; methods to display/write an object
1235 ; Code for writing objects must test that the slots they use are
1236 ; bound. Otherwise a slot-unbound method will be called and will
1237 ; conduct to an infinite loop.
1240 (define (display-address o file)
1241 (display (number->string (object-address o) 16) file))
1243 (define-method (write o file)
1244 (display "#<instance " file)
1245 (display-address o file)
1248 (define write-object (primitive-generic-generic write))
1250 (define-method (write (o <object>) file)
1251 (let ((class (class-of o)))
1252 (if (slot-bound? class 'name)
1255 (display (class-name class) file)
1256 (display #\space file)
1257 (display-address o file)
1261 (define-method (write (class <class>) file)
1262 (let ((meta (class-of class)))
1263 (if (and (slot-bound? class 'name)
1264 (slot-bound? meta 'name))
1267 (display (class-name meta) file)
1268 (display #\space file)
1269 (display (class-name class) file)
1270 (display #\space file)
1271 (display-address class file)
1275 (define-method (write (gf <generic>) file)
1276 (let ((meta (class-of gf)))
1277 (if (and (slot-bound? meta 'name)
1278 (slot-bound? gf 'methods))
1281 (display (class-name meta) file)
1282 (let ((name (generic-function-name gf)))
1285 (display #\space file)
1286 (display name file))))
1288 (display (length (generic-function-methods gf)) file)
1289 (display ")>" file))
1292 (define-method (write (o <method>) file)
1293 (let ((meta (class-of o)))
1294 (if (and (slot-bound? meta 'name)
1295 (slot-bound? o 'specializers))
1298 (display (class-name meta) file)
1299 (display #\space file)
1300 (display (map* (lambda (spec)
1301 (if (slot-bound? spec 'name)
1302 (slot-ref spec 'name)
1304 (method-specializers o))
1306 (display #\space file)
1307 (display-address o file)
1311 ;; Display (do the same thing as write by default)
1312 (define-method (display o file)
1313 (write-object o file))
1316 ;;; Handling of duplicate bindings in the module system
1319 (define (find-subclass super name)
1320 (let lp ((classes (class-direct-subclasses super)))
1323 (error "class not found" name))
1324 ((and (slot-bound? (car classes) 'name)
1325 (eq? (class-name (car classes)) name))
1328 (lp (cdr classes))))))
1331 (define <module> (find-subclass <top> '<module>))
1333 (define-method (merge-generics (module <module>)
1343 (define-method (merge-generics (module <module>)
1351 (and (not (eq? val1 val2))
1352 (make-variable (make-extended-generic (list val2 val1) name))))
1354 (define-method (merge-generics (module <module>)
1361 (gf <extended-generic>))
1362 (and (not (memq val2 (slot-ref gf 'extends)))
1366 (cons val2 (delq! val2 (slot-ref gf 'extends))))
1369 (cons gf (delq! gf (slot-ref val2 'extended-by))))
1370 (invalidate-method-cache! gf)
1373 (module-define! duplicate-handlers 'merge-generics merge-generics)
1375 (define-method (merge-accessors (module <module>)
1385 (define-method (merge-accessors (module <module>)
1393 (merge-generics module name int1 val1 int2 val2 var val))
1395 (module-define! duplicate-handlers 'merge-accessors merge-accessors)
1401 (define (class-slot-g-n-s class slot-name)
1402 (let* ((this-slot (assq slot-name (slot-ref class 'slots)))
1403 (g-n-s (cddr (or (assq slot-name (slot-ref class 'getters-n-setters))
1404 (slot-missing class slot-name)))))
1405 (if (not (memq (slot-definition-allocation this-slot)
1406 '(#:class #:each-subclass)))
1407 (slot-missing class slot-name))
1410 (define (class-slot-ref class slot)
1411 (let ((x ((car (class-slot-g-n-s class slot)) #f)))
1413 (slot-unbound class slot)
1416 (define (class-slot-set! class slot value)
1417 ((cadr (class-slot-g-n-s class slot)) #f value))
1419 (define-method (slot-unbound (c <class>) (o <object>) s)
1420 (goops-error "Slot `~S' is unbound in object ~S" s o))
1422 (define-method (slot-unbound (c <class>) s)
1423 (goops-error "Slot `~S' is unbound in class ~S" s c))
1425 (define-method (slot-unbound (o <object>))
1426 (goops-error "Unbound slot in object ~S" o))
1428 (define-method (slot-missing (c <class>) (o <object>) s)
1429 (goops-error "No slot with name `~S' in object ~S" s o))
1431 (define-method (slot-missing (c <class>) s)
1432 (goops-error "No class slot with name `~S' in class ~S" s c))
1435 (define-method (slot-missing (c <class>) (o <object>) s value)
1436 (slot-missing c o s))
1438 ;;; Methods for the possible error we can encounter when calling a gf
1440 (define-method (no-next-method (gf <generic>) args)
1441 (goops-error "No next method when calling ~S\nwith arguments ~S" gf args))
1443 (define-method (no-applicable-method (gf <generic>) args)
1444 (goops-error "No applicable method for ~S in call ~S"
1445 gf (cons (generic-function-name gf) args)))
1447 (define-method (no-method (gf <generic>) args)
1448 (goops-error "No method defined for ~S" gf))
1451 ;;; {Cloning functions (from rdeline@CS.CMU.EDU)}
1454 (define-method (shallow-clone (self <object>))
1455 (let ((clone (%allocate-instance (class-of self) '()))
1456 (slots (map slot-definition-name
1457 (class-slots (class-of self)))))
1458 (for-each (lambda (slot)
1459 (if (slot-bound? self slot)
1460 (slot-set! clone slot (slot-ref self slot))))
1464 (define-method (deep-clone (self <object>))
1465 (let ((clone (%allocate-instance (class-of self) '()))
1466 (slots (map slot-definition-name
1467 (class-slots (class-of self)))))
1468 (for-each (lambda (slot)
1469 (if (slot-bound? self slot)
1470 (slot-set! clone slot
1471 (let ((value (slot-ref self slot)))
1472 (if (instance? value)
1479 ;;; {Class redefinition utilities}
1482 ;;; (class-redefinition OLD NEW)
1485 ;;; Has correct the following conditions:
1489 ;;; 1. New accessor specializers refer to new header
1493 ;;; 1. New class cpl refers to the new class header
1494 ;;; 2. Old class header exists on old super classes direct-subclass lists
1495 ;;; 3. New class header exists on new super classes direct-subclass lists
1497 (define-method (class-redefinition (old <class>) (new <class>))
1498 ;; Work on direct methods:
1499 ;; 1. Remove accessor methods from the old class
1500 ;; 2. Patch the occurences of new in the specializers by old
1501 ;; 3. Displace the methods from old to new
1502 (remove-class-accessors! old) ;; -1-
1503 (let ((methods (class-direct-methods new)))
1504 (for-each (lambda (m)
1505 (update-direct-method! m new old)) ;; -2-
1509 (append methods (class-direct-methods old))))
1511 ;; Substitute old for new in new cpl
1512 (set-car! (slot-ref new 'cpl) old)
1514 ;; Remove the old class from the direct-subclasses list of its super classes
1515 (for-each (lambda (c) (slot-set! c 'direct-subclasses
1516 (delv! old (class-direct-subclasses c))))
1517 (class-direct-supers old))
1519 ;; Replace the new class with the old in the direct-subclasses of the supers
1520 (for-each (lambda (c)
1521 (slot-set! c 'direct-subclasses
1522 (cons old (delv! new (class-direct-subclasses c)))))
1523 (class-direct-supers new))
1525 ;; Swap object headers
1526 (%modify-class old new)
1530 ;; Redefine all the subclasses of old to take into account modification
1533 (update-direct-subclass! c new old))
1534 (class-direct-subclasses new))
1536 ;; Invalidate class so that subsequent instances slot accesses invoke
1537 ;; change-object-class
1538 (slot-set! new 'redefined old)
1539 (%invalidate-class new) ;must come after slot-set!
1544 ;;; remove-class-accessors!
1547 (define-method (remove-class-accessors! (c <class>))
1548 (for-each (lambda (m)
1549 (if (is-a? m <accessor-method>)
1550 (let ((gf (slot-ref m 'generic-function)))
1551 ;; remove the method from its GF
1552 (slot-set! gf 'methods
1553 (delq1! m (slot-ref gf 'methods)))
1554 (invalidate-method-cache! gf)
1555 ;; remove the method from its specializers
1556 (remove-method-in-classes! m))))
1557 (class-direct-methods c)))
1560 ;;; update-direct-method!
1563 (define-method (update-direct-method! (m <method>)
1566 (let loop ((l (method-specializers m)))
1567 ;; Note: the <top> in dotted list is never used.
1568 ;; So we can work as if we had only proper lists.
1571 (if (eqv? (car l) old)
1576 ;;; update-direct-subclass!
1579 (define-method (update-direct-subclass! (c <class>)
1582 (class-redefinition c
1583 (make-class (class-direct-supers c)
1584 (class-direct-slots c)
1585 #:name (class-name c)
1586 #:metaclass (class-of c))))
1589 ;;; {Utilities for INITIALIZE methods}
1592 ;;; compute-slot-accessors
1594 (define (compute-slot-accessors class slots)
1597 (let ((getter-function (slot-definition-getter s))
1598 (setter-function (slot-definition-setter s))
1599 (accessor (slot-definition-accessor s)))
1601 (add-method! getter-function
1602 (compute-getter-method class g-n-s)))
1604 (add-method! setter-function
1605 (compute-setter-method class g-n-s)))
1608 (add-method! accessor
1609 (compute-getter-method class g-n-s))
1610 (add-method! (setter accessor)
1611 (compute-setter-method class g-n-s))))))
1612 slots (slot-ref class 'getters-n-setters)))
1614 (define-method (compute-getter-method (class <class>) slotdef)
1615 (let ((init-thunk (cadr slotdef))
1616 (g-n-s (cddr slotdef)))
1617 (make <accessor-method>
1618 #:specializers (list class)
1619 #:procedure (cond ((pair? g-n-s)
1620 (make-generic-bound-check-getter (car g-n-s)))
1622 (standard-get g-n-s))
1624 (bound-check-get g-n-s)))
1625 #:slot-definition slotdef)))
1627 (define-method (compute-setter-method (class <class>) slotdef)
1628 (let ((g-n-s (cddr slotdef)))
1629 (make <accessor-method>
1630 #:specializers (list class <top>)
1631 #:procedure (if (pair? g-n-s)
1633 (standard-set g-n-s))
1634 #:slot-definition slotdef)))
1636 (define (make-generic-bound-check-getter proc)
1637 (lambda (o) (assert-bound (proc o) o)))
1639 ;;; Pre-generate getters and setters for the first 20 slots.
1640 (define-syntax define-standard-accessor-method
1642 (define num-standard-pre-cache 20)
1644 ((_ ((proc n) arg ...) body)
1646 (let ((cache (vector #,@(map (lambda (n*)
1650 (iota num-standard-pre-cache)))))
1652 (if (< n #,num-standard-pre-cache)
1653 (vector-ref cache n)
1654 (lambda (arg ...) body)))))))))
1656 (define-standard-accessor-method ((bound-check-get n) o)
1657 (let ((x (struct-ref o n)))
1662 (define-standard-accessor-method ((standard-get n) o)
1665 (define-standard-accessor-method ((standard-set n) o v)
1666 (struct-set! o n v))
1668 ;;; compute-getters-n-setters
1670 (define (compute-getters-n-setters class slots)
1672 (define (compute-slot-init-function name s)
1673 (or (let ((thunk (slot-definition-init-thunk s)))
1677 (goops-error "Bad init-thunk for slot `~S' in ~S: ~S"
1678 name class thunk))))
1679 (let ((init (slot-definition-init-value s)))
1680 (and (not (unbound? init))
1681 (lambda () init)))))
1683 (define (verify-accessors slot l)
1684 (cond ((integer? l))
1685 ((not (and (list? l) (= (length l) 2)))
1686 (goops-error "Bad getter and setter for slot `~S' in ~S: ~S"
1691 (if (not (procedure? get))
1692 (goops-error "Bad getter closure for slot `~S' in ~S: ~S"
1694 (if (not (procedure? set))
1695 (goops-error "Bad setter closure for slot `~S' in ~S: ~S"
1696 slot class set))))))
1699 ;; The strange treatment of nfields is due to backward compatibility.
1700 (let* ((index (slot-ref class 'nfields))
1701 (g-n-s (compute-get-n-set class s))
1702 (size (- (slot-ref class 'nfields) index))
1703 (name (slot-definition-name s)))
1704 ;; NOTE: The following is interdependent with C macros
1705 ;; defined above goops.c:scm_sys_prep_layout_x.
1707 ;; For simple instance slots, we have the simplest form
1708 ;; '(name init-function . index)
1709 ;; For other slots we have
1710 ;; '(name init-function getter setter . alloc)
1712 ;; '(index size) for instance allocated slots
1713 ;; '() for other slots
1714 (verify-accessors name g-n-s)
1715 (case (slot-definition-allocation s)
1716 ((#:each-subclass #:class)
1717 (unless (and (zero? size) (pair? g-n-s))
1718 (error "Class-allocated slots should not reserve fields"))
1719 ;; Don't initialize the slot; that's handled when the slot
1720 ;; is allocated, in compute-get-n-set.
1721 (cons name (cons #f g-n-s)))
1724 (cons (compute-slot-init-function name s)
1725 (if (or (integer? g-n-s)
1728 (append g-n-s (list index size)))))))))
1734 ;; Replace the bootstrap compute-cpl with this definition.
1736 (make <generic> #:name 'compute-cpl))
1738 (define-method (compute-cpl (class <class>))
1739 (compute-std-cpl class class-direct-supers))
1741 ;;; compute-get-n-set
1743 (define-method (compute-get-n-set (class <class>) s)
1744 (define (class-slot-init-value)
1745 (let ((thunk (slot-definition-init-thunk s)))
1748 (slot-definition-init-value s))))
1750 (case (slot-definition-allocation s)
1751 ((#:instance) ;; Instance slot
1752 ;; get-n-set is just its offset
1753 (let ((already-allocated (slot-ref class 'nfields)))
1754 (slot-set! class 'nfields (+ already-allocated 1))
1757 ((#:class) ;; Class slot
1758 ;; Class-slots accessors are implemented as 2 closures around
1759 ;; a Scheme variable. As instance slots, class slots must be
1760 ;; unbound at init time.
1761 (let ((name (slot-definition-name s)))
1762 (if (memq name (map slot-definition-name (class-direct-slots class)))
1763 ;; This slot is direct; create a new shared variable
1764 (make-closure-variable class (class-slot-init-value))
1765 ;; Slot is inherited. Find its definition in superclass
1766 (let loop ((l (cdr (class-precedence-list class))))
1767 (let ((r (assoc name (slot-ref (car l) 'getters-n-setters))))
1770 (loop (cdr l))))))))
1772 ((#:each-subclass) ;; slot shared by instances of direct subclass.
1773 ;; (Thomas Buerger, April 1998)
1774 (make-closure-variable class (class-slot-init-value)))
1776 ((#:virtual) ;; No allocation
1777 ;; slot-ref and slot-set! function must be given by the user
1778 (let ((get (get-keyword #:slot-ref (slot-definition-options s) #f))
1779 (set (get-keyword #:slot-set! (slot-definition-options s) #f)))
1780 (if (not (and get set))
1781 (goops-error "You must supply a #:slot-ref and a #:slot-set! in ~S"
1784 (else (next-method))))
1786 (define (make-closure-variable class value)
1787 (list (lambda (o) value)
1788 (lambda (o v) (set! value v))))
1790 (define-method (compute-get-n-set (o <object>) s)
1791 (goops-error "Allocation \"~S\" is unknown" (slot-definition-allocation s)))
1793 (define-method (compute-slots (class <class>))
1794 (build-slots-list (class-direct-slots class)
1795 (class-precedence-list class)))
1801 (define-method (initialize (object <object>) initargs)
1802 (%initialize-object object initargs))
1804 (define-method (initialize (class <class>) initargs)
1806 (let ((dslots (get-keyword #:slots initargs '()))
1807 (supers (get-keyword #:dsupers initargs '())))
1808 (slot-set! class 'name (get-keyword #:name initargs '???))
1809 (slot-set! class 'direct-supers supers)
1810 (slot-set! class 'direct-slots dslots)
1811 (slot-set! class 'direct-subclasses '())
1812 (slot-set! class 'direct-methods '())
1813 (slot-set! class 'cpl (compute-cpl class))
1814 (slot-set! class 'redefined #f)
1815 (let ((slots (compute-slots class)))
1816 (slot-set! class 'slots slots)
1817 (slot-set! class 'nfields 0)
1818 (slot-set! class 'getters-n-setters (compute-getters-n-setters class
1820 ;; Build getters - setters - accessors
1821 (compute-slot-accessors class slots))
1823 ;; Update the "direct-subclasses" of each inherited classes
1824 (for-each (lambda (x)
1827 (cons class (slot-ref x 'direct-subclasses))))
1830 ;; Support for the underlying structs:
1832 ;; Set the layout slot
1833 (%prep-layout! class)
1834 ;; Inherit class flags (invisible on scheme level) from supers
1835 (%inherit-magic! class supers)))
1837 (define (initialize-object-procedure object initargs)
1838 (let ((proc (get-keyword #:procedure initargs #f)))
1841 (apply slot-set! object 'procedure proc))
1843 (slot-set! object 'procedure proc)))))
1845 (define-method (initialize (applicable-struct <applicable-struct>) initargs)
1847 (initialize-object-procedure applicable-struct initargs))
1849 (define-method (initialize (generic <generic>) initargs)
1850 (let ((previous-definition (get-keyword #:default initargs #f))
1851 (name (get-keyword #:name initargs #f)))
1853 (slot-set! generic 'methods (if (is-a? previous-definition <procedure>)
1855 (apply previous-definition args)))
1858 (set-procedure-property! generic 'name name))
1861 (define-method (initialize (gws <generic-with-setter>) initargs)
1863 (%set-object-setter! gws (get-keyword #:setter initargs #f)))
1865 (define-method (initialize (eg <extended-generic>) initargs)
1867 (slot-set! eg 'extends (get-keyword #:extends initargs '())))
1869 (define dummy-procedure (lambda args *unspecified*))
1871 (define-method (initialize (method <method>) initargs)
1873 (slot-set! method 'generic-function (get-keyword #:generic-function initargs #f))
1874 (slot-set! method 'specializers (get-keyword #:specializers initargs '()))
1875 (slot-set! method 'procedure
1876 (get-keyword #:procedure initargs #f))
1877 (slot-set! method 'formals (get-keyword #:formals initargs '()))
1878 (slot-set! method 'body (get-keyword #:body initargs '()))
1879 (slot-set! method 'make-procedure (get-keyword #:make-procedure initargs #f)))
1886 (define (change-object-class old-instance old-class new-class)
1887 (let ((new-instance (allocate-instance new-class '())))
1888 ;; Initialize the slots of the new instance
1889 (for-each (lambda (slot)
1890 (if (and (slot-exists-using-class? old-class old-instance slot)
1891 (eq? (slot-definition-allocation
1892 (class-slot-definition old-class slot))
1894 (slot-bound-using-class? old-class old-instance slot))
1895 ;; Slot was present and allocated in old instance; copy it
1896 (slot-set-using-class!
1900 (slot-ref-using-class old-class old-instance slot))
1901 ;; slot was absent; initialize it with its default value
1902 (let ((init (slot-init-function new-class slot)))
1904 (slot-set-using-class!
1908 (apply init '()))))))
1909 (map slot-definition-name (class-slots new-class)))
1910 ;; Exchange old and new instance in place to keep pointers valid
1911 (%modify-instance old-instance new-instance)
1912 ;; Allow class specific updates of instances (which now are swapped)
1913 (update-instance-for-different-class new-instance old-instance)
1917 (define-method (update-instance-for-different-class (old-instance <object>)
1920 ;;not really important what we do, we just need a default method
1923 (define-method (change-class (old-instance <object>) (new-class <class>))
1924 (change-object-class old-instance (class-of old-instance) new-class))
1929 ;;; A new definition which overwrites the previous one which was built-in
1932 (define-method (allocate-instance (class <class>) initargs)
1933 (%allocate-instance class initargs))
1935 (define-method (make-instance (class <class>) . initargs)
1936 (let ((instance (allocate-instance class initargs)))
1937 (initialize instance initargs)
1940 (define make make-instance)
1945 ;;; Protocol for calling standard generic functions. This protocol is
1946 ;;; not used for real <generic> functions (in this case we use a
1947 ;;; completely C hard-coded protocol). Apply-generic is used by
1948 ;;; goops for calls to subclasses of <generic> and <generic-with-setter>.
1949 ;;; The code below is similar to the first MOP described in AMOP. In
1950 ;;; particular, it doesn't used the currified approach to gf
1951 ;;; call. There are 2 reasons for that:
1952 ;;; - the protocol below is exposed to mimic completely the one written in C
1953 ;;; - the currified protocol would be imho inefficient in C.
1956 (define-method (apply-generic (gf <generic>) args)
1957 (if (null? (slot-ref gf 'methods))
1958 (no-method gf args))
1959 (let ((methods (compute-applicable-methods gf args)))
1961 (apply-methods gf (sort-applicable-methods gf methods args) args)
1962 (no-applicable-method gf args))))
1964 ;; compute-applicable-methods is bound to %compute-applicable-methods.
1966 (define %%compute-applicable-methods
1967 (make <generic> #:name 'compute-applicable-methods))
1969 (define-method (%%compute-applicable-methods (gf <generic>) args)
1970 (%compute-applicable-methods gf args))
1972 (set! compute-applicable-methods %%compute-applicable-methods)
1974 (define-method (sort-applicable-methods (gf <generic>) methods args)
1975 (%sort-applicable-methods methods (map class-of args)))
1977 (define-method (method-more-specific? (m1 <method>) (m2 <method>) targs)
1978 (%method-more-specific? m1 m2 targs))
1980 (define-method (apply-method (gf <generic>) methods build-next args)
1981 (apply (method-procedure (car methods))
1982 (build-next (cdr methods) args)
1985 (define-method (apply-methods (gf <generic>) (l <list>) args)
1986 (letrec ((next (lambda (procs args)
1988 (let ((a (if (null? new-args) args new-args)))
1990 (no-next-method gf a)
1991 (apply-method gf procs next a)))))))
1992 (apply-method gf l next args)))
1994 ;; We don't want the following procedure to turn up in backtraces:
1995 (for-each (lambda (proc)
1996 (set-procedure-property! proc 'system-procedure #t))
2000 no-applicable-method
2005 ;;; {<composite-metaclass> and <active-metaclass>}
2008 ;(autoload "active-slot" <active-metaclass>)
2009 ;(autoload "composite-slot" <composite-metaclass>)
2010 ;(export <composite-metaclass> <active-metaclass>)
2018 ;; duplicate the standard list->set function but using eq instead of
2019 ;; eqv which really sucks a lot, uselessly here
2021 (define (list2set l)
2026 ((memq (car l) res) (loop (cdr l) res))
2027 (else (loop (cdr l) (cons (car l) res))))))
2029 (define (class-subclasses c)
2030 (letrec ((allsubs (lambda (c)
2031 (cons c (mapappend allsubs
2032 (class-direct-subclasses c))))))
2033 (list2set (cdr (allsubs c)))))
2035 (define (class-methods c)
2036 (list2set (mapappend class-direct-methods
2037 (cons c (class-subclasses c)))))
2040 ;;; {Final initialization}
2043 ;; Tell C code that the main bulk of Goops has been loaded
2050 ;;; {SMOB and port classes}
2053 (define <arbiter> (find-subclass <top> '<arbiter>))
2054 (define <promise> (find-subclass <top> '<promise>))
2055 (define <thread> (find-subclass <top> '<thread>))
2056 (define <mutex> (find-subclass <top> '<mutex>))
2057 (define <condition-variable> (find-subclass <top> '<condition-variable>))
2058 (define <regexp> (find-subclass <top> '<regexp>))
2059 (define <hook> (find-subclass <top> '<hook>))
2060 (define <bitvector> (find-subclass <top> '<bitvector>))
2061 (define <random-state> (find-subclass <top> '<random-state>))
2062 (define <async> (find-subclass <top> '<async>))
2063 (define <directory> (find-subclass <top> '<directory>))
2064 (define <array> (find-subclass <top> '<array>))
2065 (define <character-set> (find-subclass <top> '<character-set>))
2066 (define <dynamic-object> (find-subclass <top> '<dynamic-object>))
2067 (define <guardian> (find-subclass <applicable> '<guardian>))
2068 (define <macro> (find-subclass <top> '<macro>))
2070 (define (define-class-subtree class)
2071 (define! (class-name class) class)
2072 (for-each define-class-subtree (class-direct-subclasses class)))
2074 (define-class-subtree (find-subclass <port> '<file-port>))