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