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