Merge commit 'cdcba5b2f6270de808e51b3b933374170611b91d'
[bpt/guile.git] / module / oop / goops.scm
1 ;;; installed-scm-file
2
3 ;;;; Copyright (C) 1998,1999,2000,2001,2002, 2003, 2006, 2009, 2010, 2011, 2013, 2014, 2015 Free Software Foundation, Inc.
4 ;;;; Copyright (C) 1993-1998 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
5 ;;;;
6 ;;;; This library is free software; you can redistribute it and/or
7 ;;;; modify it under the terms of the GNU Lesser General Public
8 ;;;; License as published by the Free Software Foundation; either
9 ;;;; version 3 of the License, or (at your option) any later version.
10 ;;;;
11 ;;;; This library is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;;;; Lesser General Public License for more details.
15 ;;;;
16 ;;;; You should have received a copy of the GNU Lesser General Public
17 ;;;; License along with this library; if not, write to the Free Software
18 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 ;;;;
20 \f
21
22 ;;;;
23 ;;;; This file was based upon stklos.stk from the STk distribution
24 ;;;; version 4.0.1 by Erick Gallesio <eg@unice.fr>.
25 ;;;;
26
27 (define-module (oop goops)
28 #:use-module (srfi srfi-1)
29 #:export-syntax (define-class class standard-define-class
30 define-generic define-accessor define-method
31 define-extended-generic define-extended-generics
32 method)
33 #:export ( ;; The root of everything.
34 <top>
35 <class> <object>
36
37 ;; Slot types.
38 <foreign-slot> <protected-slot> <hidden-slot> <opaque-slot>
39 <read-only-slot> <self-slot> <protected-opaque-slot>
40 <protected-hidden-slot> <protected-read-only-slot>
41 <scm-slot> <int-slot> <float-slot> <double-slot>
42
43 ;; Methods are implementations of generic functions.
44 <method> <accessor-method>
45
46 ;; Applicable objects, either procedures or applicable structs.
47 <procedure-class> <applicable>
48 <procedure> <primitive-generic>
49
50 ;; Applicable structs.
51 <applicable-struct-class>
52 <applicable-struct>
53 <generic> <extended-generic>
54 <generic-with-setter> <extended-generic-with-setter>
55 <accessor> <extended-accessor>
56
57 ;; Types with their own allocated typecodes.
58 <boolean> <char> <list> <pair> <null> <string> <symbol>
59 <vector> <bytevector> <uvec> <foreign> <hashtable>
60 <fluid> <dynamic-state> <frame> <vm> <vm-continuation>
61 <keyword>
62
63 ;; Numbers.
64 <number> <complex> <real> <integer> <fraction>
65
66 ;; Unknown.
67 <unknown>
68
69 ;; Particular SMOB data types. All SMOB types have
70 ;; corresponding classes, which may be obtained via class-of,
71 ;; once you have an instance. Perhaps FIXME to provide a
72 ;; smob-type-name->class procedure.
73 <arbiter> <promise> <thread> <mutex> <condition-variable>
74 <regexp> <hook> <bitvector> <random-state> <async>
75 <directory> <array> <character-set>
76 <dynamic-object> <guardian> <macro>
77
78 ;; Modules.
79 <module>
80
81 ;; Ports.
82 <port> <input-port> <output-port> <input-output-port>
83
84 ;; Like SMOB types, all port types have their own classes,
85 ;; which can be accessed via `class-of' once you have an
86 ;; instance. Here we export bindings just for file ports.
87 <file-port>
88 <file-input-port> <file-output-port> <file-input-output-port>
89
90 is-a? class-of
91 ensure-metaclass ensure-metaclass-with-supers
92 make-class
93 make-generic ensure-generic
94 make-extended-generic
95 make-accessor ensure-accessor
96 add-method!
97 class-slot-ref class-slot-set! slot-unbound slot-missing
98 slot-definition-name slot-definition-options
99 slot-definition-allocation
100
101 slot-definition-getter slot-definition-setter
102 slot-definition-accessor
103 slot-definition-init-value slot-definition-init-form
104 slot-definition-init-thunk slot-definition-init-keyword
105 slot-init-function class-slot-definition
106 method-source
107 compute-cpl compute-std-cpl compute-get-n-set compute-slots
108 compute-getter-method compute-setter-method
109 allocate-instance initialize make-instance make
110 no-next-method no-applicable-method no-method
111 change-class update-instance-for-different-class
112 shallow-clone deep-clone
113 class-redefinition
114 apply-generic apply-method apply-methods
115 compute-applicable-methods %compute-applicable-methods
116 method-more-specific? sort-applicable-methods
117 class-subclasses class-methods
118 goops-error
119 min-fixnum max-fixnum
120
121 ;;; *fixme* Should go into goops.c
122 instance? slot-ref-using-class
123 slot-set-using-class! slot-bound-using-class?
124 slot-exists-using-class? slot-ref slot-set! slot-bound?
125 class-name class-direct-supers class-direct-subclasses
126 class-direct-methods class-direct-slots class-precedence-list
127 class-slots
128 generic-function-name
129 generic-function-methods method-generic-function
130 method-specializers method-formals
131 primitive-generic-generic enable-primitive-generic!
132 method-procedure accessor-method-slot-definition
133 slot-exists? make find-method get-keyword)
134 #:no-backtrace)
135
136 (define *goops-module* (current-module))
137
138 ;; XXX FIXME: figure out why the 'eval-when's in this file must use
139 ;; 'compile' and must avoid 'expand', but only in 2.2, and only when
140 ;; compiling something that imports goops, e.g. (ice-9 occam-channel),
141 ;; before (oop goops) itself has been compiled.
142
143 ;; First initialize the builtin part of GOOPS
144 (eval-when (compile load eval)
145 (%init-goops-builtins))
146
147 (eval-when (compile load eval)
148 (use-modules ((language tree-il primitives) :select (add-interesting-primitive!)))
149 (add-interesting-primitive! 'class-of))
150
151 ;; Then load the rest of GOOPS
152 (use-modules (oop goops util)
153 (oop goops dispatch)
154 (oop goops compile))
155
156 \f
157 ;; FIXME: deprecate.
158 (eval-when (compile load eval)
159 (define min-fixnum (- (expt 2 29)))
160 (define max-fixnum (- (expt 2 29) 1)))
161
162 ;;
163 ;; goops-error
164 ;;
165 (define (goops-error format-string . args)
166 (scm-error 'goops-error #f format-string args '()))
167
168 ;;
169 ;; is-a?
170 ;;
171 (define (is-a? obj class)
172 (and (memq class (class-precedence-list (class-of obj))) #t))
173
174
175 ;;;
176 ;;; {Meta classes}
177 ;;;
178
179 (define ensure-metaclass-with-supers
180 (let ((table-of-metas '()))
181 (lambda (meta-supers)
182 (let ((entry (assoc meta-supers table-of-metas)))
183 (if entry
184 ;; Found a previously created metaclass
185 (cdr entry)
186 ;; Create a new meta-class which inherit from "meta-supers"
187 (let ((new (make <class> #:dsupers meta-supers
188 #:slots '()
189 #:name (gensym "metaclass"))))
190 (set! table-of-metas (cons (cons meta-supers new) table-of-metas))
191 new))))))
192
193 (define (ensure-metaclass supers)
194 (if (null? supers)
195 <class>
196 (let* ((all-metas (map (lambda (x) (class-of x)) supers))
197 (all-cpls (append-map (lambda (m)
198 (cdr (class-precedence-list m)))
199 all-metas))
200 (needed-metas '()))
201 ;; Find the most specific metaclasses. The new metaclass will be
202 ;; a subclass of these.
203 (for-each
204 (lambda (meta)
205 (if (and (not (member meta all-cpls))
206 (not (member meta needed-metas)))
207 (set! needed-metas (append needed-metas (list meta)))))
208 all-metas)
209 ;; Now return a subclass of the metaclasses we found.
210 (if (null? (cdr needed-metas))
211 (car needed-metas) ; If there's only one, just use it.
212 (ensure-metaclass-with-supers needed-metas)))))
213
214 ;;;
215 ;;; {Classes}
216 ;;;
217
218 ;;; (define-class NAME (SUPER ...) SLOT-DEFINITION ... OPTION ...)
219 ;;;
220 ;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
221 ;;; OPTION ::= KEYWORD VALUE
222 ;;;
223
224 (define (kw-do-map mapper f kwargs)
225 (define (keywords l)
226 (cond
227 ((null? l) '())
228 ((or (null? (cdr l)) (not (keyword? (car l))))
229 (goops-error "malformed keyword arguments: ~a" kwargs))
230 (else (cons (car l) (keywords (cddr l))))))
231 (define (args l)
232 (if (null? l) '() (cons (cadr l) (args (cddr l)))))
233 ;; let* to check keywords first
234 (let* ((k (keywords kwargs))
235 (a (args kwargs)))
236 (mapper f k a)))
237
238 (define (make-class supers slots . options)
239 (let* ((name (get-keyword #:name options (make-unbound)))
240 (supers (if (not (or-map (lambda (class)
241 (memq <object>
242 (class-precedence-list class)))
243 supers))
244 (append supers (list <object>))
245 supers))
246 (metaclass (or (get-keyword #:metaclass options #f)
247 (ensure-metaclass supers))))
248
249 ;; Verify that all direct slots are different and that we don't inherit
250 ;; several time from the same class
251 (let ((tmp1 (find-duplicate supers))
252 (tmp2 (find-duplicate (map slot-definition-name slots))))
253 (if tmp1
254 (goops-error "make-class: super class ~S is duplicate in class ~S"
255 tmp1 name))
256 (if tmp2
257 (goops-error "make-class: slot ~S is duplicate in class ~S"
258 tmp2 name)))
259
260 ;; Everything seems correct, build the class
261 (apply make metaclass
262 #:dsupers supers
263 #:slots slots
264 #:name name
265 options)))
266
267 ;;; (class (SUPER ...) SLOT-DEFINITION ... OPTION ...)
268 ;;;
269 ;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
270 ;;; OPTION ::= KEYWORD VALUE
271 ;;;
272 (define-macro (class supers . slots)
273 (define (make-slot-definition-forms slots)
274 (map
275 (lambda (def)
276 (cond
277 ((pair? def)
278 `(list ',(car def)
279 ,@(kw-do-map append-map
280 (lambda (kw arg)
281 (case kw
282 ((#:init-form)
283 `(#:init-form ',arg
284 #:init-thunk (lambda () ,arg)))
285 (else (list kw arg))))
286 (cdr def))))
287 (else
288 `(list ',def))))
289 slots))
290 (if (not (list? supers))
291 (goops-error "malformed superclass list: ~S" supers))
292 (let ((slots (take-while (lambda (x) (not (keyword? x))) slots))
293 (options (or (find-tail keyword? slots) '())))
294 `(make-class
295 ;; evaluate super class variables
296 (list ,@supers)
297 ;; evaluate slot definitions, except the slot name!
298 (list ,@(make-slot-definition-forms slots))
299 ;; evaluate class options
300 ,@options)))
301
302 (define-syntax define-class-pre-definition
303 (lambda (x)
304 (syntax-case x ()
305 ((_ (k arg rest ...) out ...)
306 (keyword? (syntax->datum #'k))
307 (case (syntax->datum #'k)
308 ((#:getter #:setter)
309 #'(define-class-pre-definition (rest ...)
310 out ...
311 (if (or (not (defined? 'arg))
312 (not (is-a? arg <generic>)))
313 (toplevel-define!
314 'arg
315 (ensure-generic (if (defined? 'arg) arg #f) 'arg)))))
316 ((#:accessor)
317 #'(define-class-pre-definition (rest ...)
318 out ...
319 (if (or (not (defined? 'arg))
320 (not (is-a? arg <accessor>)))
321 (toplevel-define!
322 'arg
323 (ensure-accessor (if (defined? 'arg) arg #f) 'arg)))))
324 (else
325 #'(define-class-pre-definition (rest ...) out ...))))
326 ((_ () out ...)
327 #'(begin out ...)))))
328
329 ;; Some slot options require extra definitions to be made. In
330 ;; particular, we want to make sure that the generic function objects
331 ;; which represent accessors exist before `make-class' tries to add
332 ;; methods to them.
333 (define-syntax define-class-pre-definitions
334 (lambda (x)
335 (syntax-case x ()
336 ((_ () out ...)
337 #'(begin out ...))
338 ((_ (slot rest ...) out ...)
339 (keyword? (syntax->datum #'slot))
340 #'(begin out ...))
341 ((_ (slot rest ...) out ...)
342 (identifier? #'slot)
343 #'(define-class-pre-definitions (rest ...)
344 out ...))
345 ((_ ((slotname slotopt ...) rest ...) out ...)
346 #'(define-class-pre-definitions (rest ...)
347 out ... (define-class-pre-definition (slotopt ...)))))))
348
349 (define-syntax-rule (define-class name supers slot ...)
350 (begin
351 (define-class-pre-definitions (slot ...))
352 (if (and (defined? 'name)
353 (is-a? name <class>)
354 (memq <object> (class-precedence-list name)))
355 (class-redefinition name
356 (class supers slot ... #:name 'name))
357 (toplevel-define! 'name (class supers slot ... #:name 'name)))))
358
359 (define-syntax-rule (standard-define-class arg ...)
360 (define-class arg ...))
361
362 ;;;
363 ;;; {Generic functions and accessors}
364 ;;;
365
366 ;; Apparently the desired semantics are that we extend previous
367 ;; procedural definitions, but that if `name' was already a generic, we
368 ;; overwrite its definition.
369 (define-macro (define-generic name)
370 (if (not (symbol? name))
371 (goops-error "bad generic function name: ~S" name))
372 `(define ,name
373 (if (and (defined? ',name) (is-a? ,name <generic>))
374 (make <generic> #:name ',name)
375 (ensure-generic (if (defined? ',name) ,name #f) ',name))))
376
377 (define-macro (define-extended-generic name val)
378 (if (not (symbol? name))
379 (goops-error "bad generic function name: ~S" name))
380 `(define ,name (make-extended-generic ,val ',name)))
381
382 (define-macro (define-extended-generics names . args)
383 (let ((prefixes (get-keyword #:prefix args #f)))
384 (if prefixes
385 `(begin
386 ,@(map (lambda (name)
387 `(define-extended-generic ,name
388 (list ,@(map (lambda (prefix)
389 (symbol-append prefix name))
390 prefixes))))
391 names))
392 (goops-error "no prefixes supplied"))))
393
394 (define* (make-generic #:optional name)
395 (make <generic> #:name name))
396
397 (define* (make-extended-generic gfs #:optional name)
398 (let* ((gfs (if (list? gfs) gfs (list gfs)))
399 (gws? (any (lambda (gf) (is-a? gf <generic-with-setter>)) gfs)))
400 (let ((ans (if gws?
401 (let* ((sname (and name (make-setter-name name)))
402 (setters
403 (append-map (lambda (gf)
404 (if (is-a? gf <generic-with-setter>)
405 (list (ensure-generic (setter gf)
406 sname))
407 '()))
408 gfs))
409 (es (make <extended-generic-with-setter>
410 #:name name
411 #:extends gfs
412 #:setter (make <extended-generic>
413 #:name sname
414 #:extends setters))))
415 (extended-by! setters (setter es))
416 es)
417 (make <extended-generic>
418 #:name name
419 #:extends gfs))))
420 (extended-by! gfs ans)
421 ans)))
422
423 (define (extended-by! gfs eg)
424 (for-each (lambda (gf)
425 (slot-set! gf 'extended-by
426 (cons eg (slot-ref gf 'extended-by))))
427 gfs)
428 (invalidate-method-cache! eg))
429
430 (define (not-extended-by! gfs eg)
431 (for-each (lambda (gf)
432 (slot-set! gf 'extended-by
433 (delq! eg (slot-ref gf 'extended-by))))
434 gfs)
435 (invalidate-method-cache! eg))
436
437 (define* (ensure-generic old-definition #:optional name)
438 (cond ((is-a? old-definition <generic>) old-definition)
439 ((procedure-with-setter? old-definition)
440 (make <generic-with-setter>
441 #:name name
442 #:default (procedure old-definition)
443 #:setter (setter old-definition)))
444 ((procedure? old-definition)
445 (if (generic-capability? old-definition) old-definition
446 (make <generic> #:name name #:default old-definition)))
447 (else (make <generic> #:name name))))
448
449 ;; same semantics as <generic>
450 (define-syntax-rule (define-accessor name)
451 (define name
452 (cond ((not (defined? 'name)) (ensure-accessor #f 'name))
453 ((is-a? name <accessor>) (make <accessor> #:name 'name))
454 (else (ensure-accessor name 'name)))))
455
456 (define (make-setter-name name)
457 (string->symbol (string-append "setter:" (symbol->string name))))
458
459 (define* (make-accessor #:optional name)
460 (make <accessor>
461 #:name name
462 #:setter (make <generic>
463 #:name (and name (make-setter-name name)))))
464
465 (define* (ensure-accessor proc #:optional name)
466 (cond ((and (is-a? proc <accessor>)
467 (is-a? (setter proc) <generic>))
468 proc)
469 ((is-a? proc <generic-with-setter>)
470 (upgrade-accessor proc (setter proc)))
471 ((is-a? proc <generic>)
472 (upgrade-accessor proc (make-generic name)))
473 ((procedure-with-setter? proc)
474 (make <accessor>
475 #:name name
476 #:default (procedure proc)
477 #:setter (ensure-generic (setter proc) name)))
478 ((procedure? proc)
479 (ensure-accessor (if (generic-capability? proc)
480 (make <generic> #:name name #:default proc)
481 (ensure-generic proc name))
482 name))
483 (else
484 (make-accessor name))))
485
486 (define (upgrade-accessor generic setter)
487 (let ((methods (slot-ref generic 'methods))
488 (gws (make (if (is-a? generic <extended-generic>)
489 <extended-generic-with-setter>
490 <accessor>)
491 #:name (generic-function-name generic)
492 #:extended-by (slot-ref generic 'extended-by)
493 #:setter setter)))
494 (if (is-a? generic <extended-generic>)
495 (let ((gfs (slot-ref generic 'extends)))
496 (not-extended-by! gfs generic)
497 (slot-set! gws 'extends gfs)
498 (extended-by! gfs gws)))
499 ;; Steal old methods
500 (for-each (lambda (method)
501 (slot-set! method 'generic-function gws))
502 methods)
503 (slot-set! gws 'methods methods)
504 (invalidate-method-cache! gws)
505 gws))
506
507 ;;;
508 ;;; {Methods}
509 ;;;
510
511 (define (toplevel-define! name val)
512 (module-define! (current-module) name val))
513
514 (define-syntax define-method
515 (syntax-rules (setter)
516 ((_ ((setter name) . args) body ...)
517 (begin
518 (if (or (not (defined? 'name))
519 (not (is-a? name <accessor>)))
520 (toplevel-define! 'name
521 (ensure-accessor
522 (if (defined? 'name) name #f) 'name)))
523 (add-method! (setter name) (method args body ...))))
524 ((_ (name . args) body ...)
525 (begin
526 ;; FIXME: this code is how it always was, but it's quite cracky:
527 ;; it will only define the generic function if it was undefined
528 ;; before (ok), or *was defined to #f*. The latter is crack. But
529 ;; there are bootstrap issues about fixing this -- change it to
530 ;; (is-a? name <generic>) and see.
531 (if (or (not (defined? 'name))
532 (not name))
533 (toplevel-define! 'name (make <generic> #:name 'name)))
534 (add-method! name (method args body ...))))))
535
536 (define-syntax method
537 (lambda (x)
538 (define (parse-args args)
539 (let lp ((ls args) (formals '()) (specializers '()))
540 (syntax-case ls ()
541 (((f s) . rest)
542 (and (identifier? #'f) (identifier? #'s))
543 (lp #'rest
544 (cons #'f formals)
545 (cons #'s specializers)))
546 ((f . rest)
547 (identifier? #'f)
548 (lp #'rest
549 (cons #'f formals)
550 (cons #'<top> specializers)))
551 (()
552 (list (reverse formals)
553 (reverse (cons #''() specializers))))
554 (tail
555 (identifier? #'tail)
556 (list (append (reverse formals) #'tail)
557 (reverse (cons #'<top> specializers)))))))
558
559 (define (find-free-id exp referent)
560 (syntax-case exp ()
561 ((x . y)
562 (or (find-free-id #'x referent)
563 (find-free-id #'y referent)))
564 (x
565 (identifier? #'x)
566 (let ((id (datum->syntax #'x referent)))
567 (and (free-identifier=? #'x id) id)))
568 (_ #f)))
569
570 (define (compute-procedure formals body)
571 (syntax-case body ()
572 ((body0 ...)
573 (with-syntax ((formals formals))
574 #'(lambda formals body0 ...)))))
575
576 (define (->proper args)
577 (let lp ((ls args) (out '()))
578 (syntax-case ls ()
579 ((x . xs) (lp #'xs (cons #'x out)))
580 (() (reverse out))
581 (tail (reverse (cons #'tail out))))))
582
583 (define (compute-make-procedure formals body next-method)
584 (syntax-case body ()
585 ((body ...)
586 (with-syntax ((next-method next-method))
587 (syntax-case formals ()
588 ((formal ...)
589 #'(lambda (real-next-method)
590 (lambda (formal ...)
591 (let ((next-method (lambda args
592 (if (null? args)
593 (real-next-method formal ...)
594 (apply real-next-method args)))))
595 body ...))))
596 (formals
597 (with-syntax (((formal ...) (->proper #'formals)))
598 #'(lambda (real-next-method)
599 (lambda formals
600 (let ((next-method (lambda args
601 (if (null? args)
602 (apply real-next-method formal ...)
603 (apply real-next-method args)))))
604 body ...))))))))))
605
606 (define (compute-procedures formals body)
607 ;; So, our use of this is broken, because it operates on the
608 ;; pre-expansion source code. It's equivalent to just searching
609 ;; for referent in the datums. Ah well.
610 (let ((id (find-free-id body 'next-method)))
611 (if id
612 ;; return a make-procedure
613 (values #'#f
614 (compute-make-procedure formals body id))
615 (values (compute-procedure formals body)
616 #'#f))))
617
618 (syntax-case x ()
619 ((_ args) #'(method args (if #f #f)))
620 ((_ args body0 body1 ...)
621 (with-syntax (((formals (specializer ...)) (parse-args #'args)))
622 (call-with-values
623 (lambda ()
624 (compute-procedures #'formals #'(body0 body1 ...)))
625 (lambda (procedure make-procedure)
626 (with-syntax ((procedure procedure)
627 (make-procedure make-procedure))
628 #'(make <method>
629 #:specializers (cons* specializer ...)
630 #:formals 'formals
631 #:body '(body0 body1 ...)
632 #:make-procedure make-procedure
633 #:procedure procedure)))))))))
634
635 ;;;
636 ;;; {add-method!}
637 ;;;
638
639 (define (add-method-in-classes! m)
640 ;; Add method in all the classes which appears in its specializers list
641 (for-each* (lambda (x)
642 (let ((dm (class-direct-methods x)))
643 (if (not (memq m dm))
644 (slot-set! x 'direct-methods (cons m dm)))))
645 (method-specializers m)))
646
647 (define (remove-method-in-classes! m)
648 ;; Remove method in all the classes which appears in its specializers list
649 (for-each* (lambda (x)
650 (slot-set! x
651 'direct-methods
652 (delv! m (class-direct-methods x))))
653 (method-specializers m)))
654
655 (define (compute-new-list-of-methods gf new)
656 (let ((new-spec (method-specializers new))
657 (methods (slot-ref gf 'methods)))
658 (let loop ((l methods))
659 (if (null? l)
660 (cons new methods)
661 (if (equal? (method-specializers (car l)) new-spec)
662 (begin
663 ;; This spec. list already exists. Remove old method from dependents
664 (remove-method-in-classes! (car l))
665 (set-car! l new)
666 methods)
667 (loop (cdr l)))))))
668
669 (define (method-n-specializers m)
670 (length* (slot-ref m 'specializers)))
671
672 (define (calculate-n-specialized gf)
673 (fold (lambda (m n) (max n (method-n-specializers m)))
674 0
675 (generic-function-methods gf)))
676
677 (define (invalidate-method-cache! gf)
678 (%invalidate-method-cache! gf)
679 (slot-set! gf 'n-specialized (calculate-n-specialized gf))
680 (for-each (lambda (gf) (invalidate-method-cache! gf))
681 (slot-ref gf 'extended-by)))
682
683 (define internal-add-method!
684 (method ((gf <generic>) (m <method>))
685 (slot-set! m 'generic-function gf)
686 (slot-set! gf 'methods (compute-new-list-of-methods gf m))
687 (invalidate-method-cache! gf)
688 (add-method-in-classes! m)
689 *unspecified*))
690
691 (define-generic add-method!)
692
693 ((method-procedure internal-add-method!) add-method! internal-add-method!)
694
695 (define-method (add-method! (proc <procedure>) (m <method>))
696 (if (generic-capability? proc)
697 (begin
698 (enable-primitive-generic! proc)
699 (add-method! proc m))
700 (next-method)))
701
702 (define-method (add-method! (pg <primitive-generic>) (m <method>))
703 (add-method! (primitive-generic-generic pg) m))
704
705 (define-method (add-method! obj (m <method>))
706 (goops-error "~S is not a valid generic function" obj))
707
708 ;;;
709 ;;; {Access to meta objects}
710 ;;;
711
712 ;;;
713 ;;; Methods
714 ;;;
715 (define-method (method-source (m <method>))
716 (let* ((spec (map* class-name (slot-ref m 'specializers)))
717 (src (procedure-source (slot-ref m 'procedure))))
718 (and src
719 (let ((args (cadr src))
720 (body (cddr src)))
721 (cons 'method
722 (cons (map* list args spec)
723 body))))))
724
725 (define-method (method-formals (m <method>))
726 (slot-ref m 'formals))
727
728 ;;;
729 ;;; Slots
730 ;;;
731 (define slot-definition-name car)
732
733 (define slot-definition-options cdr)
734
735 (define (slot-definition-allocation s)
736 (get-keyword #:allocation (cdr s) #:instance))
737
738 (define (slot-definition-getter s)
739 (get-keyword #:getter (cdr s) #f))
740
741 (define (slot-definition-setter s)
742 (get-keyword #:setter (cdr s) #f))
743
744 (define (slot-definition-accessor s)
745 (get-keyword #:accessor (cdr s) #f))
746
747 (define (slot-definition-init-value s)
748 ;; can be #f, so we can't use #f as non-value
749 (get-keyword #:init-value (cdr s) (make-unbound)))
750
751 (define (slot-definition-init-form s)
752 (get-keyword #:init-form (cdr s) (make-unbound)))
753
754 (define (slot-definition-init-thunk s)
755 (get-keyword #:init-thunk (cdr s) #f))
756
757 (define (slot-definition-init-keyword s)
758 (get-keyword #:init-keyword (cdr s) #f))
759
760 (define (class-slot-definition class slot-name)
761 (assq slot-name (class-slots class)))
762
763 (define (slot-init-function class slot-name)
764 (cadr (assq slot-name (slot-ref class 'getters-n-setters))))
765
766 (define (accessor-method-slot-definition obj)
767 "Return the slot definition of the accessor @var{obj}."
768 (slot-ref obj 'slot-definition))
769
770
771 ;;;
772 ;;; {Standard methods used by the C runtime}
773 ;;;
774
775 ;;; Methods to compare objects
776 ;;;
777
778 ;; Have to do this in a strange order because equal? is used in the
779 ;; add-method! implementation; we need to make sure that when the
780 ;; primitive is extended, that the generic has a method. =
781 (define g-equal? (make-generic 'equal?))
782 ;; When this generic gets called, we will have already checked eq? and
783 ;; eqv? -- the purpose of this generic is to extend equality. So by
784 ;; default, there is no extension, thus the #f return.
785 (add-method! g-equal? (method (x y) #f))
786 (set-primitive-generic! equal? g-equal?)
787
788 ;;;
789 ;;; methods to display/write an object
790 ;;;
791
792 ; Code for writing objects must test that the slots they use are
793 ; bound. Otherwise a slot-unbound method will be called and will
794 ; conduct to an infinite loop.
795
796 ;; Write
797 (define (display-address o file)
798 (display (number->string (object-address o) 16) file))
799
800 (define-method (write o file)
801 (display "#<instance " file)
802 (display-address o file)
803 (display #\> file))
804
805 (define write-object (primitive-generic-generic write))
806
807 (define-method (write (o <object>) file)
808 (let ((class (class-of o)))
809 (if (slot-bound? class 'name)
810 (begin
811 (display "#<" file)
812 (display (class-name class) file)
813 (display #\space file)
814 (display-address o file)
815 (display #\> file))
816 (next-method))))
817
818 (define-method (write (class <class>) file)
819 (let ((meta (class-of class)))
820 (if (and (slot-bound? class 'name)
821 (slot-bound? meta 'name))
822 (begin
823 (display "#<" file)
824 (display (class-name meta) file)
825 (display #\space file)
826 (display (class-name class) file)
827 (display #\space file)
828 (display-address class file)
829 (display #\> file))
830 (next-method))))
831
832 (define-method (write (gf <generic>) file)
833 (let ((meta (class-of gf)))
834 (if (and (slot-bound? meta 'name)
835 (slot-bound? gf 'methods))
836 (begin
837 (display "#<" file)
838 (display (class-name meta) file)
839 (let ((name (generic-function-name gf)))
840 (if name
841 (begin
842 (display #\space file)
843 (display name file))))
844 (display " (" file)
845 (display (length (generic-function-methods gf)) file)
846 (display ")>" file))
847 (next-method))))
848
849 (define-method (write (o <method>) file)
850 (let ((meta (class-of o)))
851 (if (and (slot-bound? meta 'name)
852 (slot-bound? o 'specializers))
853 (begin
854 (display "#<" file)
855 (display (class-name meta) file)
856 (display #\space file)
857 (display (map* (lambda (spec)
858 (if (slot-bound? spec 'name)
859 (slot-ref spec 'name)
860 spec))
861 (method-specializers o))
862 file)
863 (display #\space file)
864 (display-address o file)
865 (display #\> file))
866 (next-method))))
867
868 ;; Display (do the same thing as write by default)
869 (define-method (display o file)
870 (write-object o file))
871
872 ;;;
873 ;;; Handling of duplicate bindings in the module system
874 ;;;
875
876 (define (find-subclass super name)
877 (let lp ((classes (class-direct-subclasses super)))
878 (cond
879 ((null? classes)
880 (error "class not found" name))
881 ((and (slot-bound? (car classes) 'name)
882 (eq? (class-name (car classes)) name))
883 (car classes))
884 (else
885 (lp (cdr classes))))))
886
887 ;; A record type.
888 (define <module> (find-subclass <top> '<module>))
889
890 (define-method (merge-generics (module <module>)
891 (name <symbol>)
892 (int1 <module>)
893 (val1 <top>)
894 (int2 <module>)
895 (val2 <top>)
896 (var <top>)
897 (val <top>))
898 #f)
899
900 (define-method (merge-generics (module <module>)
901 (name <symbol>)
902 (int1 <module>)
903 (val1 <generic>)
904 (int2 <module>)
905 (val2 <generic>)
906 (var <top>)
907 (val <boolean>))
908 (and (not (eq? val1 val2))
909 (make-variable (make-extended-generic (list val2 val1) name))))
910
911 (define-method (merge-generics (module <module>)
912 (name <symbol>)
913 (int1 <module>)
914 (val1 <generic>)
915 (int2 <module>)
916 (val2 <generic>)
917 (var <top>)
918 (gf <extended-generic>))
919 (and (not (memq val2 (slot-ref gf 'extends)))
920 (begin
921 (slot-set! gf
922 'extends
923 (cons val2 (delq! val2 (slot-ref gf 'extends))))
924 (slot-set! val2
925 'extended-by
926 (cons gf (delq! gf (slot-ref val2 'extended-by))))
927 (invalidate-method-cache! gf)
928 var)))
929
930 (module-define! duplicate-handlers 'merge-generics merge-generics)
931
932 (define-method (merge-accessors (module <module>)
933 (name <symbol>)
934 (int1 <module>)
935 (val1 <top>)
936 (int2 <module>)
937 (val2 <top>)
938 (var <top>)
939 (val <top>))
940 #f)
941
942 (define-method (merge-accessors (module <module>)
943 (name <symbol>)
944 (int1 <module>)
945 (val1 <accessor>)
946 (int2 <module>)
947 (val2 <accessor>)
948 (var <top>)
949 (val <top>))
950 (merge-generics module name int1 val1 int2 val2 var val))
951
952 (module-define! duplicate-handlers 'merge-accessors merge-accessors)
953
954 ;;;
955 ;;; slot access
956 ;;;
957
958 (define (class-slot-g-n-s class slot-name)
959 (let* ((this-slot (assq slot-name (slot-ref class 'slots)))
960 (g-n-s (cddr (or (assq slot-name (slot-ref class 'getters-n-setters))
961 (slot-missing class slot-name)))))
962 (if (not (memq (slot-definition-allocation this-slot)
963 '(#:class #:each-subclass)))
964 (slot-missing class slot-name))
965 g-n-s))
966
967 (define (class-slot-ref class slot)
968 (let ((x ((car (class-slot-g-n-s class slot)) #f)))
969 (if (unbound? x)
970 (slot-unbound class slot)
971 x)))
972
973 (define (class-slot-set! class slot value)
974 ((cadr (class-slot-g-n-s class slot)) #f value))
975
976 (define-method (slot-unbound (c <class>) (o <object>) s)
977 (goops-error "Slot `~S' is unbound in object ~S" s o))
978
979 (define-method (slot-unbound (c <class>) s)
980 (goops-error "Slot `~S' is unbound in class ~S" s c))
981
982 (define-method (slot-unbound (o <object>))
983 (goops-error "Unbound slot in object ~S" o))
984
985 (define-method (slot-missing (c <class>) (o <object>) s)
986 (goops-error "No slot with name `~S' in object ~S" s o))
987
988 (define-method (slot-missing (c <class>) s)
989 (goops-error "No class slot with name `~S' in class ~S" s c))
990
991
992 (define-method (slot-missing (c <class>) (o <object>) s value)
993 (slot-missing c o s))
994
995 ;;; Methods for the possible error we can encounter when calling a gf
996
997 (define-method (no-next-method (gf <generic>) args)
998 (goops-error "No next method when calling ~S\nwith arguments ~S" gf args))
999
1000 (define-method (no-applicable-method (gf <generic>) args)
1001 (goops-error "No applicable method for ~S in call ~S"
1002 gf (cons (generic-function-name gf) args)))
1003
1004 (define-method (no-method (gf <generic>) args)
1005 (goops-error "No method defined for ~S" gf))
1006
1007 ;;;
1008 ;;; {Cloning functions (from rdeline@CS.CMU.EDU)}
1009 ;;;
1010
1011 (define-method (shallow-clone (self <object>))
1012 (let ((clone (%allocate-instance (class-of self) '()))
1013 (slots (map slot-definition-name
1014 (class-slots (class-of self)))))
1015 (for-each (lambda (slot)
1016 (if (slot-bound? self slot)
1017 (slot-set! clone slot (slot-ref self slot))))
1018 slots)
1019 clone))
1020
1021 (define-method (deep-clone (self <object>))
1022 (let ((clone (%allocate-instance (class-of self) '()))
1023 (slots (map slot-definition-name
1024 (class-slots (class-of self)))))
1025 (for-each (lambda (slot)
1026 (if (slot-bound? self slot)
1027 (slot-set! clone slot
1028 (let ((value (slot-ref self slot)))
1029 (if (instance? value)
1030 (deep-clone value)
1031 value)))))
1032 slots)
1033 clone))
1034
1035 ;;;
1036 ;;; {Class redefinition utilities}
1037 ;;;
1038
1039 ;;; (class-redefinition OLD NEW)
1040 ;;;
1041
1042 ;;; Has correct the following conditions:
1043
1044 ;;; Methods
1045 ;;;
1046 ;;; 1. New accessor specializers refer to new header
1047 ;;;
1048 ;;; Classes
1049 ;;;
1050 ;;; 1. New class cpl refers to the new class header
1051 ;;; 2. Old class header exists on old super classes direct-subclass lists
1052 ;;; 3. New class header exists on new super classes direct-subclass lists
1053
1054 (define-method (class-redefinition (old <class>) (new <class>))
1055 ;; Work on direct methods:
1056 ;; 1. Remove accessor methods from the old class
1057 ;; 2. Patch the occurences of new in the specializers by old
1058 ;; 3. Displace the methods from old to new
1059 (remove-class-accessors! old) ;; -1-
1060 (let ((methods (class-direct-methods new)))
1061 (for-each (lambda (m)
1062 (update-direct-method! m new old)) ;; -2-
1063 methods)
1064 (slot-set! new
1065 'direct-methods
1066 (append methods (class-direct-methods old))))
1067
1068 ;; Substitute old for new in new cpl
1069 (set-car! (slot-ref new 'cpl) old)
1070
1071 ;; Remove the old class from the direct-subclasses list of its super classes
1072 (for-each (lambda (c) (slot-set! c 'direct-subclasses
1073 (delv! old (class-direct-subclasses c))))
1074 (class-direct-supers old))
1075
1076 ;; Replace the new class with the old in the direct-subclasses of the supers
1077 (for-each (lambda (c)
1078 (slot-set! c 'direct-subclasses
1079 (cons old (delv! new (class-direct-subclasses c)))))
1080 (class-direct-supers new))
1081
1082 ;; Swap object headers
1083 (%modify-class old new)
1084
1085 ;; Now old is NEW!
1086
1087 ;; Redefine all the subclasses of old to take into account modification
1088 (for-each
1089 (lambda (c)
1090 (update-direct-subclass! c new old))
1091 (class-direct-subclasses new))
1092
1093 ;; Invalidate class so that subsequent instances slot accesses invoke
1094 ;; change-object-class
1095 (slot-set! new 'redefined old)
1096 (%invalidate-class new) ;must come after slot-set!
1097
1098 old)
1099
1100 ;;;
1101 ;;; remove-class-accessors!
1102 ;;;
1103
1104 (define-method (remove-class-accessors! (c <class>))
1105 (for-each (lambda (m)
1106 (if (is-a? m <accessor-method>)
1107 (let ((gf (slot-ref m 'generic-function)))
1108 ;; remove the method from its GF
1109 (slot-set! gf 'methods
1110 (delq1! m (slot-ref gf 'methods)))
1111 (invalidate-method-cache! gf)
1112 ;; remove the method from its specializers
1113 (remove-method-in-classes! m))))
1114 (class-direct-methods c)))
1115
1116 ;;;
1117 ;;; update-direct-method!
1118 ;;;
1119
1120 (define-method (update-direct-method! (m <method>)
1121 (old <class>)
1122 (new <class>))
1123 (let loop ((l (method-specializers m)))
1124 ;; Note: the <top> in dotted list is never used.
1125 ;; So we can work as if we had only proper lists.
1126 (if (pair? l)
1127 (begin
1128 (if (eqv? (car l) old)
1129 (set-car! l new))
1130 (loop (cdr l))))))
1131
1132 ;;;
1133 ;;; update-direct-subclass!
1134 ;;;
1135
1136 (define-method (update-direct-subclass! (c <class>)
1137 (old <class>)
1138 (new <class>))
1139 (class-redefinition c
1140 (make-class (class-direct-supers c)
1141 (class-direct-slots c)
1142 #:name (class-name c)
1143 #:metaclass (class-of c))))
1144
1145 ;;;
1146 ;;; {Utilities for INITIALIZE methods}
1147 ;;;
1148
1149 ;;; compute-slot-accessors
1150 ;;;
1151 (define (compute-slot-accessors class slots)
1152 (for-each
1153 (lambda (s g-n-s)
1154 (let ((getter-function (slot-definition-getter s))
1155 (setter-function (slot-definition-setter s))
1156 (accessor (slot-definition-accessor s)))
1157 (if getter-function
1158 (add-method! getter-function
1159 (compute-getter-method class g-n-s)))
1160 (if setter-function
1161 (add-method! setter-function
1162 (compute-setter-method class g-n-s)))
1163 (if accessor
1164 (begin
1165 (add-method! accessor
1166 (compute-getter-method class g-n-s))
1167 (add-method! (setter accessor)
1168 (compute-setter-method class g-n-s))))))
1169 slots (slot-ref class 'getters-n-setters)))
1170
1171 (define-method (compute-getter-method (class <class>) slotdef)
1172 (let ((init-thunk (cadr slotdef))
1173 (g-n-s (cddr slotdef)))
1174 (make <accessor-method>
1175 #:specializers (list class)
1176 #:procedure (cond ((pair? g-n-s)
1177 (make-generic-bound-check-getter (car g-n-s)))
1178 (init-thunk
1179 (standard-get g-n-s))
1180 (else
1181 (bound-check-get g-n-s)))
1182 #:slot-definition slotdef)))
1183
1184 (define-method (compute-setter-method (class <class>) slotdef)
1185 (let ((g-n-s (cddr slotdef)))
1186 (make <accessor-method>
1187 #:specializers (list class <top>)
1188 #:procedure (if (pair? g-n-s)
1189 (cadr g-n-s)
1190 (standard-set g-n-s))
1191 #:slot-definition slotdef)))
1192
1193 (define (make-generic-bound-check-getter proc)
1194 (lambda (o) (assert-bound (proc o) o)))
1195
1196 ;;; Pre-generate getters and setters for the first 20 slots.
1197 (define-syntax define-standard-accessor-method
1198 (lambda (stx)
1199 (define num-standard-pre-cache 20)
1200 (syntax-case stx ()
1201 ((_ ((proc n) arg ...) body)
1202 #`(define proc
1203 (let ((cache (vector #,@(map (lambda (n*)
1204 #`(lambda (arg ...)
1205 (let ((n #,n*))
1206 body)))
1207 (iota num-standard-pre-cache)))))
1208 (lambda (n)
1209 (if (< n #,num-standard-pre-cache)
1210 (vector-ref cache n)
1211 (lambda (arg ...) body)))))))))
1212
1213 (define-standard-accessor-method ((bound-check-get n) o)
1214 (let ((x (struct-ref o n)))
1215 (if (unbound? x)
1216 (slot-unbound o)
1217 x)))
1218
1219 (define-standard-accessor-method ((standard-get n) o)
1220 (struct-ref o n))
1221
1222 (define-standard-accessor-method ((standard-set n) o v)
1223 (struct-set! o n v))
1224
1225 ;;; compute-getters-n-setters
1226 ;;;
1227 (define (compute-getters-n-setters class slots)
1228
1229 (define (compute-slot-init-function name s)
1230 (or (let ((thunk (slot-definition-init-thunk s)))
1231 (and thunk
1232 (if (thunk? thunk)
1233 thunk
1234 (goops-error "Bad init-thunk for slot `~S' in ~S: ~S"
1235 name class thunk))))
1236 (let ((init (slot-definition-init-value s)))
1237 (and (not (unbound? init))
1238 (lambda () init)))))
1239
1240 (define (verify-accessors slot l)
1241 (cond ((integer? l))
1242 ((not (and (list? l) (= (length l) 2)))
1243 (goops-error "Bad getter and setter for slot `~S' in ~S: ~S"
1244 slot class l))
1245 (else
1246 (let ((get (car l))
1247 (set (cadr l)))
1248 (if (not (procedure? get))
1249 (goops-error "Bad getter closure for slot `~S' in ~S: ~S"
1250 slot class get))
1251 (if (not (procedure? set))
1252 (goops-error "Bad setter closure for slot `~S' in ~S: ~S"
1253 slot class set))))))
1254
1255 (map (lambda (s)
1256 ;; The strange treatment of nfields is due to backward compatibility.
1257 (let* ((index (slot-ref class 'nfields))
1258 (g-n-s (compute-get-n-set class s))
1259 (size (- (slot-ref class 'nfields) index))
1260 (name (slot-definition-name s)))
1261 ;; NOTE: The following is interdependent with C macros
1262 ;; defined above goops.c:scm_sys_prep_layout_x.
1263 ;;
1264 ;; For simple instance slots, we have the simplest form
1265 ;; '(name init-function . index)
1266 ;; For other slots we have
1267 ;; '(name init-function getter setter . alloc)
1268 ;; where alloc is:
1269 ;; '(index size) for instance allocated slots
1270 ;; '() for other slots
1271 (verify-accessors name g-n-s)
1272 (cons name
1273 (cons (compute-slot-init-function name s)
1274 (if (or (integer? g-n-s)
1275 (zero? size))
1276 g-n-s
1277 (append g-n-s (list index size)))))))
1278 slots))
1279
1280 ;;; compute-cpl
1281 ;;;
1282 ;;; Correct behaviour:
1283 ;;;
1284 ;;; (define-class food ())
1285 ;;; (define-class fruit (food))
1286 ;;; (define-class spice (food))
1287 ;;; (define-class apple (fruit))
1288 ;;; (define-class cinnamon (spice))
1289 ;;; (define-class pie (apple cinnamon))
1290 ;;; => cpl (pie) = pie apple fruit cinnamon spice food object top
1291 ;;;
1292 ;;; (define-class d ())
1293 ;;; (define-class e ())
1294 ;;; (define-class f ())
1295 ;;; (define-class b (d e))
1296 ;;; (define-class c (e f))
1297 ;;; (define-class a (b c))
1298 ;;; => cpl (a) = a b d c e f object top
1299 ;;;
1300
1301 (define-method (compute-cpl (class <class>))
1302 (compute-std-cpl class class-direct-supers))
1303
1304 ;; Support
1305
1306 (define (only-non-null lst)
1307 (filter (lambda (l) (not (null? l))) lst))
1308
1309 (define (compute-std-cpl c get-direct-supers)
1310 (let ((c-direct-supers (get-direct-supers c)))
1311 (merge-lists (list c)
1312 (only-non-null (append (map class-precedence-list
1313 c-direct-supers)
1314 (list c-direct-supers))))))
1315
1316 (define (merge-lists reversed-partial-result inputs)
1317 (cond
1318 ((every null? inputs)
1319 (reverse! reversed-partial-result))
1320 (else
1321 (let* ((candidate (lambda (c)
1322 (and (not (any (lambda (l)
1323 (memq c (cdr l)))
1324 inputs))
1325 c)))
1326 (candidate-car (lambda (l)
1327 (and (not (null? l))
1328 (candidate (car l)))))
1329 (next (any candidate-car inputs)))
1330 (if (not next)
1331 (goops-error "merge-lists: Inconsistent precedence graph"))
1332 (let ((remove-next (lambda (l)
1333 (if (eq? (car l) next)
1334 (cdr l)
1335 l))))
1336 (merge-lists (cons next reversed-partial-result)
1337 (only-non-null (map remove-next inputs))))))))
1338
1339 ;; Modified from TinyClos:
1340 ;;
1341 ;; A simple topological sort.
1342 ;;
1343 ;; It's in this file so that both TinyClos and Objects can use it.
1344 ;;
1345 ;; This is a fairly modified version of code I originally got from Anurag
1346 ;; Mendhekar <anurag@moose.cs.indiana.edu>.
1347 ;;
1348
1349 (define (compute-clos-cpl c get-direct-supers)
1350 (top-sort ((build-transitive-closure get-direct-supers) c)
1351 ((build-constraints get-direct-supers) c)
1352 (std-tie-breaker get-direct-supers)))
1353
1354
1355 (define (top-sort elements constraints tie-breaker)
1356 (let loop ((elements elements)
1357 (constraints constraints)
1358 (result '()))
1359 (if (null? elements)
1360 result
1361 (let ((can-go-in-now
1362 (filter
1363 (lambda (x)
1364 (every (lambda (constraint)
1365 (or (not (eq? (cadr constraint) x))
1366 (memq (car constraint) result)))
1367 constraints))
1368 elements)))
1369 (if (null? can-go-in-now)
1370 (goops-error "top-sort: Invalid constraints")
1371 (let ((choice (if (null? (cdr can-go-in-now))
1372 (car can-go-in-now)
1373 (tie-breaker result
1374 can-go-in-now))))
1375 (loop
1376 (filter (lambda (x) (not (eq? x choice)))
1377 elements)
1378 constraints
1379 (append result (list choice)))))))))
1380
1381 (define (std-tie-breaker get-supers)
1382 (lambda (partial-cpl min-elts)
1383 (let loop ((pcpl (reverse partial-cpl)))
1384 (let ((current-elt (car pcpl)))
1385 (let ((ds-of-ce (get-supers current-elt)))
1386 (let ((common (filter (lambda (x)
1387 (memq x ds-of-ce))
1388 min-elts)))
1389 (if (null? common)
1390 (if (null? (cdr pcpl))
1391 (goops-error "std-tie-breaker: Nothing valid")
1392 (loop (cdr pcpl)))
1393 (car common))))))))
1394
1395
1396 (define (build-transitive-closure get-follow-ons)
1397 (lambda (x)
1398 (let track ((result '())
1399 (pending (list x)))
1400 (if (null? pending)
1401 result
1402 (let ((next (car pending)))
1403 (if (memq next result)
1404 (track result (cdr pending))
1405 (track (cons next result)
1406 (append (get-follow-ons next)
1407 (cdr pending)))))))))
1408
1409 (define (build-constraints get-follow-ons)
1410 (lambda (x)
1411 (let loop ((elements ((build-transitive-closure get-follow-ons) x))
1412 (this-one '())
1413 (result '()))
1414 (if (or (null? this-one) (null? (cdr this-one)))
1415 (if (null? elements)
1416 result
1417 (loop (cdr elements)
1418 (cons (car elements)
1419 (get-follow-ons (car elements)))
1420 result))
1421 (loop elements
1422 (cdr this-one)
1423 (cons (list (car this-one) (cadr this-one))
1424 result))))))
1425
1426 ;;; compute-get-n-set
1427 ;;;
1428 (define-method (compute-get-n-set (class <class>) s)
1429 (case (slot-definition-allocation s)
1430 ((#:instance) ;; Instance slot
1431 ;; get-n-set is just its offset
1432 (let ((already-allocated (slot-ref class 'nfields)))
1433 (slot-set! class 'nfields (+ already-allocated 1))
1434 already-allocated))
1435
1436 ((#:class) ;; Class slot
1437 ;; Class-slots accessors are implemented as 2 closures around
1438 ;; a Scheme variable. As instance slots, class slots must be
1439 ;; unbound at init time.
1440 (let ((name (slot-definition-name s)))
1441 (if (memq name (map slot-definition-name (class-direct-slots class)))
1442 ;; This slot is direct; create a new shared variable
1443 (make-closure-variable class)
1444 ;; Slot is inherited. Find its definition in superclass
1445 (let loop ((l (cdr (class-precedence-list class))))
1446 (let ((r (assoc name (slot-ref (car l) 'getters-n-setters))))
1447 (if r
1448 (cddr r)
1449 (loop (cdr l))))))))
1450
1451 ((#:each-subclass) ;; slot shared by instances of direct subclass.
1452 ;; (Thomas Buerger, April 1998)
1453 (make-closure-variable class))
1454
1455 ((#:virtual) ;; No allocation
1456 ;; slot-ref and slot-set! function must be given by the user
1457 (let ((get (get-keyword #:slot-ref (slot-definition-options s) #f))
1458 (set (get-keyword #:slot-set! (slot-definition-options s) #f)))
1459 (if (not (and get set))
1460 (goops-error "You must supply a #:slot-ref and a #:slot-set! in ~S"
1461 s))
1462 (list get set)))
1463 (else (next-method))))
1464
1465 (define (make-closure-variable class)
1466 (let ((shared-variable (make-unbound)))
1467 (list (lambda (o) shared-variable)
1468 (lambda (o v) (set! shared-variable v)))))
1469
1470 (define-method (compute-get-n-set (o <object>) s)
1471 (goops-error "Allocation \"~S\" is unknown" (slot-definition-allocation s)))
1472
1473 (define-method (compute-slots (class <class>))
1474 (%compute-slots class))
1475
1476 ;;;
1477 ;;; {Initialize}
1478 ;;;
1479
1480 (define-method (initialize (object <object>) initargs)
1481 (%initialize-object object initargs))
1482
1483 (define-method (initialize (class <class>) initargs)
1484 (next-method)
1485 (let ((dslots (get-keyword #:slots initargs '()))
1486 (supers (get-keyword #:dsupers initargs '())))
1487 (slot-set! class 'name (get-keyword #:name initargs '???))
1488 (slot-set! class 'direct-supers supers)
1489 (slot-set! class 'direct-slots dslots)
1490 (slot-set! class 'direct-subclasses '())
1491 (slot-set! class 'direct-methods '())
1492 (slot-set! class 'cpl (compute-cpl class))
1493 (slot-set! class 'redefined #f)
1494 (let ((slots (compute-slots class)))
1495 (slot-set! class 'slots slots)
1496 (slot-set! class 'nfields 0)
1497 (slot-set! class 'getters-n-setters (compute-getters-n-setters class
1498 slots))
1499 ;; Build getters - setters - accessors
1500 (compute-slot-accessors class slots))
1501
1502 ;; Update the "direct-subclasses" of each inherited classes
1503 (for-each (lambda (x)
1504 (slot-set! x
1505 'direct-subclasses
1506 (cons class (slot-ref x 'direct-subclasses))))
1507 supers)
1508
1509 ;; Support for the underlying structs:
1510
1511 ;; Set the layout slot
1512 (%prep-layout! class)
1513 ;; Inherit class flags (invisible on scheme level) from supers
1514 (%inherit-magic! class supers)))
1515
1516 (define (initialize-object-procedure object initargs)
1517 (let ((proc (get-keyword #:procedure initargs #f)))
1518 (cond ((not proc))
1519 ((pair? proc)
1520 (apply slot-set! object 'procedure proc))
1521 (else
1522 (slot-set! object 'procedure proc)))))
1523
1524 (define-method (initialize (applicable-struct <applicable-struct>) initargs)
1525 (next-method)
1526 (initialize-object-procedure applicable-struct initargs))
1527
1528 (define-method (initialize (generic <generic>) initargs)
1529 (let ((previous-definition (get-keyword #:default initargs #f))
1530 (name (get-keyword #:name initargs #f)))
1531 (next-method)
1532 (slot-set! generic 'methods (if (is-a? previous-definition <procedure>)
1533 (list (method args
1534 (apply previous-definition args)))
1535 '()))
1536 (if name
1537 (set-procedure-property! generic 'name name))
1538 ))
1539
1540 (define-method (initialize (gws <generic-with-setter>) initargs)
1541 (next-method)
1542 (%set-object-setter! gws (get-keyword #:setter initargs #f)))
1543
1544 (define-method (initialize (eg <extended-generic>) initargs)
1545 (next-method)
1546 (slot-set! eg 'extends (get-keyword #:extends initargs '())))
1547
1548 (define dummy-procedure (lambda args *unspecified*))
1549
1550 (define-method (initialize (method <method>) initargs)
1551 (next-method)
1552 (slot-set! method 'generic-function (get-keyword #:generic-function initargs #f))
1553 (slot-set! method 'specializers (get-keyword #:specializers initargs '()))
1554 (slot-set! method 'procedure
1555 (get-keyword #:procedure initargs #f))
1556 (slot-set! method 'formals (get-keyword #:formals initargs '()))
1557 (slot-set! method 'body (get-keyword #:body initargs '()))
1558 (slot-set! method 'make-procedure (get-keyword #:make-procedure initargs #f)))
1559
1560
1561 ;;;
1562 ;;; {Change-class}
1563 ;;;
1564
1565 (define (change-object-class old-instance old-class new-class)
1566 (let ((new-instance (allocate-instance new-class '())))
1567 ;; Initialize the slots of the new instance
1568 (for-each (lambda (slot)
1569 (if (and (slot-exists-using-class? old-class old-instance slot)
1570 (eq? (slot-definition-allocation
1571 (class-slot-definition old-class slot))
1572 #:instance)
1573 (slot-bound-using-class? old-class old-instance slot))
1574 ;; Slot was present and allocated in old instance; copy it
1575 (slot-set-using-class!
1576 new-class
1577 new-instance
1578 slot
1579 (slot-ref-using-class old-class old-instance slot))
1580 ;; slot was absent; initialize it with its default value
1581 (let ((init (slot-init-function new-class slot)))
1582 (if init
1583 (slot-set-using-class!
1584 new-class
1585 new-instance
1586 slot
1587 (apply init '()))))))
1588 (map slot-definition-name (class-slots new-class)))
1589 ;; Exchange old and new instance in place to keep pointers valid
1590 (%modify-instance old-instance new-instance)
1591 ;; Allow class specific updates of instances (which now are swapped)
1592 (update-instance-for-different-class new-instance old-instance)
1593 old-instance))
1594
1595
1596 (define-method (update-instance-for-different-class (old-instance <object>)
1597 (new-instance
1598 <object>))
1599 ;;not really important what we do, we just need a default method
1600 new-instance)
1601
1602 (define-method (change-class (old-instance <object>) (new-class <class>))
1603 (change-object-class old-instance (class-of old-instance) new-class))
1604
1605 ;;;
1606 ;;; {make}
1607 ;;;
1608 ;;; A new definition which overwrites the previous one which was built-in
1609 ;;;
1610
1611 (define-method (allocate-instance (class <class>) initargs)
1612 (%allocate-instance class initargs))
1613
1614 (define-method (make-instance (class <class>) . initargs)
1615 (let ((instance (allocate-instance class initargs)))
1616 (initialize instance initargs)
1617 instance))
1618
1619 (define make make-instance)
1620
1621 ;;;
1622 ;;; {apply-generic}
1623 ;;;
1624 ;;; Protocol for calling standard generic functions. This protocol is
1625 ;;; not used for real <generic> functions (in this case we use a
1626 ;;; completely C hard-coded protocol). Apply-generic is used by
1627 ;;; goops for calls to subclasses of <generic> and <generic-with-setter>.
1628 ;;; The code below is similar to the first MOP described in AMOP. In
1629 ;;; particular, it doesn't used the currified approach to gf
1630 ;;; call. There are 2 reasons for that:
1631 ;;; - the protocol below is exposed to mimic completely the one written in C
1632 ;;; - the currified protocol would be imho inefficient in C.
1633 ;;;
1634
1635 (define-method (apply-generic (gf <generic>) args)
1636 (if (null? (slot-ref gf 'methods))
1637 (no-method gf args))
1638 (let ((methods (compute-applicable-methods gf args)))
1639 (if methods
1640 (apply-methods gf (sort-applicable-methods gf methods args) args)
1641 (no-applicable-method gf args))))
1642
1643 ;; compute-applicable-methods is bound to %compute-applicable-methods.
1644 ;; *fixme* use let
1645 (define %%compute-applicable-methods
1646 (make <generic> #:name 'compute-applicable-methods))
1647
1648 (define-method (%%compute-applicable-methods (gf <generic>) args)
1649 (%compute-applicable-methods gf args))
1650
1651 (set! compute-applicable-methods %%compute-applicable-methods)
1652
1653 (define-method (sort-applicable-methods (gf <generic>) methods args)
1654 (let ((targs (map class-of args)))
1655 (sort methods (lambda (m1 m2) (method-more-specific? m1 m2 targs)))))
1656
1657 (define-method (method-more-specific? (m1 <method>) (m2 <method>) targs)
1658 (%method-more-specific? m1 m2 targs))
1659
1660 (define-method (apply-method (gf <generic>) methods build-next args)
1661 (apply (method-procedure (car methods))
1662 (build-next (cdr methods) args)
1663 args))
1664
1665 (define-method (apply-methods (gf <generic>) (l <list>) args)
1666 (letrec ((next (lambda (procs args)
1667 (lambda new-args
1668 (let ((a (if (null? new-args) args new-args)))
1669 (if (null? procs)
1670 (no-next-method gf a)
1671 (apply-method gf procs next a)))))))
1672 (apply-method gf l next args)))
1673
1674 ;; We don't want the following procedure to turn up in backtraces:
1675 (for-each (lambda (proc)
1676 (set-procedure-property! proc 'system-procedure #t))
1677 (list slot-unbound
1678 slot-missing
1679 no-next-method
1680 no-applicable-method
1681 no-method
1682 ))
1683
1684 ;;;
1685 ;;; {<composite-metaclass> and <active-metaclass>}
1686 ;;;
1687
1688 ;(autoload "active-slot" <active-metaclass>)
1689 ;(autoload "composite-slot" <composite-metaclass>)
1690 ;(export <composite-metaclass> <active-metaclass>)
1691
1692 ;;;
1693 ;;; {Tools}
1694 ;;;
1695
1696 ;; list2set
1697 ;;
1698 ;; duplicate the standard list->set function but using eq instead of
1699 ;; eqv which really sucks a lot, uselessly here
1700 ;;
1701 (define (list2set l)
1702 (let loop ((l l)
1703 (res '()))
1704 (cond
1705 ((null? l) res)
1706 ((memq (car l) res) (loop (cdr l) res))
1707 (else (loop (cdr l) (cons (car l) res))))))
1708
1709 (define (class-subclasses c)
1710 (letrec ((allsubs (lambda (c)
1711 (cons c (mapappend allsubs
1712 (class-direct-subclasses c))))))
1713 (list2set (cdr (allsubs c)))))
1714
1715 (define (class-methods c)
1716 (list2set (mapappend class-direct-methods
1717 (cons c (class-subclasses c)))))
1718
1719 ;;;
1720 ;;; {Final initialization}
1721 ;;;
1722
1723 ;; Tell C code that the main bulk of Goops has been loaded
1724 (%goops-loaded)
1725
1726
1727 \f
1728
1729 ;;;
1730 ;;; {SMOB and port classes}
1731 ;;;
1732
1733 (define <arbiter> (find-subclass <top> '<arbiter>))
1734 (define <promise> (find-subclass <top> '<promise>))
1735 (define <thread> (find-subclass <top> '<thread>))
1736 (define <mutex> (find-subclass <top> '<mutex>))
1737 (define <condition-variable> (find-subclass <top> '<condition-variable>))
1738 (define <regexp> (find-subclass <top> '<regexp>))
1739 (define <hook> (find-subclass <top> '<hook>))
1740 (define <bitvector> (find-subclass <top> '<bitvector>))
1741 (define <random-state> (find-subclass <top> '<random-state>))
1742 (define <async> (find-subclass <top> '<async>))
1743 (define <directory> (find-subclass <top> '<directory>))
1744 (define <array> (find-subclass <top> '<array>))
1745 (define <character-set> (find-subclass <top> '<character-set>))
1746 (define <dynamic-object> (find-subclass <top> '<dynamic-object>))
1747 (define <guardian> (find-subclass <applicable> '<guardian>))
1748 (define <macro> (find-subclass <top> '<macro>))
1749
1750 (define (define-class-subtree class)
1751 (define! (class-name class) class)
1752 (for-each define-class-subtree (class-direct-subclasses class)))
1753
1754 (define-class-subtree (find-subclass <port> '<file-port>))