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