Export `<macro>' from (oop goops).
[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 ;; Macros.
48 <macro>
49
50 ;; Applicable objects, either procedures or applicable structs.
51 <procedure-class> <applicable>
52 <procedure> <primitive-generic>
53
54 ;; Applicable structs.
55 <applicable-struct-class>
56 <applicable-struct>
57 <generic> <extended-generic>
58 <generic-with-setter> <extended-generic-with-setter>
59 <accessor> <extended-accessor>
60
61 ;; Types with their own allocated typecodes.
62 <boolean> <char> <list> <pair> <null> <string> <symbol>
63 <vector> <bytevector> <uvec> <foreign> <hashtable>
64 <fluid> <dynamic-state> <frame> <objcode> <vm> <vm-continuation>
65
66 ;; Numbers.
67 <number> <complex> <real> <integer> <fraction>
68
69 ;; Unknown.
70 <unknown>
71
72 ;; Particular SMOB data types. All SMOB types have
73 ;; corresponding classes, which may be obtained via class-of,
74 ;; once you have an instance. Perhaps FIXME to provide a
75 ;; smob-type-name->class procedure.
76 <arbiter> <promise> <thread> <mutex> <condition-variable>
77 <regexp> <hook> <bitvector> <random-state> <async>
78 <directory> <keyword> <array> <character-set>
79 <dynamic-object> <guardian>
80
81 ;; Modules.
82 <module>
83
84 ;; Ports.
85 <port> <input-port> <output-port> <input-output-port>
86
87 ;; Like SMOB types, all port types have their own classes,
88 ;; which can be accessed via `class-of' once you have an
89 ;; instance. Here we export bindings just for file ports.
90 <file-port>
91 <file-input-port> <file-output-port> <file-input-output-port>
92
93 is-a? class-of
94 ensure-metaclass ensure-metaclass-with-supers
95 make-class
96 make-generic ensure-generic
97 make-extended-generic
98 make-accessor ensure-accessor
99 add-method!
100 class-slot-ref class-slot-set! slot-unbound slot-missing
101 slot-definition-name slot-definition-options
102 slot-definition-allocation
103
104 slot-definition-getter slot-definition-setter
105 slot-definition-accessor
106 slot-definition-init-value slot-definition-init-form
107 slot-definition-init-thunk slot-definition-init-keyword
108 slot-init-function class-slot-definition
109 method-source
110 compute-cpl compute-std-cpl compute-get-n-set compute-slots
111 compute-getter-method compute-setter-method
112 allocate-instance initialize make-instance make
113 no-next-method no-applicable-method no-method
114 change-class update-instance-for-different-class
115 shallow-clone deep-clone
116 class-redefinition
117 apply-generic apply-method apply-methods
118 compute-applicable-methods %compute-applicable-methods
119 method-more-specific? sort-applicable-methods
120 class-subclasses class-methods
121 goops-error
122 min-fixnum max-fixnum
123
124 ;;; *fixme* Should go into goops.c
125 instance? slot-ref-using-class
126 slot-set-using-class! slot-bound-using-class?
127 slot-exists-using-class? slot-ref slot-set! slot-bound?
128 class-name class-direct-supers class-direct-subclasses
129 class-direct-methods class-direct-slots class-precedence-list
130 class-slots
131 generic-function-name
132 generic-function-methods method-generic-function
133 method-specializers method-formals
134 primitive-generic-generic enable-primitive-generic!
135 method-procedure accessor-method-slot-definition
136 slot-exists? make find-method get-keyword)
137 #:no-backtrace)
138
139 (define *goops-module* (current-module))
140
141 ;; First initialize the builtin part of GOOPS
142 (eval-when (eval load compile)
143 (%init-goops-builtins))
144
145 (eval-when (eval load compile)
146 (use-modules ((language tree-il primitives) :select (add-interesting-primitive!)))
147 (add-interesting-primitive! 'class-of)
148 (define (@slot-ref o n)
149 (struct-ref o n))
150 (define (@slot-set! o n v)
151 (struct-set! o n v))
152 (add-interesting-primitive! '@slot-ref)
153 (add-interesting-primitive! '@slot-set!))
154
155 ;; Then load the rest of GOOPS
156 (use-modules (oop goops util)
157 (oop goops dispatch)
158 (oop goops compile))
159
160 \f
161 ;; FIXME: deprecate.
162 (eval-when (eval load compile)
163 (define min-fixnum (- (expt 2 29)))
164 (define max-fixnum (- (expt 2 29) 1)))
165
166 ;;
167 ;; goops-error
168 ;;
169 (define (goops-error format-string . args)
170 (scm-error 'goops-error #f format-string args '()))
171
172 ;;
173 ;; is-a?
174 ;;
175 (define (is-a? obj class)
176 (and (memq class (class-precedence-list (class-of obj))) #t))
177
178
179 ;;;
180 ;;; {Meta classes}
181 ;;;
182
183 (define ensure-metaclass-with-supers
184 (let ((table-of-metas '()))
185 (lambda (meta-supers)
186 (let ((entry (assoc meta-supers table-of-metas)))
187 (if entry
188 ;; Found a previously created metaclass
189 (cdr entry)
190 ;; Create a new meta-class which inherit from "meta-supers"
191 (let ((new (make <class> #:dsupers meta-supers
192 #:slots '()
193 #:name (gensym "metaclass"))))
194 (set! table-of-metas (cons (cons meta-supers new) table-of-metas))
195 new))))))
196
197 (define (ensure-metaclass supers)
198 (if (null? supers)
199 <class>
200 (let* ((all-metas (map (lambda (x) (class-of x)) supers))
201 (all-cpls (append-map (lambda (m)
202 (cdr (class-precedence-list m)))
203 all-metas))
204 (needed-metas '()))
205 ;; Find the most specific metaclasses. The new metaclass will be
206 ;; a subclass of these.
207 (for-each
208 (lambda (meta)
209 (if (and (not (member meta all-cpls))
210 (not (member meta needed-metas)))
211 (set! needed-metas (append needed-metas (list meta)))))
212 all-metas)
213 ;; Now return a subclass of the metaclasses we found.
214 (if (null? (cdr needed-metas))
215 (car needed-metas) ; If there's only one, just use it.
216 (ensure-metaclass-with-supers needed-metas)))))
217
218 ;;;
219 ;;; {Classes}
220 ;;;
221
222 ;;; (define-class NAME (SUPER ...) SLOT-DEFINITION ... OPTION ...)
223 ;;;
224 ;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
225 ;;; OPTION ::= KEYWORD VALUE
226 ;;;
227
228 (define (kw-do-map mapper f kwargs)
229 (define (keywords l)
230 (cond
231 ((null? l) '())
232 ((or (null? (cdr l)) (not (keyword? (car l))))
233 (goops-error "malformed keyword arguments: ~a" kwargs))
234 (else (cons (car l) (keywords (cddr l))))))
235 (define (args l)
236 (if (null? l) '() (cons (cadr l) (args (cddr l)))))
237 ;; let* to check keywords first
238 (let* ((k (keywords kwargs))
239 (a (args kwargs)))
240 (mapper f k a)))
241
242 (define (make-class supers slots . options)
243 (let* ((name (get-keyword #:name options (make-unbound)))
244 (supers (if (not (or-map (lambda (class)
245 (memq <object>
246 (class-precedence-list class)))
247 supers))
248 (append supers (list <object>))
249 supers))
250 (metaclass (or (get-keyword #:metaclass options #f)
251 (ensure-metaclass supers))))
252
253 ;; Verify that all direct slots are different and that we don't inherit
254 ;; several time from the same class
255 (let ((tmp1 (find-duplicate supers))
256 (tmp2 (find-duplicate (map slot-definition-name slots))))
257 (if tmp1
258 (goops-error "make-class: super class ~S is duplicate in class ~S"
259 tmp1 name))
260 (if tmp2
261 (goops-error "make-class: slot ~S is duplicate in class ~S"
262 tmp2 name)))
263
264 ;; Everything seems correct, build the class
265 (apply make metaclass
266 #:dsupers supers
267 #:slots slots
268 #:name name
269 options)))
270
271 ;;; (class (SUPER ...) SLOT-DEFINITION ... OPTION ...)
272 ;;;
273 ;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
274 ;;; OPTION ::= KEYWORD VALUE
275 ;;;
276 (define-macro (class supers . slots)
277 (define (make-slot-definition-forms slots)
278 (map
279 (lambda (def)
280 (cond
281 ((pair? def)
282 `(list ',(car def)
283 ,@(kw-do-map append-map
284 (lambda (kw arg)
285 (case kw
286 ((#:init-form)
287 `(#:init-form ',arg
288 #:init-thunk (lambda () ,arg)))
289 (else (list kw arg))))
290 (cdr def))))
291 (else
292 `(list ',def))))
293 slots))
294 (if (not (list? supers))
295 (goops-error "malformed superclass list: ~S" supers))
296 (let ((slots (take-while (lambda (x) (not (keyword? x))) slots))
297 (options (or (find-tail keyword? slots) '())))
298 `(make-class
299 ;; evaluate super class variables
300 (list ,@supers)
301 ;; evaluate slot definitions, except the slot name!
302 (list ,@(make-slot-definition-forms slots))
303 ;; evaluate class options
304 ,@options)))
305
306 (define-syntax define-class-pre-definition
307 (lambda (x)
308 (syntax-case x ()
309 ((_ (k arg rest ...) out ...)
310 (keyword? (syntax->datum #'k))
311 (case (syntax->datum #'k)
312 ((#:getter #:setter)
313 #'(define-class-pre-definition (rest ...)
314 out ...
315 (if (or (not (defined? 'arg))
316 (not (is-a? arg <generic>)))
317 (toplevel-define!
318 'arg
319 (ensure-generic (if (defined? 'arg) arg #f) 'arg)))))
320 ((#:accessor)
321 #'(define-class-pre-definition (rest ...)
322 out ...
323 (if (or (not (defined? 'arg))
324 (not (is-a? arg <accessor>)))
325 (toplevel-define!
326 'arg
327 (ensure-accessor (if (defined? 'arg) arg #f) 'arg)))))
328 (else
329 #'(define-class-pre-definition (rest ...) out ...))))
330 ((_ () out ...)
331 #'(begin out ...)))))
332
333 ;; Some slot options require extra definitions to be made. In
334 ;; particular, we want to make sure that the generic function objects
335 ;; which represent accessors exist before `make-class' tries to add
336 ;; methods to them.
337 (define-syntax define-class-pre-definitions
338 (lambda (x)
339 (syntax-case x ()
340 ((_ () out ...)
341 #'(begin out ...))
342 ((_ (slot rest ...) out ...)
343 (keyword? (syntax->datum #'slot))
344 #'(begin out ...))
345 ((_ (slot rest ...) out ...)
346 (identifier? #'slot)
347 #'(define-class-pre-definitions (rest ...)
348 out ...))
349 ((_ ((slotname slotopt ...) rest ...) out ...)
350 #'(define-class-pre-definitions (rest ...)
351 out ... (define-class-pre-definition (slotopt ...)))))))
352
353 (define-syntax define-class
354 (syntax-rules ()
355 ((_ name supers slot ...)
356 (begin
357 (define-class-pre-definitions (slot ...))
358 (if (and (defined? 'name)
359 (is-a? name <class>)
360 (memq <object> (class-precedence-list name)))
361 (class-redefinition name
362 (class supers slot ... #:name 'name))
363 (toplevel-define! 'name (class supers slot ... #:name 'name)))))))
364
365 (define-syntax standard-define-class
366 (syntax-rules ()
367 ((_ arg ...) (define-class arg ...))))
368
369 ;;;
370 ;;; {Generic functions and accessors}
371 ;;;
372
373 ;; Apparently the desired semantics are that we extend previous
374 ;; procedural definitions, but that if `name' was already a generic, we
375 ;; overwrite its definition.
376 (define-macro (define-generic name)
377 (if (not (symbol? name))
378 (goops-error "bad generic function name: ~S" name))
379 `(define ,name
380 (if (and (defined? ',name) (is-a? ,name <generic>))
381 (make <generic> #:name ',name)
382 (ensure-generic (if (defined? ',name) ,name #f) ',name))))
383
384 (define-macro (define-extended-generic name val)
385 (if (not (symbol? name))
386 (goops-error "bad generic function name: ~S" name))
387 `(define ,name (make-extended-generic ,val ',name)))
388
389 (define-macro (define-extended-generics names . args)
390 (let ((prefixes (get-keyword #:prefix args #f)))
391 (if prefixes
392 `(begin
393 ,@(map (lambda (name)
394 `(define-extended-generic ,name
395 (list ,@(map (lambda (prefix)
396 (symbol-append prefix name))
397 prefixes))))
398 names))
399 (goops-error "no prefixes supplied"))))
400
401 (define (make-generic . name)
402 (let ((name (and (pair? name) (car name))))
403 (make <generic> #:name name)))
404
405 (define (make-extended-generic gfs . name)
406 (let* ((name (and (pair? name) (car name)))
407 (gfs (if (pair? gfs) gfs (list gfs)))
408 (gws? (any (lambda (gf) (is-a? gf <generic-with-setter>)) gfs)))
409 (let ((ans (if gws?
410 (let* ((sname (and name (make-setter-name name)))
411 (setters
412 (append-map (lambda (gf)
413 (if (is-a? gf <generic-with-setter>)
414 (list (ensure-generic (setter gf)
415 sname))
416 '()))
417 gfs))
418 (es (make <extended-generic-with-setter>
419 #:name name
420 #:extends gfs
421 #:setter (make <extended-generic>
422 #:name sname
423 #:extends setters))))
424 (extended-by! setters (setter es))
425 es)
426 (make <extended-generic>
427 #:name name
428 #:extends gfs))))
429 (extended-by! gfs ans)
430 ans)))
431
432 (define (extended-by! gfs eg)
433 (for-each (lambda (gf)
434 (slot-set! gf 'extended-by
435 (cons eg (slot-ref gf 'extended-by))))
436 gfs))
437
438 (define (not-extended-by! gfs eg)
439 (for-each (lambda (gf)
440 (slot-set! gf 'extended-by
441 (delq! eg (slot-ref gf 'extended-by))))
442 gfs))
443
444 (define (ensure-generic old-definition . name)
445 (let ((name (and (pair? name) (car name))))
446 (cond ((is-a? old-definition <generic>) old-definition)
447 ((procedure-with-setter? old-definition)
448 (make <generic-with-setter>
449 #:name name
450 #:default (procedure old-definition)
451 #:setter (setter old-definition)))
452 ((procedure? old-definition)
453 (if (generic-capability? old-definition) old-definition
454 (make <generic> #:name name #:default old-definition)))
455 (else (make <generic> #:name name)))))
456
457 ;; same semantics as <generic>
458 (define-syntax define-accessor
459 (syntax-rules ()
460 ((_ name)
461 (define name
462 (cond ((not (defined? 'name)) (ensure-accessor #f 'name))
463 ((is-a? name <accessor>) (make <accessor> #:name 'name))
464 (else (ensure-accessor name 'name)))))))
465
466 (define (make-setter-name name)
467 (string->symbol (string-append "setter:" (symbol->string name))))
468
469 (define (make-accessor . name)
470 (let ((name (and (pair? name) (car name))))
471 (make <accessor>
472 #:name name
473 #:setter (make <generic>
474 #:name (and name (make-setter-name name))))))
475
476 (define (ensure-accessor proc . name)
477 (let ((name (and (pair? name) (car name))))
478 (cond ((and (is-a? proc <accessor>)
479 (is-a? (setter proc) <generic>))
480 proc)
481 ((is-a? proc <generic-with-setter>)
482 (upgrade-accessor proc (setter proc)))
483 ((is-a? proc <generic>)
484 (upgrade-accessor proc (make-generic name)))
485 ((procedure-with-setter? proc)
486 (make <accessor>
487 #:name name
488 #:default (procedure proc)
489 #:setter (ensure-generic (setter proc) name)))
490 ((procedure? proc)
491 (ensure-accessor (if (generic-capability? proc)
492 (make <generic> #:name name #:default proc)
493 (ensure-generic proc name))
494 name))
495 (else
496 (make-accessor name)))))
497
498 (define (upgrade-accessor generic setter)
499 (let ((methods (slot-ref generic 'methods))
500 (gws (make (if (is-a? generic <extended-generic>)
501 <extended-generic-with-setter>
502 <accessor>)
503 #:name (generic-function-name generic)
504 #:extended-by (slot-ref generic 'extended-by)
505 #:setter setter)))
506 (if (is-a? generic <extended-generic>)
507 (let ((gfs (slot-ref generic 'extends)))
508 (not-extended-by! gfs generic)
509 (slot-set! gws 'extends gfs)
510 (extended-by! gfs gws)))
511 ;; Steal old methods
512 (for-each (lambda (method)
513 (slot-set! method 'generic-function gws))
514 methods)
515 (slot-set! gws 'methods methods)
516 gws))
517
518 ;;;
519 ;;; {Methods}
520 ;;;
521
522 (define (toplevel-define! name val)
523 (module-define! (current-module) name val))
524
525 (define-syntax define-method
526 (syntax-rules (setter)
527 ((_ ((setter name) . args) body ...)
528 (begin
529 (if (or (not (defined? 'name))
530 (not (is-a? name <accessor>)))
531 (toplevel-define! 'name
532 (ensure-accessor
533 (if (defined? 'name) name #f) 'name)))
534 (add-method! (setter name) (method args body ...))))
535 ((_ (name . args) body ...)
536 (begin
537 ;; FIXME: this code is how it always was, but it's quite cracky:
538 ;; it will only define the generic function if it was undefined
539 ;; before (ok), or *was defined to #f*. The latter is crack. But
540 ;; there are bootstrap issues about fixing this -- change it to
541 ;; (is-a? name <generic>) and see.
542 (if (or (not (defined? 'name))
543 (not name))
544 (toplevel-define! 'name (make <generic> #:name 'name)))
545 (add-method! name (method args body ...))))))
546
547 (define-syntax method
548 (lambda (x)
549 (define (parse-args args)
550 (let lp ((ls args) (formals '()) (specializers '()))
551 (syntax-case ls ()
552 (((f s) . rest)
553 (and (identifier? #'f) (identifier? #'s))
554 (lp #'rest
555 (cons #'f formals)
556 (cons #'s specializers)))
557 ((f . rest)
558 (identifier? #'f)
559 (lp #'rest
560 (cons #'f formals)
561 (cons #'<top> specializers)))
562 (()
563 (list (reverse formals)
564 (reverse (cons #''() specializers))))
565 (tail
566 (identifier? #'tail)
567 (list (append (reverse formals) #'tail)
568 (reverse (cons #'<top> specializers)))))))
569
570 (define (find-free-id exp referent)
571 (syntax-case exp ()
572 ((x . y)
573 (or (find-free-id #'x referent)
574 (find-free-id #'y referent)))
575 (x
576 (identifier? #'x)
577 (let ((id (datum->syntax #'x referent)))
578 (and (free-identifier=? #'x id) id)))
579 (_ #f)))
580
581 (define (compute-procedure formals body)
582 (syntax-case body ()
583 ((body0 ...)
584 (with-syntax ((formals formals))
585 #'(lambda formals body0 ...)))))
586
587 (define (->proper args)
588 (let lp ((ls args) (out '()))
589 (syntax-case ls ()
590 ((x . xs) (lp #'xs (cons #'x out)))
591 (() (reverse out))
592 (tail (reverse (cons #'tail out))))))
593
594 (define (compute-make-procedure formals body next-method)
595 (syntax-case body ()
596 ((body ...)
597 (with-syntax ((next-method next-method))
598 (syntax-case formals ()
599 ((formal ...)
600 #'(lambda (real-next-method)
601 (lambda (formal ...)
602 (let ((next-method (lambda args
603 (if (null? args)
604 (real-next-method formal ...)
605 (apply real-next-method args)))))
606 body ...))))
607 (formals
608 (with-syntax (((formal ...) (->proper #'formals)))
609 #'(lambda (real-next-method)
610 (lambda formals
611 (let ((next-method (lambda args
612 (if (null? args)
613 (apply real-next-method formal ...)
614 (apply real-next-method args)))))
615 body ...))))))))))
616
617 (define (compute-procedures formals body)
618 ;; So, our use of this is broken, because it operates on the
619 ;; pre-expansion source code. It's equivalent to just searching
620 ;; for referent in the datums. Ah well.
621 (let ((id (find-free-id body 'next-method)))
622 (if id
623 ;; return a make-procedure
624 (values #'#f
625 (compute-make-procedure formals body id))
626 (values (compute-procedure formals body)
627 #'#f))))
628
629 (syntax-case x ()
630 ((_ args) #'(method args (if #f #f)))
631 ((_ args body0 body1 ...)
632 (with-syntax (((formals (specializer ...)) (parse-args #'args)))
633 (call-with-values
634 (lambda ()
635 (compute-procedures #'formals #'(body0 body1 ...)))
636 (lambda (procedure make-procedure)
637 (with-syntax ((procedure procedure)
638 (make-procedure make-procedure))
639 #'(make <method>
640 #:specializers (cons* specializer ...)
641 #:formals 'formals
642 #:body '(body0 body1 ...)
643 #:make-procedure make-procedure
644 #:procedure procedure)))))))))
645
646 ;;;
647 ;;; {add-method!}
648 ;;;
649
650 (define (add-method-in-classes! m)
651 ;; Add method in all the classes which appears in its specializers list
652 (for-each* (lambda (x)
653 (let ((dm (class-direct-methods x)))
654 (if (not (memq m dm))
655 (slot-set! x 'direct-methods (cons m dm)))))
656 (method-specializers m)))
657
658 (define (remove-method-in-classes! m)
659 ;; Remove method in all the classes which appears in its specializers list
660 (for-each* (lambda (x)
661 (slot-set! x
662 'direct-methods
663 (delv! m (class-direct-methods x))))
664 (method-specializers m)))
665
666 (define (compute-new-list-of-methods gf new)
667 (let ((new-spec (method-specializers new))
668 (methods (slot-ref gf 'methods)))
669 (let loop ((l methods))
670 (if (null? l)
671 (cons new methods)
672 (if (equal? (method-specializers (car l)) new-spec)
673 (begin
674 ;; This spec. list already exists. Remove old method from dependents
675 (remove-method-in-classes! (car l))
676 (set-car! l new)
677 methods)
678 (loop (cdr l)))))))
679
680 (define internal-add-method!
681 (method ((gf <generic>) (m <method>))
682 (slot-set! m 'generic-function gf)
683 (slot-set! gf 'methods (compute-new-list-of-methods gf m))
684 (let ((specializers (slot-ref m 'specializers)))
685 (slot-set! gf 'n-specialized
686 (max (length* specializers)
687 (slot-ref gf 'n-specialized))))
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 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 ;; the idea is to compile the index into the procedure, for fastest
1197 ;; lookup. Also, @slot-ref and @slot-set! have their own bytecodes.
1198
1199 (eval-when (eval load compile)
1200 (define num-standard-pre-cache 20))
1201
1202 (define-macro (define-standard-accessor-method form . body)
1203 (let ((name (caar form))
1204 (n-var (cadar form))
1205 (args (cdr form)))
1206 (define (make-one x)
1207 (define (body-trans form)
1208 (cond ((not (pair? form)) form)
1209 ((eq? (car form) '@slot-ref)
1210 `(,(car form) ,(cadr form) ,x))
1211 ((eq? (car form) '@slot-set!)
1212 `(,(car form) ,(cadr form) ,x ,(cadddr form)))
1213 (else
1214 (map body-trans form))))
1215 `(lambda ,args ,@(map body-trans body)))
1216 `(define ,name
1217 (let ((cache (vector ,@(map make-one (iota num-standard-pre-cache)))))
1218 (lambda (n)
1219 (if (< n ,num-standard-pre-cache)
1220 (vector-ref cache n)
1221 ((lambda (,n-var) (lambda ,args ,@body)) n)))))))
1222
1223 (define-standard-accessor-method ((bound-check-get n) o)
1224 (let ((x (@slot-ref o n)))
1225 (if (unbound? x)
1226 (slot-unbound o)
1227 x)))
1228
1229 (define-standard-accessor-method ((standard-get n) o)
1230 (@slot-ref o n))
1231
1232 (define-standard-accessor-method ((standard-set n) o v)
1233 (@slot-set! o n v))
1234
1235 ;;; compute-getters-n-setters
1236 ;;;
1237 (define (compute-getters-n-setters class slots)
1238
1239 (define (compute-slot-init-function name s)
1240 (or (let ((thunk (slot-definition-init-thunk s)))
1241 (and thunk
1242 (if (thunk? thunk)
1243 thunk
1244 (goops-error "Bad init-thunk for slot `~S' in ~S: ~S"
1245 name class thunk))))
1246 (let ((init (slot-definition-init-value s)))
1247 (and (not (unbound? init))
1248 (lambda () init)))))
1249
1250 (define (verify-accessors slot l)
1251 (cond ((integer? l))
1252 ((not (and (list? l) (= (length l) 2)))
1253 (goops-error "Bad getter and setter for slot `~S' in ~S: ~S"
1254 slot class l))
1255 (else
1256 (let ((get (car l))
1257 (set (cadr l)))
1258 (if (not (procedure? get))
1259 (goops-error "Bad getter closure for slot `~S' in ~S: ~S"
1260 slot class get))
1261 (if (not (procedure? set))
1262 (goops-error "Bad setter closure for slot `~S' in ~S: ~S"
1263 slot class set))))))
1264
1265 (map (lambda (s)
1266 ;; The strange treatment of nfields is due to backward compatibility.
1267 (let* ((index (slot-ref class 'nfields))
1268 (g-n-s (compute-get-n-set class s))
1269 (size (- (slot-ref class 'nfields) index))
1270 (name (slot-definition-name s)))
1271 ;; NOTE: The following is interdependent with C macros
1272 ;; defined above goops.c:scm_sys_prep_layout_x.
1273 ;;
1274 ;; For simple instance slots, we have the simplest form
1275 ;; '(name init-function . index)
1276 ;; For other slots we have
1277 ;; '(name init-function getter setter . alloc)
1278 ;; where alloc is:
1279 ;; '(index size) for instance allocated slots
1280 ;; '() for other slots
1281 (verify-accessors name g-n-s)
1282 (cons name
1283 (cons (compute-slot-init-function name s)
1284 (if (or (integer? g-n-s)
1285 (zero? size))
1286 g-n-s
1287 (append g-n-s (list index size)))))))
1288 slots))
1289
1290 ;;; compute-cpl
1291 ;;;
1292 ;;; Correct behaviour:
1293 ;;;
1294 ;;; (define-class food ())
1295 ;;; (define-class fruit (food))
1296 ;;; (define-class spice (food))
1297 ;;; (define-class apple (fruit))
1298 ;;; (define-class cinnamon (spice))
1299 ;;; (define-class pie (apple cinnamon))
1300 ;;; => cpl (pie) = pie apple fruit cinnamon spice food object top
1301 ;;;
1302 ;;; (define-class d ())
1303 ;;; (define-class e ())
1304 ;;; (define-class f ())
1305 ;;; (define-class b (d e))
1306 ;;; (define-class c (e f))
1307 ;;; (define-class a (b c))
1308 ;;; => cpl (a) = a b d c e f object top
1309 ;;;
1310
1311 (define-method (compute-cpl (class <class>))
1312 (compute-std-cpl class class-direct-supers))
1313
1314 ;; Support
1315
1316 (define (only-non-null lst)
1317 (filter (lambda (l) (not (null? l))) lst))
1318
1319 (define (compute-std-cpl c get-direct-supers)
1320 (let ((c-direct-supers (get-direct-supers c)))
1321 (merge-lists (list c)
1322 (only-non-null (append (map class-precedence-list
1323 c-direct-supers)
1324 (list c-direct-supers))))))
1325
1326 (define (merge-lists reversed-partial-result inputs)
1327 (cond
1328 ((every null? inputs)
1329 (reverse! reversed-partial-result))
1330 (else
1331 (let* ((candidate (lambda (c)
1332 (and (not (any (lambda (l)
1333 (memq c (cdr l)))
1334 inputs))
1335 c)))
1336 (candidate-car (lambda (l)
1337 (and (not (null? l))
1338 (candidate (car l)))))
1339 (next (any candidate-car inputs)))
1340 (if (not next)
1341 (goops-error "merge-lists: Inconsistent precedence graph"))
1342 (let ((remove-next (lambda (l)
1343 (if (eq? (car l) next)
1344 (cdr l)
1345 l))))
1346 (merge-lists (cons next reversed-partial-result)
1347 (only-non-null (map remove-next inputs))))))))
1348
1349 ;; Modified from TinyClos:
1350 ;;
1351 ;; A simple topological sort.
1352 ;;
1353 ;; It's in this file so that both TinyClos and Objects can use it.
1354 ;;
1355 ;; This is a fairly modified version of code I originally got from Anurag
1356 ;; Mendhekar <anurag@moose.cs.indiana.edu>.
1357 ;;
1358
1359 (define (compute-clos-cpl c get-direct-supers)
1360 (top-sort ((build-transitive-closure get-direct-supers) c)
1361 ((build-constraints get-direct-supers) c)
1362 (std-tie-breaker get-direct-supers)))
1363
1364
1365 (define (top-sort elements constraints tie-breaker)
1366 (let loop ((elements elements)
1367 (constraints constraints)
1368 (result '()))
1369 (if (null? elements)
1370 result
1371 (let ((can-go-in-now
1372 (filter
1373 (lambda (x)
1374 (every (lambda (constraint)
1375 (or (not (eq? (cadr constraint) x))
1376 (memq (car constraint) result)))
1377 constraints))
1378 elements)))
1379 (if (null? can-go-in-now)
1380 (goops-error "top-sort: Invalid constraints")
1381 (let ((choice (if (null? (cdr can-go-in-now))
1382 (car can-go-in-now)
1383 (tie-breaker result
1384 can-go-in-now))))
1385 (loop
1386 (filter (lambda (x) (not (eq? x choice)))
1387 elements)
1388 constraints
1389 (append result (list choice)))))))))
1390
1391 (define (std-tie-breaker get-supers)
1392 (lambda (partial-cpl min-elts)
1393 (let loop ((pcpl (reverse partial-cpl)))
1394 (let ((current-elt (car pcpl)))
1395 (let ((ds-of-ce (get-supers current-elt)))
1396 (let ((common (filter (lambda (x)
1397 (memq x ds-of-ce))
1398 min-elts)))
1399 (if (null? common)
1400 (if (null? (cdr pcpl))
1401 (goops-error "std-tie-breaker: Nothing valid")
1402 (loop (cdr pcpl)))
1403 (car common))))))))
1404
1405
1406 (define (build-transitive-closure get-follow-ons)
1407 (lambda (x)
1408 (let track ((result '())
1409 (pending (list x)))
1410 (if (null? pending)
1411 result
1412 (let ((next (car pending)))
1413 (if (memq next result)
1414 (track result (cdr pending))
1415 (track (cons next result)
1416 (append (get-follow-ons next)
1417 (cdr pending)))))))))
1418
1419 (define (build-constraints get-follow-ons)
1420 (lambda (x)
1421 (let loop ((elements ((build-transitive-closure get-follow-ons) x))
1422 (this-one '())
1423 (result '()))
1424 (if (or (null? this-one) (null? (cdr this-one)))
1425 (if (null? elements)
1426 result
1427 (loop (cdr elements)
1428 (cons (car elements)
1429 (get-follow-ons (car elements)))
1430 result))
1431 (loop elements
1432 (cdr this-one)
1433 (cons (list (car this-one) (cadr this-one))
1434 result))))))
1435
1436 ;;; compute-get-n-set
1437 ;;;
1438 (define-method (compute-get-n-set (class <class>) s)
1439 (case (slot-definition-allocation s)
1440 ((#:instance) ;; Instance slot
1441 ;; get-n-set is just its offset
1442 (let ((already-allocated (slot-ref class 'nfields)))
1443 (slot-set! class 'nfields (+ already-allocated 1))
1444 already-allocated))
1445
1446 ((#:class) ;; Class slot
1447 ;; Class-slots accessors are implemented as 2 closures around
1448 ;; a Scheme variable. As instance slots, class slots must be
1449 ;; unbound at init time.
1450 (let ((name (slot-definition-name s)))
1451 (if (memq name (map slot-definition-name (class-direct-slots class)))
1452 ;; This slot is direct; create a new shared variable
1453 (make-closure-variable class)
1454 ;; Slot is inherited. Find its definition in superclass
1455 (let loop ((l (cdr (class-precedence-list class))))
1456 (let ((r (assoc name (slot-ref (car l) 'getters-n-setters))))
1457 (if r
1458 (cddr r)
1459 (loop (cdr l))))))))
1460
1461 ((#:each-subclass) ;; slot shared by instances of direct subclass.
1462 ;; (Thomas Buerger, April 1998)
1463 (make-closure-variable class))
1464
1465 ((#:virtual) ;; No allocation
1466 ;; slot-ref and slot-set! function must be given by the user
1467 (let ((get (get-keyword #:slot-ref (slot-definition-options s) #f))
1468 (set (get-keyword #:slot-set! (slot-definition-options s) #f)))
1469 (if (not (and get set))
1470 (goops-error "You must supply a #:slot-ref and a #:slot-set! in ~S"
1471 s))
1472 (list get set)))
1473 (else (next-method))))
1474
1475 (define (make-closure-variable class)
1476 (let ((shared-variable (make-unbound)))
1477 (list (lambda (o) shared-variable)
1478 (lambda (o v) (set! shared-variable v)))))
1479
1480 (define-method (compute-get-n-set (o <object>) s)
1481 (goops-error "Allocation \"~S\" is unknown" (slot-definition-allocation s)))
1482
1483 (define-method (compute-slots (class <class>))
1484 (%compute-slots class))
1485
1486 ;;;
1487 ;;; {Initialize}
1488 ;;;
1489
1490 (define-method (initialize (object <object>) initargs)
1491 (%initialize-object object initargs))
1492
1493 (define-method (initialize (class <class>) initargs)
1494 (next-method)
1495 (let ((dslots (get-keyword #:slots initargs '()))
1496 (supers (get-keyword #:dsupers initargs '())))
1497 (slot-set! class 'name (get-keyword #:name initargs '???))
1498 (slot-set! class 'direct-supers supers)
1499 (slot-set! class 'direct-slots dslots)
1500 (slot-set! class 'direct-subclasses '())
1501 (slot-set! class 'direct-methods '())
1502 (slot-set! class 'cpl (compute-cpl class))
1503 (slot-set! class 'redefined #f)
1504 (let ((slots (compute-slots class)))
1505 (slot-set! class 'slots slots)
1506 (slot-set! class 'nfields 0)
1507 (slot-set! class 'getters-n-setters (compute-getters-n-setters class
1508 slots))
1509 ;; Build getters - setters - accessors
1510 (compute-slot-accessors class slots))
1511
1512 ;; Update the "direct-subclasses" of each inherited classes
1513 (for-each (lambda (x)
1514 (slot-set! x
1515 'direct-subclasses
1516 (cons class (slot-ref x 'direct-subclasses))))
1517 supers)
1518
1519 ;; Support for the underlying structs:
1520
1521 ;; Set the layout slot
1522 (%prep-layout! class)
1523 ;; Inherit class flags (invisible on scheme level) from supers
1524 (%inherit-magic! class supers)))
1525
1526 (define (initialize-object-procedure object initargs)
1527 (let ((proc (get-keyword #:procedure initargs #f)))
1528 (cond ((not proc))
1529 ((pair? proc)
1530 (apply slot-set! object 'procedure proc))
1531 (else
1532 (slot-set! object 'procedure proc)))))
1533
1534 (define-method (initialize (applicable-struct <applicable-struct>) initargs)
1535 (next-method)
1536 (initialize-object-procedure applicable-struct initargs))
1537
1538 (define-method (initialize (generic <generic>) initargs)
1539 (let ((previous-definition (get-keyword #:default initargs #f))
1540 (name (get-keyword #:name initargs #f)))
1541 (next-method)
1542 (slot-set! generic 'methods (if (is-a? previous-definition <procedure>)
1543 (list (method args
1544 (apply previous-definition args)))
1545 '()))
1546 (if name
1547 (set-procedure-property! generic 'name name))
1548 ))
1549
1550 (define-method (initialize (gws <generic-with-setter>) initargs)
1551 (next-method)
1552 (%set-object-setter! gws (get-keyword #:setter initargs #f)))
1553
1554 (define-method (initialize (eg <extended-generic>) initargs)
1555 (next-method)
1556 (slot-set! eg 'extends (get-keyword #:extends initargs '())))
1557
1558 (define dummy-procedure (lambda args *unspecified*))
1559
1560 (define-method (initialize (method <method>) initargs)
1561 (next-method)
1562 (slot-set! method 'generic-function (get-keyword #:generic-function initargs #f))
1563 (slot-set! method 'specializers (get-keyword #:specializers initargs '()))
1564 (slot-set! method 'procedure
1565 (get-keyword #:procedure initargs #f))
1566 (slot-set! method 'formals (get-keyword #:formals initargs '()))
1567 (slot-set! method 'body (get-keyword #:body initargs '()))
1568 (slot-set! method 'make-procedure (get-keyword #:make-procedure initargs #f)))
1569
1570
1571 ;;;
1572 ;;; {Change-class}
1573 ;;;
1574
1575 (define (change-object-class old-instance old-class new-class)
1576 (let ((new-instance (allocate-instance new-class '())))
1577 ;; Initialize the slots of the new instance
1578 (for-each (lambda (slot)
1579 (if (and (slot-exists-using-class? old-class old-instance slot)
1580 (eq? (slot-definition-allocation
1581 (class-slot-definition old-class slot))
1582 #:instance)
1583 (slot-bound-using-class? old-class old-instance slot))
1584 ;; Slot was present and allocated in old instance; copy it
1585 (slot-set-using-class!
1586 new-class
1587 new-instance
1588 slot
1589 (slot-ref-using-class old-class old-instance slot))
1590 ;; slot was absent; initialize it with its default value
1591 (let ((init (slot-init-function new-class slot)))
1592 (if init
1593 (slot-set-using-class!
1594 new-class
1595 new-instance
1596 slot
1597 (apply init '()))))))
1598 (map slot-definition-name (class-slots new-class)))
1599 ;; Exchange old and new instance in place to keep pointers valid
1600 (%modify-instance old-instance new-instance)
1601 ;; Allow class specific updates of instances (which now are swapped)
1602 (update-instance-for-different-class new-instance old-instance)
1603 old-instance))
1604
1605
1606 (define-method (update-instance-for-different-class (old-instance <object>)
1607 (new-instance
1608 <object>))
1609 ;;not really important what we do, we just need a default method
1610 new-instance)
1611
1612 (define-method (change-class (old-instance <object>) (new-class <class>))
1613 (change-object-class old-instance (class-of old-instance) new-class))
1614
1615 ;;;
1616 ;;; {make}
1617 ;;;
1618 ;;; A new definition which overwrites the previous one which was built-in
1619 ;;;
1620
1621 (define-method (allocate-instance (class <class>) initargs)
1622 (%allocate-instance class initargs))
1623
1624 (define-method (make-instance (class <class>) . initargs)
1625 (let ((instance (allocate-instance class initargs)))
1626 (initialize instance initargs)
1627 instance))
1628
1629 (define make make-instance)
1630
1631 ;;;
1632 ;;; {apply-generic}
1633 ;;;
1634 ;;; Protocol for calling standard generic functions. This protocol is
1635 ;;; not used for real <generic> functions (in this case we use a
1636 ;;; completely C hard-coded protocol). Apply-generic is used by
1637 ;;; goops for calls to subclasses of <generic> and <generic-with-setter>.
1638 ;;; The code below is similar to the first MOP described in AMOP. In
1639 ;;; particular, it doesn't used the currified approach to gf
1640 ;;; call. There are 2 reasons for that:
1641 ;;; - the protocol below is exposed to mimic completely the one written in C
1642 ;;; - the currified protocol would be imho inefficient in C.
1643 ;;;
1644
1645 (define-method (apply-generic (gf <generic>) args)
1646 (if (null? (slot-ref gf 'methods))
1647 (no-method gf args))
1648 (let ((methods (compute-applicable-methods gf args)))
1649 (if methods
1650 (apply-methods gf (sort-applicable-methods gf methods args) args)
1651 (no-applicable-method gf args))))
1652
1653 ;; compute-applicable-methods is bound to %compute-applicable-methods.
1654 ;; *fixme* use let
1655 (define %%compute-applicable-methods
1656 (make <generic> #:name 'compute-applicable-methods))
1657
1658 (define-method (%%compute-applicable-methods (gf <generic>) args)
1659 (%compute-applicable-methods gf args))
1660
1661 (set! compute-applicable-methods %%compute-applicable-methods)
1662
1663 (define-method (sort-applicable-methods (gf <generic>) methods args)
1664 (let ((targs (map class-of args)))
1665 (sort methods (lambda (m1 m2) (method-more-specific? m1 m2 targs)))))
1666
1667 (define-method (method-more-specific? (m1 <method>) (m2 <method>) targs)
1668 (%method-more-specific? m1 m2 targs))
1669
1670 (define-method (apply-method (gf <generic>) methods build-next args)
1671 (apply (method-procedure (car methods))
1672 (build-next (cdr methods) args)
1673 args))
1674
1675 (define-method (apply-methods (gf <generic>) (l <list>) args)
1676 (letrec ((next (lambda (procs args)
1677 (lambda new-args
1678 (let ((a (if (null? new-args) args new-args)))
1679 (if (null? procs)
1680 (no-next-method gf a)
1681 (apply-method gf procs next a)))))))
1682 (apply-method gf l next args)))
1683
1684 ;; We don't want the following procedure to turn up in backtraces:
1685 (for-each (lambda (proc)
1686 (set-procedure-property! proc 'system-procedure #t))
1687 (list slot-unbound
1688 slot-missing
1689 no-next-method
1690 no-applicable-method
1691 no-method
1692 ))
1693
1694 ;;;
1695 ;;; {<composite-metaclass> and <active-metaclass>}
1696 ;;;
1697
1698 ;(autoload "active-slot" <active-metaclass>)
1699 ;(autoload "composite-slot" <composite-metaclass>)
1700 ;(export <composite-metaclass> <active-metaclass>)
1701
1702 ;;;
1703 ;;; {Tools}
1704 ;;;
1705
1706 ;; list2set
1707 ;;
1708 ;; duplicate the standard list->set function but using eq instead of
1709 ;; eqv which really sucks a lot, uselessly here
1710 ;;
1711 (define (list2set l)
1712 (let loop ((l l)
1713 (res '()))
1714 (cond
1715 ((null? l) res)
1716 ((memq (car l) res) (loop (cdr l) res))
1717 (else (loop (cdr l) (cons (car l) res))))))
1718
1719 (define (class-subclasses c)
1720 (letrec ((allsubs (lambda (c)
1721 (cons c (mapappend allsubs
1722 (class-direct-subclasses c))))))
1723 (list2set (cdr (allsubs c)))))
1724
1725 (define (class-methods c)
1726 (list2set (mapappend class-direct-methods
1727 (cons c (class-subclasses c)))))
1728
1729 ;;;
1730 ;;; {Final initialization}
1731 ;;;
1732
1733 ;; Tell C code that the main bulk of Goops has been loaded
1734 (%goops-loaded)
1735
1736
1737 \f
1738
1739 ;;;
1740 ;;; {SMOB and port classes}
1741 ;;;
1742
1743 (define <arbiter> (find-subclass <top> '<arbiter>))
1744 (define <promise> (find-subclass <top> '<promise>))
1745 (define <thread> (find-subclass <top> '<thread>))
1746 (define <mutex> (find-subclass <top> '<mutex>))
1747 (define <condition-variable> (find-subclass <top> '<condition-variable>))
1748 (define <regexp> (find-subclass <top> '<regexp>))
1749 (define <hook> (find-subclass <top> '<hook>))
1750 (define <bitvector> (find-subclass <top> '<bitvector>))
1751 (define <random-state> (find-subclass <top> '<random-state>))
1752 (define <async> (find-subclass <top> '<async>))
1753 (define <directory> (find-subclass <top> '<directory>))
1754 (define <keyword> (find-subclass <top> '<keyword>))
1755 (define <array> (find-subclass <top> '<array>))
1756 (define <character-set> (find-subclass <top> '<character-set>))
1757 (define <dynamic-object> (find-subclass <top> '<dynamic-object>))
1758 (define <guardian> (find-subclass <applicable> '<guardian>))
1759
1760 (define (define-class-subtree class)
1761 (define! (class-name class) class)
1762 (for-each define-class-subtree (class-direct-subclasses class)))
1763
1764 (define-class-subtree (find-subclass <port> '<file-port>))