compile goops
[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)))
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>)
7d38f3d8
AW
1410 (list (method args
1411 (apply previous-definition args)))
14f1d9fe
MD
1412 '()))
1413 (if name
1414 (set-procedure-property! generic 'name name))
1415 ))
1416
bbf8d523
MD
1417(define-method (initialize (eg <extended-generic>) initargs)
1418 (next-method)
1419 (slot-set! eg 'extends (get-keyword #:extends initargs '())))
1420
b432fb4b
MD
1421(define dummy-procedure (lambda args *unspecified*))
1422
71d540f7 1423(define-method (initialize (method <method>) initargs)
14f1d9fe
MD
1424 (next-method)
1425 (slot-set! method 'generic-function (get-keyword #:generic-function initargs #f))
1426 (slot-set! method 'specializers (get-keyword #:specializers initargs '()))
b432fb4b
MD
1427 (slot-set! method 'procedure
1428 (get-keyword #:procedure initargs dummy-procedure))
21497600
AW
1429 (slot-set! method 'code-table '())
1430 (slot-set! method 'formals (get-keyword #:formals initargs '()))
1431 (slot-set! method 'body (get-keyword #:body initargs '()))
1432 (slot-set! method 'compile-env (get-keyword #:compile-env initargs #f)))
1433
14f1d9fe 1434
71d540f7 1435(define-method (initialize (obj <foreign-object>) initargs))
14f1d9fe
MD
1436
1437;;;
1438;;; {Change-class}
1439;;;
1440
1441(define (change-object-class old-instance old-class new-class)
cc6c7fee 1442 (let ((new-instance (allocate-instance new-class '())))
8378b269 1443 ;; Initialize the slots of the new instance
14f1d9fe
MD
1444 (for-each (lambda (slot)
1445 (if (and (slot-exists-using-class? old-class old-instance slot)
1446 (eq? (slot-definition-allocation
1447 (class-slot-definition old-class slot))
1448 #:instance)
1449 (slot-bound-using-class? old-class old-instance slot))
1450 ;; Slot was present and allocated in old instance; copy it
1451 (slot-set-using-class!
1452 new-class
1453 new-instance
1454 slot
1455 (slot-ref-using-class old-class old-instance slot))
1456 ;; slot was absent; initialize it with its default value
1457 (let ((init (slot-init-function new-class slot)))
1458 (if init
1459 (slot-set-using-class!
1460 new-class
1461 new-instance
1462 slot
1463 (apply init '()))))))
1464 (map slot-definition-name (class-slots new-class)))
1465 ;; Exchange old and new instance in place to keep pointers valid
1466 (%modify-instance old-instance new-instance)
1467 ;; Allow class specific updates of instances (which now are swapped)
1468 (update-instance-for-different-class new-instance old-instance)
1469 old-instance))
1470
1471
71d540f7 1472(define-method (update-instance-for-different-class (old-instance <object>)
14f1d9fe
MD
1473 (new-instance
1474 <object>))
1475 ;;not really important what we do, we just need a default method
1476 new-instance)
1477
71d540f7 1478(define-method (change-class (old-instance <object>) (new-class <class>))
14f1d9fe
MD
1479 (change-object-class old-instance (class-of old-instance) new-class))
1480
1481;;;
1482;;; {make}
1483;;;
1484;;; A new definition which overwrites the previous one which was built-in
1485;;;
1486
71d540f7 1487(define-method (allocate-instance (class <class>) initargs)
14f1d9fe
MD
1488 (%allocate-instance class initargs))
1489
71d540f7 1490(define-method (make-instance (class <class>) . initargs)
14f1d9fe
MD
1491 (let ((instance (allocate-instance class initargs)))
1492 (initialize instance initargs)
1493 instance))
1494
1495(define make make-instance)
1496
1497;;;
1498;;; {apply-generic}
1499;;;
1500;;; Protocol for calling standard generic functions. This protocol is
1501;;; not used for real <generic> functions (in this case we use a
1502;;; completely C hard-coded protocol). Apply-generic is used by
1503;;; goops for calls to subclasses of <generic> and <generic-with-setter>.
1504;;; The code below is similar to the first MOP described in AMOP. In
1505;;; particular, it doesn't used the currified approach to gf
1506;;; call. There are 2 reasons for that:
1507;;; - the protocol below is exposed to mimic completely the one written in C
1508;;; - the currified protocol would be imho inefficient in C.
1509;;;
1510
71d540f7 1511(define-method (apply-generic (gf <generic>) args)
14f1d9fe
MD
1512 (if (null? (slot-ref gf 'methods))
1513 (no-method gf args))
1514 (let ((methods (compute-applicable-methods gf args)))
1515 (if methods
1516 (apply-methods gf (sort-applicable-methods gf methods args) args)
1517 (no-applicable-method gf args))))
1518
1519;; compute-applicable-methods is bound to %compute-applicable-methods.
1520;; *fixme* use let
1521(define %%compute-applicable-methods
1522 (make <generic> #:name 'compute-applicable-methods))
1523
71d540f7 1524(define-method (%%compute-applicable-methods (gf <generic>) args)
14f1d9fe
MD
1525 (%compute-applicable-methods gf args))
1526
1527(set! compute-applicable-methods %%compute-applicable-methods)
1528
71d540f7 1529(define-method (sort-applicable-methods (gf <generic>) methods args)
14f1d9fe
MD
1530 (let ((targs (map class-of args)))
1531 (sort methods (lambda (m1 m2) (method-more-specific? m1 m2 targs)))))
1532
71d540f7 1533(define-method (method-more-specific? (m1 <method>) (m2 <method>) targs)
14f1d9fe
MD
1534 (%method-more-specific? m1 m2 targs))
1535
71d540f7 1536(define-method (apply-method (gf <generic>) methods build-next args)
14f1d9fe
MD
1537 (apply (method-procedure (car methods))
1538 (build-next (cdr methods) args)
1539 args))
1540
71d540f7 1541(define-method (apply-methods (gf <generic>) (l <list>) args)
14f1d9fe
MD
1542 (letrec ((next (lambda (procs args)
1543 (lambda new-args
1544 (let ((a (if (null? new-args) args new-args)))
1545 (if (null? procs)
1546 (no-next-method gf a)
1547 (apply-method gf procs next a)))))))
1548 (apply-method gf l next args)))
1549
1550;; We don't want the following procedure to turn up in backtraces:
1551(for-each (lambda (proc)
1552 (set-procedure-property! proc 'system-procedure #t))
1553 (list slot-unbound
1554 slot-missing
1555 no-next-method
1556 no-applicable-method
1557 no-method
1558 ))
1559
1560;;;
1561;;; {<composite-metaclass> and <active-metaclass>}
1562;;;
1563
1564;(autoload "active-slot" <active-metaclass>)
1565;(autoload "composite-slot" <composite-metaclass>)
1566;(export <composite-metaclass> <active-metaclass>)
1567
1568;;;
1569;;; {Tools}
1570;;;
1571
1572;; list2set
1573;;
1574;; duplicate the standard list->set function but using eq instead of
1575;; eqv which really sucks a lot, uselessly here
1576;;
1577(define (list2set l)
1578 (let loop ((l l)
1579 (res '()))
1580 (cond
1581 ((null? l) res)
1582 ((memq (car l) res) (loop (cdr l) res))
1583 (else (loop (cdr l) (cons (car l) res))))))
1584
1585(define (class-subclasses c)
1586 (letrec ((allsubs (lambda (c)
1587 (cons c (mapappend allsubs
1588 (class-direct-subclasses c))))))
1589 (list2set (cdr (allsubs c)))))
1590
1591(define (class-methods c)
1592 (list2set (mapappend class-direct-methods
1593 (cons c (class-subclasses c)))))
1594
1595;;;
1596;;; {Final initialization}
1597;;;
1598
1599;; Tell C code that the main bulk of Goops has been loaded
1600(%goops-loaded)