3 ;;;; Copyright (C) 2000, 2001 Free Software Foundation, Inc.
5 ;;;; This program is free software; you can redistribute it and/or modify
6 ;;;; it under the terms of the GNU General Public License as published by
7 ;;;; the Free Software Foundation; either version 2, or (at your option)
8 ;;;; any later version.
10 ;;;; This program is distributed in the hope that it will be useful,
11 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;;;; GNU General Public License for more details.
15 ;;;; You should have received a copy of the GNU General Public License
16 ;;;; along with this software; see the file COPYING. If not, write to
17 ;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 ;;;; Boston, MA 02111-1307 USA
20 ;;;; As a special exception, the Free Software Foundation gives permission
21 ;;;; for additional uses of the text contained in its release of GUILE.
23 ;;;; The exception is that, if you link the GUILE library with other files
24 ;;;; to produce an executable, this does not by itself cause the
25 ;;;; resulting executable to be covered by the GNU General Public License.
26 ;;;; Your use of that executable is in no way restricted on account of
27 ;;;; linking the GUILE library code into it.
29 ;;;; This exception does not however invalidate any other reasons why
30 ;;;; the executable file might be covered by the GNU General Public License.
32 ;;;; This exception applies only to the code released by the
33 ;;;; Free Software Foundation under the name GUILE. If you copy
34 ;;;; code from other Free Software Foundation releases into a copy of
35 ;;;; GUILE, as the General Public License permits, the exception does
36 ;;;; not apply to the code that you add in this way. To avoid misleading
37 ;;;; anyone as to the status of such modified files, you must delete
38 ;;;; this exception notice from them.
40 ;;;; If you write modifications of your own for GUILE, it is your choice
41 ;;;; whether to permit this exception to apply to your modifications.
42 ;;;; If you do not wish that, delete this exception notice.
46 (define-module (oop goops save)
47 :use-module (oop goops internal)
48 :use-module (oop goops util)
49 :re-export (make-unbound)
50 :export (save-objects load-objects restore
51 enumerate! enumerate-component!
52 write-readably write-component write-component-procedure
53 literal? readable make-readable))
56 ;;; save-objects ALIST PORT [EXCLUDED] [USES]
58 ;;; ALIST ::= ((NAME . OBJECT) ...)
60 ;;; Save OBJECT ... to PORT so that when the data is read and evaluated
61 ;;; OBJECT ... are re-created under names NAME ... .
62 ;;; Exclude any references to objects in the list EXCLUDED.
63 ;;; Add a (use-modules . USES) line to the top of the saved text.
65 ;;; In some instances, when `save-object' doesn't know how to produce
66 ;;; readable syntax for an object, you can explicitly register read
67 ;;; syntax for an object using the special form `readable'.
71 ;;; The function `foo' produces an object of obscure structure.
72 ;;; Only `foo' can construct such objects. Because of this, an
75 ;;; (define x (vector 1 (foo)))
77 ;;; cannot be saved by `save-objects'. But if you instead write
79 ;;; (define x (vector 1 (readable (foo))))
81 ;;; `save-objects' will happily produce the necessary read syntax.
83 ;;; To add new read syntax, hang methods on `enumerate!' and
86 ;;; enumerate! OBJECT ENV
87 ;;; Should call `enumerate-component!' (which takes same args) on
88 ;;; each component object. Should return #t if the composite object
89 ;;; can be written as a literal. (`enumerate-component!' returns #t
90 ;;; if the component is a literal.
92 ;;; write-readably OBJECT PORT ENV
93 ;;; Should write a readable representation of OBJECT to PORT.
94 ;;; Should use `write-component' to print each component object.
95 ;;; Use `literal?' to decide if a component is a literal.
99 ;;; enumerate-component! OBJECT ENV
101 ;;; write-component OBJECT PATCHER PORT ENV
102 ;;; PATCHER is an expression which, when evaluated, stores OBJECT
103 ;;; into its current location.
107 ;;; (write-component (car ls) `(set-car! ,ls ,(car ls)) file env)
109 ;;; write-component is a macro.
111 ;;; literal? COMPONENT ENV
114 (define-method (immediate? (o <top>)) #f)
116 (define-method (immediate? (o <null>)) #t)
117 (define-method (immediate? (o <number>)) #t)
118 (define-method (immediate? (o <boolean>)) #t)
119 (define-method (immediate? (o <symbol>)) #t)
120 (define-method (immediate? (o <char>)) #t)
121 (define-method (immediate? (o <keyword>)) #t)
123 ;;; enumerate! OBJECT ENVIRONMENT
125 ;;; Return #t if object is a literal.
127 (define-method (enumerate! (o <top>) env) #t)
129 (define-method (write-readably (o <top>) file env)
130 ;;(goops-error "No read-syntax defined for object `~S'" o)
131 (write o file) ;doesn't catch bugs, but is much more flexible
138 (if (or (not (defined? 'readables))
140 (define readables (make-weak-key-hash-table 61)))
143 (procedure->memoizing-macro
145 `(make-readable ,(cadr exp) ',(copy-tree (cadr exp))))))
147 (define (make-readable obj expr)
148 (hashq-set! readables obj expr)
151 (define (readable-expression obj)
152 `(readable ,(hashq-ref readables obj)))
154 (define (readable? obj)
155 (hashq-get-handle readables obj))
161 (define-method (enumerate! (o <string>) env) #f)
167 (define-method (enumerate! (o <vector>) env)
168 (or (not (vector? o))
170 (array-for-each (lambda (o)
171 (if (not (enumerate-component! o env))
176 (define-method (write-readably (o <vector>) file env)
177 (if (not (vector? o))
179 (let ((n (vector-length o)))
182 (let ((not-literal? (not (literal? o env))))
183 (display (if not-literal?
187 (if (and not-literal?
188 (literal? (vector-ref o 0) env))
190 (write-component (vector-ref o 0)
191 `(vector-set! ,o 0 ,(vector-ref o 0))
196 (display #\space file)
197 (if (and not-literal?
198 (literal? (vector-ref o i) env))
200 (write-component (vector-ref o i)
201 `(vector-set! ,o ,i ,(vector-ref o i))
204 (display #\) file))))))
211 (define-method (enumerate! (o <array>) env)
212 (enumerate-component! (shared-array-root o) env))
214 (define (make-mapper array)
215 (let* ((dims (array-dimensions array))
216 (n (array-rank array))
217 (indices (reverse (if (<= n 11)
218 (list-tail '(t s r q p n m l k j i) (- 11 n))
224 (cons (gensym "i") ls))))))))
226 (+ ,(shared-array-offset array)
227 ,@(map (lambda (ind dim inc)
228 `(* ,inc ,(if (pair? dim) `(- ,ind ,(car dim)) ind)))
230 (array-dimensions array)
231 (shared-array-increments array))))))
233 (define (write-array prefix o not-literal? file env)
234 (letrec ((inner (lambda (n indices)
236 (let ((el (apply array-ref o
237 (reverse (cons 0 indices)))))
238 (if (and not-literal?
243 `(array-set! ,o ,el ,@indices)
248 (display #\space file)
249 (let ((el (apply array-ref o
250 (reverse (cons i indices)))))
251 (if (and not-literal?
256 `(array-set! ,o ,el ,@indices)
259 (display prefix file)
260 (let loop ((dims (array-dimensions o))
262 (cond ((null? (cdr dims))
263 (inner (car dims) indices))
265 (let ((n (car dims)))
269 (display #\space file))
270 (display prefix file)
271 (loop (cdr dims) (cons i indices))
272 (display #\) file))))))
275 (define-method (write-readably (o <array>) file env)
276 (let ((root (shared-array-root o)))
277 (cond ((literal? o env)
278 (if (not (vector? root))
282 (display (array-rank o) file)
283 (write-array #\( o #f file env))))
285 (display "(make-shared-array " file)
286 (if (literal? root env)
288 (write-component root
289 (goops-error "write-readably(<array>): internal error")
292 (display #\space file)
293 (display (make-mapper o) file)
294 (for-each (lambda (dim)
295 (display #\space file)
297 (array-dimensions o))
300 (display "(list->uniform-array " file)
301 (display (array-rank o) file)
302 (display " '() " file)
303 (write-array "(list " o file env)))))
309 ;;; These methods have more complex structure than is required for
310 ;;; most objects, since they take over some of the logic of
311 ;;; `write-component'.
314 (define-method (enumerate! (o <pair>) env)
315 (let ((literal? (enumerate-component! (car o) env)))
316 (and (enumerate-component! (cdr o) env)
319 (define-method (write-readably (o <pair>) file env)
320 (let ((proper? (let loop ((ls o))
323 (not (binding? (cdr ls) env))
325 (1? (or (not (pair? (cdr o)))
326 (binding? (cdr o) env)))
327 (not-literal? (not (literal? o env)))
329 (refs (ref-stack env)))
330 (display (cond ((not not-literal?) #\()
335 (if (and not-literal?
336 (literal? (car o) env))
338 (write-component (car o) `(set-car! ,o ,(car o)) file env)
339 (do ((ls (cdr o) (cdr ls))
341 ((or (not (pair? ls))
345 (if (not not-literal?)
347 (display #\space file)
348 (if (and not-literal?
351 (write-component ls `(set-cdr! ,prev ,ls) file env)))
353 (display #\space file)
354 (set! infos (cons (object-info ls env) infos))
355 (push-ref! ls env) ;*fixme* optimize
356 (set! (visiting? (car infos)) #t)
357 (if (and not-literal?
358 (literal? (car ls) env))
360 (write-component (car ls) `(set-car! ,ls ,(car ls)) file env)
362 (for-each (lambda (info)
363 (set! (visiting? info) #f))
365 (set! (ref-stack env) refs)
372 ;;; Doesn't yet handle unbound slots
374 ;; Don't export this function! This is all very temporary.
376 (define (get-set-for-each proc class)
377 (for-each (lambda (slotdef g-n-s)
378 (let ((g-n-s (cddr g-n-s)))
379 (cond ((integer? g-n-s)
380 (proc (standard-get g-n-s) (standard-set g-n-s)))
381 ((not (memq (slot-definition-allocation slotdef)
382 '(#:class #:each-subclass)))
383 (proc (car g-n-s) (cadr g-n-s))))))
385 (slot-ref class 'getters-n-setters)))
387 (define (access-for-each proc class)
388 (for-each (lambda (slotdef g-n-s)
389 (let ((g-n-s (cddr g-n-s))
390 (a (slot-definition-accessor slotdef)))
391 (cond ((integer? g-n-s)
392 (proc (slot-definition-name slotdef)
393 (and a (generic-function-name a))
395 (standard-set g-n-s)))
396 ((not (memq (slot-definition-allocation slotdef)
397 '(#:class #:each-subclass)))
398 (proc (slot-definition-name slotdef)
399 (and a (generic-function-name a))
403 (slot-ref class 'getters-n-setters)))
408 "(restore CLASS (SLOT-NAME1 ...) EXP1 ...)"
409 `(let ((o (,%allocate-instance ,(cadr exp) '())))
410 (for-each (lambda (name val)
411 (,slot-set! o name val))
413 (list ,@(cdddr exp)))
416 (define-method (enumerate! (o <object>) env)
417 (get-set-for-each (lambda (get set)
419 (if (not (unbound? val))
420 (enumerate-component! val env))))
424 (define-method (write-readably (o <object>) file env)
425 (let ((class (class-of o)))
426 (display "(restore " file)
427 (display (class-name class) file)
430 (filter (lambda (slotdef)
431 (not (or (memq (slot-definition-allocation slotdef)
432 '(#:class #:each-subclass))
433 (and (slot-bound? o (slot-definition-name slotdef))
435 (slot-ref o (slot-definition-name slotdef))
437 (class-slots class))))
438 (if (not (null? slotdefs))
440 (display (slot-definition-name (car slotdefs)) file)
441 (for-each (lambda (slotdef)
442 (display #\space file)
443 (display (slot-definition-name slotdef) file))
446 (access-for-each (lambda (name aname get set)
447 (display #\space file)
449 (cond ((unbound? val)
450 (display '(make-unbound) file))
451 ((excluded? val env))
453 (if (literal? val env)
457 `(set! (,aname ,o) ,val)
458 `(slot-set! ,o ',name ,val))
467 ;;; Currently, we don't support reading in class objects
470 (define-method (enumerate! (o <class>) env) #f)
472 (define-method (write-readably (o <class>) file env)
473 (display (class-name o) file))
479 ;;; Currently, we don't support reading in generic functions
482 (define-method (enumerate! (o <generic>) env) #f)
484 (define-method (write-readably (o <generic>) file env)
485 (display (generic-function-name o) file))
491 ;;; Currently, we don't support reading in methods
494 (define-method (enumerate! (o <method>) env) #f)
496 (define-method (write-readably (o <method>) file env)
497 (goops-error "No read-syntax for <method> defined"))
503 (define-class <environment> ()
504 (object-info #:accessor object-info
505 #:init-form (make-hash-table 61))
506 (excluded #:accessor excluded
507 #:init-form (make-hash-table 61))
508 (pass-2? #:accessor pass-2?
510 (ref-stack #:accessor ref-stack
512 (objects #:accessor objects
514 (pre-defines #:accessor pre-defines
516 (locals #:accessor locals
518 (stand-ins #:accessor stand-ins
520 (post-defines #:accessor post-defines
522 (patchers #:accessor patchers
524 (multiple-bound #:accessor multiple-bound
528 (define-method (initialize (env <environment>) initargs)
530 (cond ((get-keyword #:excluded initargs #f)
531 => (lambda (excludees)
532 (for-each (lambda (e)
533 (hashq-create-handle! (excluded env) e #f))
536 (define-method (object-info o env)
537 (hashq-ref (object-info env) o))
539 (define-method ((setter object-info) o env x)
540 (hashq-set! (object-info env) o x))
542 (define (excluded? o env)
543 (hashq-get-handle (excluded env) o))
545 (define (add-patcher! patcher env)
546 (set! (patchers env) (cons patcher (patchers env))))
548 (define (push-ref! o env)
549 (set! (ref-stack env) (cons o (ref-stack env))))
551 (define (pop-ref! env)
552 (set! (ref-stack env) (cdr (ref-stack env))))
554 (define (container env)
555 (car (ref-stack env)))
557 (define-class <object-info> ()
558 (visiting #:accessor visiting
560 (binding #:accessor binding
562 (literal? #:accessor literal?
566 (define visiting? visiting)
568 (define-method (binding (info <boolean>))
571 (define-method (binding o env)
572 (binding (object-info o env)))
574 (define binding? binding)
576 (define-method (literal? (info <boolean>))
579 ;;; Note that this method is intended to be used only during the
582 (define-method (literal? o env)
585 (let ((info (object-info o env)))
586 ;; write-component sets all bindings first to #:defining,
588 (and (or (not (binding? info))
589 ;; we might be using `literal?' in a write-readably method
590 ;; to query about the object being defined
591 (and (eq? (visiting info) #:defining)
592 (null? (cdr (ref-stack env)))))
599 ;;; Enumeration has two passes.
601 ;;; Pass 1: Detect common substructure, circular references and order
603 ;;; Pass 2: Detect literals
605 (define (enumerate-component! o env)
606 (cond ((immediate? o) #t)
608 ((excluded? o env) #t)
610 (let ((info (object-info o env)))
612 ;; if circular reference, we print as a literal
613 ;; (note that during pass-2, circular references are
614 ;; forward references, i.e. *not* yet marked with #:pass-2
615 (not (eq? (visiting? info) #:pass-2))
616 (and (enumerate! o env)
618 (set! (literal? info) #t)
622 (set! (binding info) #t)
624 ;; circular reference--mark container
625 (set! (binding (object-info (container env) env)) #t))))
627 (let ((info (make <object-info>)))
628 (set! (object-info o env) info)
630 (set! (visiting? info) #t)
632 (set! (visiting? info) #f)
634 (set! (objects env) (cons o (objects env)))))))
636 (define (write-component-procedure o file env)
637 "Return #f if circular reference"
638 (cond ((immediate? o) (write o file) #t)
639 ((readable? o) (write (readable-expression o) file) #t)
640 ((excluded? o env) (display #f file) #t)
642 (let ((info (object-info o env)))
643 (cond ((not (binding? info)) (write-readably o file env) #t)
644 ((not (eq? (visiting info) #:defined)) #f) ;forward reference
645 (else (display (binding info) file) #t))))))
647 ;;; write-component OBJECT PATCHER FILE ENV
649 (define write-component
650 (procedure->memoizing-macro
652 `(or (write-component-procedure ,(cadr exp) ,@(cdddr exp))
654 (display #f ,(cadddr exp))
655 (add-patcher! ,(caddr exp) env))))))
661 (define binding-name car)
662 (define binding-object cdr)
664 (define (pass-1! alist env)
665 ;; Determine object order and necessary bindings
666 (for-each (lambda (binding)
667 (enumerate-component! (binding-object binding) env))
670 (define (make-local i)
671 (string->symbol (string-append "%o" (number->string i))))
673 (define (name-bindings! alist env)
674 ;; Name top-level bindings
675 (for-each (lambda (b)
676 (let ((o (binding-object b)))
677 (if (not (or (immediate? o)
680 (let ((info (object-info o env)))
681 (if (symbol? (binding info))
682 ;; already bound to a variable
683 (set! (multiple-bound env)
684 (acons (binding info)
686 (multiple-bound env)))
688 (binding-name b)))))))
690 ;; Name rest of bindings and create stand-in and definition lists
691 (let post-loop ((ls (objects env))
693 (cond ((or (null? ls)
694 (eq? (binding (car ls) env) #t))
695 (set! (post-defines env) post-defs)
696 (set! (objects env) ls))
697 ((not (binding (car ls) env))
698 (post-loop (cdr ls) post-defs))
700 (post-loop (cdr ls) (cons (car ls) post-defs)))))
701 (let pre-loop ((ls (reverse (objects env)))
708 (set! (pre-defines env) (reverse pre-defs))
709 (set! (locals env) (reverse locs))
710 (set! (stand-ins env) (reverse sins)))
711 (let ((info (object-info (car ls) env)))
712 (cond ((not (binding? info))
713 (pre-loop (cdr ls) i pre-defs locs sins))
714 ((boolean? (binding info))
716 (set! (binding info) (make-local i))
725 (cons (car ls) pre-defs)
729 (let ((real-name (binding info)))
730 (set! (binding info) (make-local i))
735 (acons (binding info) real-name sins)))))))))
737 (define (pass-2! env)
738 (set! (pass-2? env) #t)
739 (for-each (lambda (o)
740 (let ((info (object-info o env)))
741 (set! (literal? info) (enumerate! o env))
742 (set! (visiting info) #:pass-2)))
743 (append (pre-defines env)
745 (post-defines env))))
747 (define (write-define! name val literal? file)
748 (display "(define " file)
750 (display #\space file)
751 (if literal? (display #\' file))
753 (display ")\n" file))
755 (define (write-empty-defines! file env)
756 (for-each (lambda (stand-in)
757 (write-define! (cdr stand-in) #f #f file))
759 (for-each (lambda (o)
760 (write-define! (binding o env) #f #f file))
763 (define (write-definition! prefix o file env)
764 (display prefix file)
765 (let ((info (object-info o env)))
766 (display (binding info) file)
767 (display #\space file)
771 (set! (visiting info) #:defining)
772 (write-readably o file env)
773 (set! (visiting info) #:defined)
777 (define (write-let*-head! file env)
778 (display "(let* (" file)
779 (write-definition! "(" (car (locals env)) file env)
780 (for-each (lambda (o)
781 (write-definition! "\n (" o file env))
783 (display ")\n" file))
785 (define (write-rebindings! prefix bindings file env)
786 (for-each (lambda (patch)
787 (display prefix file)
788 (display (cdr patch) file)
789 (display #\space file)
790 (display (car patch) file)
791 (display ")\n" file))
794 (define (write-definitions! selector prefix file env)
795 (for-each (lambda (o)
796 (write-definition! prefix o file env)
800 (define (write-patches! prefix file env)
801 (for-each (lambda (patch)
802 (display prefix file)
803 (display (let name-objects ((patcher patch))
804 (cond ((binding patcher env)
806 (cond ((assq name (stand-ins env))
810 (cons (name-objects (car patcher))
811 (name-objects (cdr patcher))))
815 (reverse (patchers env))))
817 (define (write-immediates! alist file)
818 (for-each (lambda (b)
819 (if (immediate? (binding-object b))
820 (write-define! (binding-name b)
826 (define (write-readables! alist file env)
828 (for-each (lambda (b)
829 (cond ((not (readable? (binding-object b))))
830 ((assq (binding-object b) written)
832 (set! (multiple-bound env)
835 (multiple-bound env)))))
837 (write-define! (binding-name b)
838 (readable-expression (binding-object b))
841 (set! written (acons (binding-object b)
846 (define-method (save-objects (alist <pair>) (file <string>) . rest)
847 (let ((port (open-output-file file)))
848 (apply save-objects alist port rest)
852 (define-method (save-objects (alist <pair>) (file <output-port>) . rest)
853 (let ((excluded (if (>= (length rest) 1) (car rest) '()))
854 (uses (if (>= (length rest) 2) (cadr rest) '())))
855 (let ((env (make <environment> #:excluded excluded)))
857 (name-bindings! alist env)
859 (if (not (null? uses))
861 (write `(use-modules ,@uses) file)
863 (write-immediates! alist file)
864 (if (null? (locals env))
866 (write-definitions! post-defines "(define " file env)
867 (write-patches! "" file env))
869 (write-definitions! pre-defines "(define " file env)
870 (write-empty-defines! file env)
871 (write-let*-head! file env)
872 (write-rebindings! " (set! " (stand-ins env) file env)
873 (write-definitions! post-defines " (set! " file env)
874 (write-patches! " " file env)
875 (display " )\n" file)))
876 (write-readables! alist file env)
877 (write-rebindings! "(define " (reverse (multiple-bound env)) file env))))
879 (define-method (load-objects (file <string>))
880 (let* ((port (open-input-file file))
881 (objects (load-objects port)))
885 (define-method (load-objects (file <input-port>))
886 (let ((m (make-module)))
887 (module-use! m the-scm-module)
888 (module-use! m %module-public-interface)
889 (save-module-excursion
891 (set-current-module m)
892 (let loop ((sexp (read file)))
893 (if (not (eof-object? sexp))
896 (loop (read file)))))))
897 (module-map (lambda (name var)
898 (cons name (variable-ref var)))