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