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