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