compile goops submodules, goops.test now passes again
[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 81;; First initialize the builtin part of GOOPS
7d38f3d8
AW
82(eval-case
83 ((load-toplevel compile-toplevel)
84 (%init-goops-builtins)))
78ec533c
MV
85
86;; Then load the rest of GOOPS
87(use-modules (oop goops util)
88 (oop goops dispatch)
51f7ef47 89 (oop goops compile))
14f1d9fe 90
14f1d9fe
MD
91\f
92(define min-fixnum (- (expt 2 29)))
93
94(define max-fixnum (- (expt 2 29) 1))
95
96;;
97;; goops-error
98;;
99(define (goops-error format-string . args)
100 (save-stack)
101 (scm-error 'goops-error #f format-string args '()))
102
103;;
104;; is-a?
105;;
106(define (is-a? obj class)
107 (and (memq class (class-precedence-list (class-of obj))) #t))
108
109
110;;;
111;;; {Meta classes}
112;;;
113
114(define ensure-metaclass-with-supers
115 (let ((table-of-metas '()))
116 (lambda (meta-supers)
117 (let ((entry (assoc meta-supers table-of-metas)))
118 (if entry
119 ;; Found a previously created metaclass
120 (cdr entry)
121 ;; Create a new meta-class which inherit from "meta-supers"
122 (let ((new (make <class> #:dsupers meta-supers
123 #:slots '()
124 #:name (gensym "metaclass"))))
125 (set! table-of-metas (cons (cons meta-supers new) table-of-metas))
126 new))))))
127
128(define (ensure-metaclass supers env)
129 (if (null? supers)
130 <class>
131 (let* ((all-metas (map (lambda (x) (class-of x)) supers))
132 (all-cpls (apply append
133 (map (lambda (m)
134 (cdr (class-precedence-list m)))
135 all-metas)))
136 (needed-metas '()))
137 ;; Find the most specific metaclasses. The new metaclass will be
138 ;; a subclass of these.
139 (for-each
140 (lambda (meta)
141 (if (and (not (member meta all-cpls))
142 (not (member meta needed-metas)))
143 (set! needed-metas (append needed-metas (list meta)))))
144 all-metas)
145 ;; Now return a subclass of the metaclasses we found.
146 (if (null? (cdr needed-metas))
147 (car needed-metas) ; If there's only one, just use it.
148 (ensure-metaclass-with-supers needed-metas)))))
149
150;;;
151;;; {Classes}
152;;;
153
154;;; (define-class NAME (SUPER ...) SLOT-DEFINITION ... OPTION ...)
155;;;
156;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
157;;; OPTION ::= KEYWORD VALUE
158;;;
d31c5d19
AW
159(define (define-class-pre-definition kw val)
160 (case kw
14f1d9fe 161 ((#:getter #:setter)
d31c5d19
AW
162 `(if (or (not (defined? ',val))
163 (not (is-a? ,val <generic>)))
164 (define-generic ,val)))
14f1d9fe 165 ((#:accessor)
d31c5d19
AW
166 `(if (or (not (defined? ',val))
167 (not (is-a? ,val <accessor>)))
168 (define-accessor ,val)))
14f1d9fe
MD
169 (else #f)))
170
d31c5d19
AW
171(define (kw-do-map mapper f kwargs)
172 (define (keywords l)
173 (cond
174 ((null? l) '())
175 ((or (null? (cdr l)) (not (keyword? (car l))))
176 (goops-error "malformed keyword arguments: ~a" kwargs))
177 (else (cons (car l) (keywords (cddr l))))))
178 (define (args l)
179 (if (null? l) '() (cons (cadr l) (args (cddr l)))))
180 ;; let* to check keywords first
181 (let* ((k (keywords kwargs))
182 (a (args kwargs)))
183 (mapper f k a)))
c31142ee 184
14f1d9fe
MD
185;;; This code should be implemented in C.
186;;;
d31c5d19
AW
187(define-macro (define-class name supers . slots)
188 ;; Some slot options require extra definitions to be made. In
189 ;; particular, we want to make sure that the generic function objects
190 ;; which represent accessors exist before `make-class' tries to add
191 ;; methods to them.
192 ;;
193 ;; Postpone some error handling to class macro.
194 ;;
195 `(begin
196 ;; define accessors
197 ,@(append-map (lambda (slot)
198 (kw-do-map filter-map
199 define-class-pre-definition
200 (if (pair? slot) (cdr slot) '())))
201 (take-while (lambda (x) (not (keyword? x))) slots))
202 (if (and (defined? ',name)
203 (is-a? ,name <class>)
204 (memq <object> (class-precedence-list ,name)))
205 (class-redefinition ,name
206 (class ,supers ,@slots #:name ',name))
207 (define ,name (class ,supers ,@slots #:name ',name)))))
14f1d9fe 208
f12de0a1 209(define standard-define-class define-class)
14f1d9fe
MD
210
211;;; (class (SUPER ...) SLOT-DEFINITION ... OPTION ...)
212;;;
213;;; SLOT-DEFINITION ::= SLOT-NAME | (SLOT-NAME OPTION ...)
214;;; OPTION ::= KEYWORD VALUE
215;;;
d31c5d19
AW
216(define-macro (class supers . slots)
217 (define (make-slot-definition-forms slots)
218 (map
219 (lambda (def)
220 (cond
221 ((pair? def)
222 `(list ',(car def)
223 ,@(kw-do-map append-map
224 (lambda (kw arg)
225 (case kw
226 ((#:init-form)
227 `(#:init-form ',arg
228 #:init-thunk (lambda () ,arg)))
229 (else (list kw arg))))
230 (cdr def))))
231 (else
232 `(list ',def))))
233 slots))
14f1d9fe 234
d31c5d19
AW
235 (if (not (list? supers))
236 (goops-error "malformed superclass list: ~S" supers))
237 (let ((slot-defs (cons #f '()))
238 (slots (take-while (lambda (x) (not (keyword? x))) slots))
239 (options (or (find-tail keyword? slots) '())))
240 `(make-class
241 ;; evaluate super class variables
242 (list ,@supers)
243 ;; evaluate slot definitions, except the slot name!
244 (list ,@(make-slot-definition-forms slots))
245 ;; evaluate class options
246 ,@options)))
14f1d9fe
MD
247
248(define (make-class supers slots . options)
249 (let ((env (or (get-keyword #:environment options #f)
250 (top-level-env))))
251 (let* ((name (get-keyword #:name options (make-unbound)))
252 (supers (if (not (or-map (lambda (class)
253 (memq <object>
254 (class-precedence-list class)))
255 supers))
256 (append supers (list <object>))
257 supers))
258 (metaclass (or (get-keyword #:metaclass options #f)
259 (ensure-metaclass supers env))))
260
261 ;; Verify that all direct slots are different and that we don't inherit
262 ;; several time from the same class
263 (let ((tmp1 (find-duplicate supers))
264 (tmp2 (find-duplicate (map slot-definition-name slots))))
265 (if tmp1
266 (goops-error "make-class: super class ~S is duplicate in class ~S"
267 tmp1 name))
268 (if tmp2
269 (goops-error "make-class: slot ~S is duplicate in class ~S"
270 tmp2 name)))
271
272 ;; Everything seems correct, build the class
273 (apply make metaclass
274 #:dsupers supers
275 #:slots slots
276 #:name name
277 #:environment env
278 options))))
279
280;;;
281;;; {Generic functions and accessors}
282;;;
283
1d83f47e
AW
284;; Apparently the desired semantics are that we extend previous
285;; procedural definitions, but that if `name' was already a generic, we
286;; overwrite its definition.
287(define-macro (define-generic name)
288 (if (not (symbol? name))
289 (goops-error "bad generic function name: ~S" name))
290 `(define ,name
291 (if (and (defined? ',name) (is-a? ,name <generic>))
292 (make <generic> #:name ',name)
293 (ensure-generic (if (defined? ',name) ,name #f) ',name))))
294
295(define-macro (define-extended-generic name val)
296 (if (not (symbol? name))
297 (goops-error "bad generic function name: ~S" name))
298 `(define ,name (make-extended-generic ,val ',name)))
299
300(define-macro (define-extended-generics names . args)
301 (let ((prefixes (get-keyword #:prefix args #f)))
302 (if prefixes
303 `(begin
304 ,@(map (lambda (name)
305 `(define-extended-generic ,name
306 (list ,@(map (lambda (prefix)
307 (symbol-append prefix name))
308 prefixes))))
309 names))
310 (goops-error "no prefixes supplied"))))
bbf8d523 311
14f1d9fe
MD
312(define (make-generic . name)
313 (let ((name (and (pair? name) (car name))))
314 (make <generic> #:name name)))
315
bbf8d523
MD
316(define (make-extended-generic gfs . name)
317 (let* ((name (and (pair? name) (car name)))
318 (gfs (if (pair? gfs) gfs (list gfs)))
319 (gws? (any (lambda (gf) (is-a? gf <generic-with-setter>)) gfs)))
320 (let ((ans (if gws?
321 (let* ((sname (and name (make-setter-name name)))
322 (setters
323 (apply append
324 (map (lambda (gf)
325 (if (is-a? gf <generic-with-setter>)
326 (list (ensure-generic (setter gf)
327 sname))
328 '()))
329 gfs)))
330 (es (make <extended-generic-with-setter>
331 #:name name
332 #:extends gfs
333 #:setter (make <extended-generic>
334 #:name sname
335 #:extends setters))))
336 (extended-by! setters (setter es))
337 es)
338 (make <extended-generic>
339 #:name name
340 #:extends gfs))))
341 (extended-by! gfs ans)
342 ans)))
343
344(define (extended-by! gfs eg)
345 (for-each (lambda (gf)
346 (slot-set! gf 'extended-by
347 (cons eg (slot-ref gf 'extended-by))))
348 gfs))
349
350(define (not-extended-by! gfs eg)
351 (for-each (lambda (gf)
352 (slot-set! gf 'extended-by
353 (delq! eg (slot-ref gf 'extended-by))))
354 gfs))
355
14f1d9fe
MD
356(define (ensure-generic old-definition . name)
357 (let ((name (and (pair? name) (car name))))
358 (cond ((is-a? old-definition <generic>) old-definition)
359 ((procedure-with-setter? old-definition)
360 (make <generic-with-setter>
361 #:name name
362 #:default (procedure old-definition)
363 #:setter (setter old-definition)))
364 ((procedure? old-definition)
365 (make <generic> #:name name #:default old-definition))
366 (else (make <generic> #:name name)))))
367
1d83f47e
AW
368;; same semantics as <generic>
369(define-macro (define-accessor name)
370 (if (not (symbol? name))
371 (goops-error "bad accessor name: ~S" name))
372 `(define ,name
373 (if (and (defined? ',name) (is-a? ,name <accessor>))
374 (make <accessor> #:name ',name)
375 (ensure-accessor (if (defined? ',name) ,name #f) ',name))))
c31142ee 376
14f1d9fe
MD
377(define (make-setter-name name)
378 (string->symbol (string-append "setter:" (symbol->string name))))
379
380(define (make-accessor . name)
381 (let ((name (and (pair? name) (car name))))
f8af5c6d 382 (make <accessor>
14f1d9fe
MD
383 #:name name
384 #:setter (make <generic>
385 #:name (and name (make-setter-name name))))))
386
387(define (ensure-accessor proc . name)
388 (let ((name (and (pair? name) (car name))))
f8af5c6d
MD
389 (cond ((and (is-a? proc <accessor>)
390 (is-a? (setter proc) <generic>))
391 proc)
392 ((is-a? proc <generic-with-setter>)
393 (upgrade-accessor proc (setter proc)))
14f1d9fe 394 ((is-a? proc <generic>)
f8af5c6d 395 (upgrade-accessor proc (make-generic name)))
14f1d9fe 396 ((procedure-with-setter? proc)
f8af5c6d 397 (make <accessor>
14f1d9fe
MD
398 #:name name
399 #:default (procedure proc)
400 #:setter (ensure-generic (setter proc) name)))
401 ((procedure? proc)
402 (ensure-accessor (ensure-generic proc name) name))
403 (else
404 (make-accessor name)))))
405
f8af5c6d 406(define (upgrade-accessor generic setter)
bbf8d523
MD
407 (let ((methods (slot-ref generic 'methods))
408 (gws (make (if (is-a? generic <extended-generic>)
409 <extended-generic-with-setter>
f8af5c6d 410 <accessor>)
14f1d9fe 411 #:name (generic-function-name generic)
bbf8d523 412 #:extended-by (slot-ref generic 'extended-by)
14f1d9fe 413 #:setter setter)))
bbf8d523
MD
414 (if (is-a? generic <extended-generic>)
415 (let ((gfs (slot-ref generic 'extends)))
416 (not-extended-by! gfs generic)
417 (slot-set! gws 'extends gfs)
418 (extended-by! gfs gws)))
14f1d9fe
MD
419 ;; Steal old methods
420 (for-each (lambda (method)
421 (slot-set! method 'generic-function gws))
422 methods)
423 (slot-set! gws 'methods methods)
424 gws))
425
426;;;
427;;; {Methods}
428;;;
429
56f952c6
AW
430(define-macro (define-method head . body)
431 (if (not (pair? head))
432 (goops-error "bad method head: ~S" head))
433 (let ((gf (car head)))
434 (cond ((and (pair? gf)
435 (eq? (car gf) 'setter)
436 (pair? (cdr gf))
437 (symbol? (cadr gf))
438 (null? (cddr gf)))
439 ;; named setter method
440 (let ((name (cadr gf)))
441 (cond ((not (symbol? name))
442 `(add-method! (setter ,name)
443 (method ,(cdr head) ,@body)))
444 (else
445 `(begin
446 (if (or (not (defined? ',name))
447 (not (is-a? ,name <accessor>)))
448 (define-accessor ,name))
449 (add-method! (setter ,name)
450 (method ,(cdr head) ,@body)))))))
451 ((not (symbol? gf))
452 `(add-method! ,gf (method ,(cdr head) ,@body)))
453 (else
454 `(begin
455 ;; FIXME: this code is how it always was, but it's quite
456 ;; cracky: it will only define the generic function if it
457 ;; was undefined before (ok), or *was defined to #f*. The
458 ;; latter is crack. But there are bootstrap issues about
459 ;; fixing this -- change it to (is-a? ,gf <generic>) and
460 ;; see.
461 (if (or (not (defined? ',gf))
462 (not ,gf))
463 (define-generic ,gf))
464 (add-method! ,gf
465 (method ,(cdr head) ,@body)))))))
14f1d9fe
MD
466
467(define (make-method specializers procedure)
468 (make <method>
469 #:specializers specializers
470 #:procedure procedure))
471
02e720ff 472(define-macro (method args . body)
14f1d9fe
MD
473 (letrec ((specializers
474 (lambda (ls)
27b32aad 475 (cond ((null? ls) (list (list 'quote '())))
14f1d9fe
MD
476 ((pair? ls) (cons (if (pair? (car ls))
477 (cadar ls)
478 '<top>)
479 (specializers (cdr ls))))
480 (else '(<top>)))))
481 (formals
482 (lambda (ls)
483 (if (pair? ls)
484 (cons (if (pair? (car ls)) (caar ls) (car ls))
485 (formals (cdr ls)))
486 ls))))
02e720ff
AW
487 `(make <method>
488 #:specializers (cons* ,@(specializers args))
21497600
AW
489 #:formals ',(formals args)
490 #:body ',body
491 #:compile-env (compile-time-environment)
02e720ff
AW
492 #:procedure (lambda ,(formals args)
493 ,@(if (null? body)
7d38f3d8 494 '(begin)
02e720ff 495 body)))))
14f1d9fe
MD
496
497;;;
498;;; {add-method!}
499;;;
500
501(define (add-method-in-classes! m)
502 ;; Add method in all the classes which appears in its specializers list
503 (for-each* (lambda (x)
504 (let ((dm (class-direct-methods x)))
505 (if (not (memv m dm))
506 (slot-set! x 'direct-methods (cons m dm)))))
507 (method-specializers m)))
508
509(define (remove-method-in-classes! m)
510 ;; Remove method in all the classes which appears in its specializers list
511 (for-each* (lambda (x)
512 (slot-set! x
513 'direct-methods
514 (delv! m (class-direct-methods x))))
515 (method-specializers m)))
516
517(define (compute-new-list-of-methods gf new)
518 (let ((new-spec (method-specializers new))
bbf8d523 519 (methods (slot-ref gf 'methods)))
14f1d9fe
MD
520 (let loop ((l methods))
521 (if (null? l)
522 (cons new methods)
523 (if (equal? (method-specializers (car l)) new-spec)
524 (begin
525 ;; This spec. list already exists. Remove old method from dependents
526 (remove-method-in-classes! (car l))
527 (set-car! l new)
528 methods)
529 (loop (cdr l)))))))
530
7d38f3d8
AW
531(define internal-add-method!
532 (method ((gf <generic>) (m <method>))
533 (slot-set! m 'generic-function gf)
534 (slot-set! gf 'methods (compute-new-list-of-methods gf m))
535 (let ((specializers (slot-ref m 'specializers)))
536 (slot-set! gf 'n-specialized
537 (max (length* specializers)
538 (slot-ref gf 'n-specialized))))
539 (%invalidate-method-cache! gf)
540 (add-method-in-classes! m)
541 *unspecified*))
14f1d9fe
MD
542
543(define-generic add-method!)
544
7d38f3d8 545((method-procedure internal-add-method!) add-method! internal-add-method!)
14f1d9fe 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)))
4631414e
AW
1102 ;; note that we allow non-closures; we only check arity on
1103 ;; the closures, though, because we inline their dispatch
1104 ;; in %get-slot-value / %set-slot-value.
1105 (if (or (not (procedure? get))
1106 (and (closure? get)
1107 (not (= (car (procedure-property get 'arity)) 1))))
21ab2aeb
MD
1108 (goops-error "Bad getter closure for slot `~S' in ~S: ~S"
1109 slot class get))
4631414e
AW
1110 (if (or (not (procedure? set))
1111 (and (closure? set)
1112 (not (= (car (procedure-property set 'arity)) 2))))
21ab2aeb
MD
1113 (goops-error "Bad setter closure for slot `~S' in ~S: ~S"
1114 slot class set))))))
14f1d9fe
MD
1115
1116 (map (lambda (s)
21ab2aeb
MD
1117 ;; The strange treatment of nfields is due to backward compatibility.
1118 (let* ((index (slot-ref class 'nfields))
1119 (g-n-s (compute-get-n-set class s))
1120 (size (- (slot-ref class 'nfields) index))
14f1d9fe 1121 (name (slot-definition-name s)))
21ab2aeb
MD
1122 ;; NOTE: The following is interdependent with C macros
1123 ;; defined above goops.c:scm_sys_prep_layout_x.
1124 ;;
1125 ;; For simple instance slots, we have the simplest form
1126 ;; '(name init-function . index)
1127 ;; For other slots we have
1128 ;; '(name init-function getter setter . alloc)
1129 ;; where alloc is:
1130 ;; '(index size) for instance allocated slots
1131 ;; '() for other slots
14f1d9fe
MD
1132 (verify-accessors name g-n-s)
1133 (cons name
266f3a23 1134 (cons (compute-slot-init-function name s)
21ab2aeb
MD
1135 (if (or (integer? g-n-s)
1136 (zero? size))
1137 g-n-s
e1ac894c 1138 (append g-n-s (list index size)))))))
14f1d9fe
MD
1139 slots))
1140
1141;;; compute-cpl
1142;;;
1143;;; Correct behaviour:
1144;;;
1145;;; (define-class food ())
1146;;; (define-class fruit (food))
1147;;; (define-class spice (food))
1148;;; (define-class apple (fruit))
1149;;; (define-class cinnamon (spice))
1150;;; (define-class pie (apple cinnamon))
1151;;; => cpl (pie) = pie apple fruit cinnamon spice food object top
1152;;;
1153;;; (define-class d ())
1154;;; (define-class e ())
1155;;; (define-class f ())
1156;;; (define-class b (d e))
1157;;; (define-class c (e f))
1158;;; (define-class a (b c))
1159;;; => cpl (a) = a b d c e f object top
1160;;;
1161
71d540f7 1162(define-method (compute-cpl (class <class>))
14f1d9fe
MD
1163 (compute-std-cpl class class-direct-supers))
1164
1165;; Support
1166
1167(define (only-non-null lst)
1168 (filter (lambda (l) (not (null? l))) lst))
1169
1170(define (compute-std-cpl c get-direct-supers)
1171 (let ((c-direct-supers (get-direct-supers c)))
1172 (merge-lists (list c)
1173 (only-non-null (append (map class-precedence-list
1174 c-direct-supers)
1175 (list c-direct-supers))))))
1176
1177(define (merge-lists reversed-partial-result inputs)
1178 (cond
1179 ((every null? inputs)
1180 (reverse! reversed-partial-result))
1181 (else
1182 (let* ((candidate (lambda (c)
1183 (and (not (any (lambda (l)
1184 (memq c (cdr l)))
1185 inputs))
1186 c)))
1187 (candidate-car (lambda (l)
1188 (and (not (null? l))
1189 (candidate (car l)))))
1190 (next (any candidate-car inputs)))
1191 (if (not next)
1192 (goops-error "merge-lists: Inconsistent precedence graph"))
1193 (let ((remove-next (lambda (l)
1194 (if (eq? (car l) next)
1195 (cdr l)
1196 l))))
1197 (merge-lists (cons next reversed-partial-result)
1198 (only-non-null (map remove-next inputs))))))))
1199
1200;; Modified from TinyClos:
1201;;
1202;; A simple topological sort.
1203;;
1204;; It's in this file so that both TinyClos and Objects can use it.
1205;;
1206;; This is a fairly modified version of code I originally got from Anurag
1207;; Mendhekar <anurag@moose.cs.indiana.edu>.
1208;;
1209
1210(define (compute-clos-cpl c get-direct-supers)
1211 (top-sort ((build-transitive-closure get-direct-supers) c)
1212 ((build-constraints get-direct-supers) c)
1213 (std-tie-breaker get-direct-supers)))
1214
1215
1216(define (top-sort elements constraints tie-breaker)
1217 (let loop ((elements elements)
1218 (constraints constraints)
1219 (result '()))
1220 (if (null? elements)
1221 result
1222 (let ((can-go-in-now
1223 (filter
1224 (lambda (x)
1225 (every (lambda (constraint)
1226 (or (not (eq? (cadr constraint) x))
1227 (memq (car constraint) result)))
1228 constraints))
1229 elements)))
1230 (if (null? can-go-in-now)
1231 (goops-error "top-sort: Invalid constraints")
1232 (let ((choice (if (null? (cdr can-go-in-now))
1233 (car can-go-in-now)
1234 (tie-breaker result
1235 can-go-in-now))))
1236 (loop
1237 (filter (lambda (x) (not (eq? x choice)))
b0dff018 1238 elements)
14f1d9fe
MD
1239 constraints
1240 (append result (list choice)))))))))
1241
1242(define (std-tie-breaker get-supers)
1243 (lambda (partial-cpl min-elts)
1244 (let loop ((pcpl (reverse partial-cpl)))
1245 (let ((current-elt (car pcpl)))
1246 (let ((ds-of-ce (get-supers current-elt)))
1247 (let ((common (filter (lambda (x)
1248 (memq x ds-of-ce))
1249 min-elts)))
1250 (if (null? common)
1251 (if (null? (cdr pcpl))
1252 (goops-error "std-tie-breaker: Nothing valid")
1253 (loop (cdr pcpl)))
1254 (car common))))))))
1255
1256
1257(define (build-transitive-closure get-follow-ons)
1258 (lambda (x)
1259 (let track ((result '())
1260 (pending (list x)))
1261 (if (null? pending)
1262 result
1263 (let ((next (car pending)))
1264 (if (memq next result)
1265 (track result (cdr pending))
1266 (track (cons next result)
1267 (append (get-follow-ons next)
1268 (cdr pending)))))))))
1269
1270(define (build-constraints get-follow-ons)
1271 (lambda (x)
1272 (let loop ((elements ((build-transitive-closure get-follow-ons) x))
1273 (this-one '())
1274 (result '()))
1275 (if (or (null? this-one) (null? (cdr this-one)))
1276 (if (null? elements)
1277 result
1278 (loop (cdr elements)
1279 (cons (car elements)
1280 (get-follow-ons (car elements)))
1281 result))
1282 (loop elements
1283 (cdr this-one)
1284 (cons (list (car this-one) (cadr this-one))
1285 result))))))
1286
1287;;; compute-get-n-set
1288;;;
71d540f7 1289(define-method (compute-get-n-set (class <class>) s)
14f1d9fe
MD
1290 (case (slot-definition-allocation s)
1291 ((#:instance) ;; Instance slot
1292 ;; get-n-set is just its offset
1293 (let ((already-allocated (slot-ref class 'nfields)))
1294 (slot-set! class 'nfields (+ already-allocated 1))
1295 already-allocated))
1296
1297 ((#:class) ;; Class slot
1298 ;; Class-slots accessors are implemented as 2 closures around
1299 ;; a Scheme variable. As instance slots, class slots must be
1300 ;; unbound at init time.
1301 (let ((name (slot-definition-name s)))
1302 (if (memq name (map slot-definition-name (class-direct-slots class)))
1303 ;; This slot is direct; create a new shared variable
1304 (make-closure-variable class)
1305 ;; Slot is inherited. Find its definition in superclass
1306 (let loop ((l (cdr (class-precedence-list class))))
1307 (let ((r (assoc name (slot-ref (car l) 'getters-n-setters))))
1308 (if r
1309 (cddr r)
1310 (loop (cdr l))))))))
1311
1312 ((#:each-subclass) ;; slot shared by instances of direct subclass.
1313 ;; (Thomas Buerger, April 1998)
1314 (make-closure-variable class))
1315
1316 ((#:virtual) ;; No allocation
1317 ;; slot-ref and slot-set! function must be given by the user
1318 (let ((get (get-keyword #:slot-ref (slot-definition-options s) #f))
1319 (set (get-keyword #:slot-set! (slot-definition-options s) #f))
1320 (env (class-environment class)))
1321 (if (not (and get set))
3a43b605 1322 (goops-error "You must supply a #:slot-ref and a #:slot-set! in ~S"
14f1d9fe
MD
1323 s))
1324 (list get set)))
1325 (else (next-method))))
1326
1327(define (make-closure-variable class)
1328 (let ((shared-variable (make-unbound)))
1329 (list (lambda (o) shared-variable)
1330 (lambda (o v) (set! shared-variable v)))))
1331
71d540f7 1332(define-method (compute-get-n-set (o <object>) s)
14f1d9fe
MD
1333 (goops-error "Allocation \"~S\" is unknown" (slot-definition-allocation s)))
1334
71d540f7 1335(define-method (compute-slots (class <class>))
14f1d9fe
MD
1336 (%compute-slots class))
1337
1338;;;
1339;;; {Initialize}
1340;;;
1341
71d540f7 1342(define-method (initialize (object <object>) initargs)
14f1d9fe
MD
1343 (%initialize-object object initargs))
1344
71d540f7 1345(define-method (initialize (class <class>) initargs)
14f1d9fe
MD
1346 (next-method)
1347 (let ((dslots (get-keyword #:slots initargs '()))
1348 (supers (get-keyword #:dsupers initargs '()))
1349 (env (get-keyword #:environment initargs (top-level-env))))
1350
1351 (slot-set! class 'name (get-keyword #:name initargs '???))
1352 (slot-set! class 'direct-supers supers)
1353 (slot-set! class 'direct-slots dslots)
1354 (slot-set! class 'direct-subclasses '())
1355 (slot-set! class 'direct-methods '())
1356 (slot-set! class 'cpl (compute-cpl class))
1357 (slot-set! class 'redefined #f)
1358 (slot-set! class 'environment env)
1359 (let ((slots (compute-slots class)))
1360 (slot-set! class 'slots slots)
1361 (slot-set! class 'nfields 0)
1362 (slot-set! class 'getters-n-setters (compute-getters-n-setters class
1363 slots
1364 env))
1365 ;; Build getters - setters - accessors
1366 (compute-slot-accessors class slots env))
1367
1368 ;; Update the "direct-subclasses" of each inherited classes
1369 (for-each (lambda (x)
1370 (slot-set! x
1371 'direct-subclasses
1372 (cons class (slot-ref x 'direct-subclasses))))
1373 supers)
1374
1375 ;; Support for the underlying structs:
1376
1377 ;; Inherit class flags (invisible on scheme level) from supers
1378 (%inherit-magic! class supers)
1379
1380 ;; Set the layout slot
1381 (%prep-layout! class)))
1382
14f1d9fe
MD
1383(define (initialize-object-procedure object initargs)
1384 (let ((proc (get-keyword #:procedure initargs #f)))
1385 (cond ((not proc))
1386 ((pair? proc)
1387 (apply set-object-procedure! object proc))
a524a03f 1388 ((valid-object-procedure? proc)
14f1d9fe
MD
1389 (set-object-procedure! object proc))
1390 (else
1391 (set-object-procedure! object
1392 (lambda args (apply proc args)))))))
1393
71d540f7 1394(define-method (initialize (class <operator-class>) initargs)
14f1d9fe
MD
1395 (next-method)
1396 (initialize-object-procedure class initargs))
1397
71d540f7 1398(define-method (initialize (owsc <operator-with-setter-class>) initargs)
14f1d9fe
MD
1399 (next-method)
1400 (%set-object-setter! owsc (get-keyword #:setter initargs #f)))
1401
71d540f7 1402(define-method (initialize (entity <entity>) initargs)
14f1d9fe
MD
1403 (next-method)
1404 (initialize-object-procedure entity initargs))
1405
71d540f7 1406(define-method (initialize (ews <entity-with-setter>) initargs)
14f1d9fe
MD
1407 (next-method)
1408 (%set-object-setter! ews (get-keyword #:setter initargs #f)))
1409
71d540f7 1410(define-method (initialize (generic <generic>) initargs)
14f1d9fe
MD
1411 (let ((previous-definition (get-keyword #:default initargs #f))
1412 (name (get-keyword #:name initargs #f)))
1413 (next-method)
1414 (slot-set! generic 'methods (if (is-a? previous-definition <procedure>)
7d38f3d8
AW
1415 (list (method args
1416 (apply previous-definition args)))
14f1d9fe
MD
1417 '()))
1418 (if name
1419 (set-procedure-property! generic 'name name))
1420 ))
1421
bbf8d523
MD
1422(define-method (initialize (eg <extended-generic>) initargs)
1423 (next-method)
1424 (slot-set! eg 'extends (get-keyword #:extends initargs '())))
1425
b432fb4b
MD
1426(define dummy-procedure (lambda args *unspecified*))
1427
71d540f7 1428(define-method (initialize (method <method>) initargs)
14f1d9fe
MD
1429 (next-method)
1430 (slot-set! method 'generic-function (get-keyword #:generic-function initargs #f))
1431 (slot-set! method 'specializers (get-keyword #:specializers initargs '()))
b432fb4b
MD
1432 (slot-set! method 'procedure
1433 (get-keyword #:procedure initargs dummy-procedure))
21497600
AW
1434 (slot-set! method 'code-table '())
1435 (slot-set! method 'formals (get-keyword #:formals initargs '()))
1436 (slot-set! method 'body (get-keyword #:body initargs '()))
1437 (slot-set! method 'compile-env (get-keyword #:compile-env initargs #f)))
1438
14f1d9fe 1439
71d540f7 1440(define-method (initialize (obj <foreign-object>) initargs))
14f1d9fe
MD
1441
1442;;;
1443;;; {Change-class}
1444;;;
1445
1446(define (change-object-class old-instance old-class new-class)
cc6c7fee 1447 (let ((new-instance (allocate-instance new-class '())))
8378b269 1448 ;; Initialize the slots of the new instance
14f1d9fe
MD
1449 (for-each (lambda (slot)
1450 (if (and (slot-exists-using-class? old-class old-instance slot)
1451 (eq? (slot-definition-allocation
1452 (class-slot-definition old-class slot))
1453 #:instance)
1454 (slot-bound-using-class? old-class old-instance slot))
1455 ;; Slot was present and allocated in old instance; copy it
1456 (slot-set-using-class!
1457 new-class
1458 new-instance
1459 slot
1460 (slot-ref-using-class old-class old-instance slot))
1461 ;; slot was absent; initialize it with its default value
1462 (let ((init (slot-init-function new-class slot)))
1463 (if init
1464 (slot-set-using-class!
1465 new-class
1466 new-instance
1467 slot
1468 (apply init '()))))))
1469 (map slot-definition-name (class-slots new-class)))
1470 ;; Exchange old and new instance in place to keep pointers valid
1471 (%modify-instance old-instance new-instance)
1472 ;; Allow class specific updates of instances (which now are swapped)
1473 (update-instance-for-different-class new-instance old-instance)
1474 old-instance))
1475
1476
71d540f7 1477(define-method (update-instance-for-different-class (old-instance <object>)
14f1d9fe
MD
1478 (new-instance
1479 <object>))
1480 ;;not really important what we do, we just need a default method
1481 new-instance)
1482
71d540f7 1483(define-method (change-class (old-instance <object>) (new-class <class>))
14f1d9fe
MD
1484 (change-object-class old-instance (class-of old-instance) new-class))
1485
1486;;;
1487;;; {make}
1488;;;
1489;;; A new definition which overwrites the previous one which was built-in
1490;;;
1491
71d540f7 1492(define-method (allocate-instance (class <class>) initargs)
14f1d9fe
MD
1493 (%allocate-instance class initargs))
1494
71d540f7 1495(define-method (make-instance (class <class>) . initargs)
14f1d9fe
MD
1496 (let ((instance (allocate-instance class initargs)))
1497 (initialize instance initargs)
1498 instance))
1499
1500(define make make-instance)
1501
1502;;;
1503;;; {apply-generic}
1504;;;
1505;;; Protocol for calling standard generic functions. This protocol is
1506;;; not used for real <generic> functions (in this case we use a
1507;;; completely C hard-coded protocol). Apply-generic is used by
1508;;; goops for calls to subclasses of <generic> and <generic-with-setter>.
1509;;; The code below is similar to the first MOP described in AMOP. In
1510;;; particular, it doesn't used the currified approach to gf
1511;;; call. There are 2 reasons for that:
1512;;; - the protocol below is exposed to mimic completely the one written in C
1513;;; - the currified protocol would be imho inefficient in C.
1514;;;
1515
71d540f7 1516(define-method (apply-generic (gf <generic>) args)
14f1d9fe
MD
1517 (if (null? (slot-ref gf 'methods))
1518 (no-method gf args))
1519 (let ((methods (compute-applicable-methods gf args)))
1520 (if methods
1521 (apply-methods gf (sort-applicable-methods gf methods args) args)
1522 (no-applicable-method gf args))))
1523
1524;; compute-applicable-methods is bound to %compute-applicable-methods.
1525;; *fixme* use let
1526(define %%compute-applicable-methods
1527 (make <generic> #:name 'compute-applicable-methods))
1528
71d540f7 1529(define-method (%%compute-applicable-methods (gf <generic>) args)
14f1d9fe
MD
1530 (%compute-applicable-methods gf args))
1531
1532(set! compute-applicable-methods %%compute-applicable-methods)
1533
71d540f7 1534(define-method (sort-applicable-methods (gf <generic>) methods args)
14f1d9fe
MD
1535 (let ((targs (map class-of args)))
1536 (sort methods (lambda (m1 m2) (method-more-specific? m1 m2 targs)))))
1537
71d540f7 1538(define-method (method-more-specific? (m1 <method>) (m2 <method>) targs)
14f1d9fe
MD
1539 (%method-more-specific? m1 m2 targs))
1540
71d540f7 1541(define-method (apply-method (gf <generic>) methods build-next args)
14f1d9fe
MD
1542 (apply (method-procedure (car methods))
1543 (build-next (cdr methods) args)
1544 args))
1545
71d540f7 1546(define-method (apply-methods (gf <generic>) (l <list>) args)
14f1d9fe
MD
1547 (letrec ((next (lambda (procs args)
1548 (lambda new-args
1549 (let ((a (if (null? new-args) args new-args)))
1550 (if (null? procs)
1551 (no-next-method gf a)
1552 (apply-method gf procs next a)))))))
1553 (apply-method gf l next args)))
1554
1555;; We don't want the following procedure to turn up in backtraces:
1556(for-each (lambda (proc)
1557 (set-procedure-property! proc 'system-procedure #t))
1558 (list slot-unbound
1559 slot-missing
1560 no-next-method
1561 no-applicable-method
1562 no-method
1563 ))
1564
1565;;;
1566;;; {<composite-metaclass> and <active-metaclass>}
1567;;;
1568
1569;(autoload "active-slot" <active-metaclass>)
1570;(autoload "composite-slot" <composite-metaclass>)
1571;(export <composite-metaclass> <active-metaclass>)
1572
1573;;;
1574;;; {Tools}
1575;;;
1576
1577;; list2set
1578;;
1579;; duplicate the standard list->set function but using eq instead of
1580;; eqv which really sucks a lot, uselessly here
1581;;
1582(define (list2set l)
1583 (let loop ((l l)
1584 (res '()))
1585 (cond
1586 ((null? l) res)
1587 ((memq (car l) res) (loop (cdr l) res))
1588 (else (loop (cdr l) (cons (car l) res))))))
1589
1590(define (class-subclasses c)
1591 (letrec ((allsubs (lambda (c)
1592 (cons c (mapappend allsubs
1593 (class-direct-subclasses c))))))
1594 (list2set (cdr (allsubs c)))))
1595
1596(define (class-methods c)
1597 (list2set (mapappend class-direct-methods
1598 (cons c (class-subclasses c)))))
1599
1600;;;
1601;;; {Final initialization}
1602;;;
1603
1604;; Tell C code that the main bulk of Goops has been loaded
1605(%goops-loaded)