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