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