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