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