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