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