3 ;;;; Copyright (C) 2000,2001,2002, 2006, 2009, 2010, 2013 Free Software Foundation, Inc.
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 3 of the License, or (at your option) any later version.
10 ;;;; This library 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 GNU
13 ;;;; Lesser General Public License for more details.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 (define-module (oop goops save)
22 :use-module (oop goops internal)
23 :use-module (oop goops util)
24 :re-export (make-unbound)
25 :export (save-objects load-objects restore
26 enumerate! enumerate-component!
27 write-readably write-component write-component-procedure
28 literal? readable make-readable))
31 ;;; save-objects ALIST PORT [EXCLUDED] [USES]
33 ;;; ALIST ::= ((NAME . OBJECT) ...)
35 ;;; Save OBJECT ... to PORT so that when the data is read and evaluated
36 ;;; OBJECT ... are re-created under names NAME ... .
37 ;;; Exclude any references to objects in the list EXCLUDED.
38 ;;; Add a (use-modules . USES) line to the top of the saved text.
40 ;;; In some instances, when `save-object' doesn't know how to produce
41 ;;; readable syntax for an object, you can explicitly register read
42 ;;; syntax for an object using the special form `readable'.
46 ;;; The function `foo' produces an object of obscure structure.
47 ;;; Only `foo' can construct such objects. Because of this, an
50 ;;; (define x (vector 1 (foo)))
52 ;;; cannot be saved by `save-objects'. But if you instead write
54 ;;; (define x (vector 1 (readable (foo))))
56 ;;; `save-objects' will happily produce the necessary read syntax.
58 ;;; To add new read syntax, hang methods on `enumerate!' and
61 ;;; enumerate! OBJECT ENV
62 ;;; Should call `enumerate-component!' (which takes same args) on
63 ;;; each component object. Should return #t if the composite object
64 ;;; can be written as a literal. (`enumerate-component!' returns #t
65 ;;; if the component is a literal.
67 ;;; write-readably OBJECT PORT ENV
68 ;;; Should write a readable representation of OBJECT to PORT.
69 ;;; Should use `write-component' to print each component object.
70 ;;; Use `literal?' to decide if a component is a literal.
74 ;;; enumerate-component! OBJECT ENV
76 ;;; write-component OBJECT PATCHER PORT ENV
77 ;;; PATCHER is an expression which, when evaluated, stores OBJECT
78 ;;; into its current location.
82 ;;; (write-component (car ls) `(set-car! ,ls ,(car ls)) file env)
84 ;;; write-component is a macro.
86 ;;; literal? COMPONENT ENV
89 (define-method (immediate? (o <top>)) #f)
91 (define-method (immediate? (o <null>)) #t)
92 (define-method (immediate? (o <number>)) #t)
93 (define-method (immediate? (o <boolean>)) #t)
94 (define-method (immediate? (o <symbol>)) #t)
95 (define-method (immediate? (o <char>)) #t)
96 (define-method (immediate? (o <keyword>)) #t)
98 ;;; enumerate! OBJECT ENVIRONMENT
100 ;;; Return #t if object is a literal.
102 (define-method (enumerate! (o <top>) env) #t)
104 (define-method (write-readably (o <top>) file env)
105 ;;(goops-error "No read-syntax defined for object `~S'" o)
106 (write o file) ;doesn't catch bugs, but is much more flexible
113 (define readables (make-weak-key-hash-table 61))
115 (define-macro (readable exp)
116 `(make-readable ,exp ',(copy-tree exp)))
118 (define (make-readable obj expr)
119 (hashq-set! readables obj expr)
122 (define (readable-expression obj)
123 `(readable ,(hashq-ref readables obj)))
125 ;; FIXME: if obj is nil or false, this can return a false value. OTOH
126 ;; usually this is only for non-immediates.
127 (define (readable? obj)
128 (hashq-ref readables obj))
134 (define-method (enumerate! (o <string>) env) #f)
140 (define-method (enumerate! (o <vector>) env)
141 (or (not (vector? o))
143 (array-for-each (lambda (o)
144 (if (not (enumerate-component! o env))
149 (define-method (write-readably (o <vector>) file env)
150 (if (not (vector? o))
152 (let ((n (vector-length o)))
155 (let ((not-literal? (not (literal? o env))))
156 (display (if not-literal?
160 (if (and not-literal?
161 (literal? (vector-ref o 0) env))
163 (write-component (vector-ref o 0)
164 `(vector-set! ,o 0 ,(vector-ref o 0))
169 (display #\space file)
170 (if (and not-literal?
171 (literal? (vector-ref o i) env))
173 (write-component (vector-ref o i)
174 `(vector-set! ,o ,i ,(vector-ref o i))
177 (display #\) file))))))
184 (define-method (enumerate! (o <array>) env)
185 (enumerate-component! (shared-array-root o) env))
187 (define (make-mapper array)
188 (let* ((n (array-rank array))
189 (indices (reverse (if (<= n 11)
190 (list-tail '(t s r q p n m l k j i) (- 11 n))
196 (cons (gensym "i") ls))))))))
198 (+ ,(shared-array-offset array)
199 ,@(map (lambda (ind dim inc)
200 `(* ,inc ,(if (pair? dim) `(- ,ind ,(car dim)) ind)))
202 (array-dimensions array)
203 (shared-array-increments array))))))
205 (define (write-array prefix o not-literal? file env)
206 (letrec ((inner (lambda (n indices)
208 (let ((el (apply array-ref o
209 (reverse (cons 0 indices)))))
210 (if (and not-literal?
215 `(array-set! ,o ,el ,@indices)
220 (display #\space file)
221 (let ((el (apply array-ref o
222 (reverse (cons i indices)))))
223 (if (and not-literal?
228 `(array-set! ,o ,el ,@indices)
231 (display prefix file)
232 (let loop ((dims (array-dimensions o))
234 (cond ((null? (cdr dims))
235 (inner (car dims) indices))
237 (let ((n (car dims)))
241 (display #\space file))
242 (display prefix file)
243 (loop (cdr dims) (cons i indices))
244 (display #\) file))))))
247 (define-method (write-readably (o <array>) file env)
248 (let ((root (shared-array-root o)))
249 (cond ((literal? o env)
250 (if (not (vector? root))
254 (display (array-rank o) file)
255 (write-array #\( o #f file env))))
257 (display "(make-shared-array " file)
258 (if (literal? root env)
260 (write-component root
261 (goops-error "write-readably(<array>): internal error")
264 (display #\space file)
265 (display (make-mapper o) file)
266 (for-each (lambda (dim)
267 (display #\space file)
269 (array-dimensions o))
272 (display "(list->uniform-array " file)
273 (display (array-rank o) file)
274 (display " '() " file)
275 (write-array "(list " o #f file env)))))
281 ;;; These methods have more complex structure than is required for
282 ;;; most objects, since they take over some of the logic of
283 ;;; `write-component'.
286 (define-method (enumerate! (o <pair>) env)
287 (let ((literal? (enumerate-component! (car o) env)))
288 (and (enumerate-component! (cdr o) env)
291 (define-method (write-readably (o <pair>) file env)
292 (let ((proper? (let loop ((ls o))
295 (not (binding? (cdr ls) env))
297 (1? (or (not (pair? (cdr o)))
298 (binding? (cdr o) env)))
299 (not-literal? (not (literal? o env)))
301 (refs (ref-stack env)))
302 (display (cond ((not not-literal?) #\()
307 (if (and not-literal?
308 (literal? (car o) env))
310 (write-component (car o) `(set-car! ,o ,(car o)) file env)
311 (do ((ls (cdr o) (cdr ls))
313 ((or (not (pair? ls))
317 (if (not not-literal?)
319 (display #\space file)
320 (if (and not-literal?
323 (write-component ls `(set-cdr! ,prev ,ls) file env)))
325 (display #\space file)
326 (set! infos (cons (object-info ls env) infos))
327 (push-ref! ls env) ;*fixme* optimize
328 (set! (visiting? (car infos)) #t)
329 (if (and not-literal?
330 (literal? (car ls) env))
332 (write-component (car ls) `(set-car! ,ls ,(car ls)) file env)
334 (for-each (lambda (info)
335 (set! (visiting? info) #f))
337 (set! (ref-stack env) refs)
344 ;;; Doesn't yet handle unbound slots
346 ;; Don't export this function! This is all very temporary.
348 (define (get-set-for-each proc class)
349 (for-each (lambda (slotdef g-n-s)
350 (let ((g-n-s (cddr g-n-s)))
351 (cond ((integer? g-n-s)
352 (proc (standard-get g-n-s) (standard-set g-n-s)))
353 ((not (memq (slot-definition-allocation slotdef)
354 '(#:class #:each-subclass)))
355 (proc (car g-n-s) (cadr g-n-s))))))
357 (slot-ref class 'getters-n-setters)))
359 (define (access-for-each proc class)
360 (for-each (lambda (slotdef g-n-s)
361 (let ((g-n-s (cddr g-n-s))
362 (a (slot-definition-accessor slotdef)))
363 (cond ((integer? g-n-s)
364 (proc (slot-definition-name slotdef)
365 (and a (generic-function-name a))
367 (standard-set g-n-s)))
368 ((not (memq (slot-definition-allocation slotdef)
369 '(#:class #:each-subclass)))
370 (proc (slot-definition-name slotdef)
371 (and a (generic-function-name a))
375 (slot-ref class 'getters-n-setters)))
377 (define-macro (restore class slots . exps)
378 "(restore CLASS (SLOT-NAME1 ...) EXP1 ...)"
379 `(let ((o ((@@ (oop goops) %allocate-instance) ,class '())))
380 (for-each (lambda (name val)
381 (slot-set! o name val))
386 (define-method (enumerate! (o <object>) env)
387 (get-set-for-each (lambda (get set)
389 (if (not (unbound? val))
390 (enumerate-component! val env))))
394 (define-method (write-readably (o <object>) file env)
395 (let ((class (class-of o)))
396 (display "(restore " file)
397 (display (class-name class) file)
400 (filter (lambda (slotdef)
401 (not (or (memq (slot-definition-allocation slotdef)
402 '(#:class #:each-subclass))
403 (and (slot-bound? o (slot-definition-name slotdef))
405 (slot-ref o (slot-definition-name slotdef))
407 (class-slots class))))
408 (if (not (null? slotdefs))
410 (display (slot-definition-name (car slotdefs)) file)
411 (for-each (lambda (slotdef)
412 (display #\space file)
413 (display (slot-definition-name slotdef) file))
416 (access-for-each (lambda (name aname get set)
417 (display #\space file)
419 (cond ((unbound? val)
420 (display '(make-unbound) file))
421 ((excluded? val env))
423 (if (literal? val env)
427 `(set! (,aname ,o) ,val)
428 `(slot-set! ,o ',name ,val))
437 ;;; Currently, we don't support reading in class objects
440 (define-method (enumerate! (o <class>) env) #f)
442 (define-method (write-readably (o <class>) file env)
443 (display (class-name o) file))
449 ;;; Currently, we don't support reading in generic functions
452 (define-method (enumerate! (o <generic>) env) #f)
454 (define-method (write-readably (o <generic>) file env)
455 (display (generic-function-name o) file))
461 ;;; Currently, we don't support reading in methods
464 (define-method (enumerate! (o <method>) env) #f)
466 (define-method (write-readably (o <method>) file env)
467 (goops-error "No read-syntax for <method> defined"))
473 (define-class <environment> ()
474 (object-info #:accessor object-info
475 #:init-form (make-hash-table 61))
476 (excluded #:accessor excluded
477 #:init-form (make-hash-table 61))
478 (pass-2? #:accessor pass-2?
480 (ref-stack #:accessor ref-stack
482 (objects #:accessor objects
484 (pre-defines #:accessor pre-defines
486 (locals #:accessor locals
488 (stand-ins #:accessor stand-ins
490 (post-defines #:accessor post-defines
492 (patchers #:accessor patchers
494 (multiple-bound #:accessor multiple-bound
498 (define-method (initialize (env <environment>) initargs)
500 (cond ((get-keyword #:excluded initargs #f)
501 => (lambda (excludees)
502 (for-each (lambda (e)
503 (hashq-create-handle! (excluded env) e #f))
506 (define-method (object-info o env)
507 (hashq-ref (object-info env) o))
509 (define-method ((setter object-info) o env x)
510 (hashq-set! (object-info env) o x))
512 (define (excluded? o env)
513 (hashq-get-handle (excluded env) o))
515 (define (add-patcher! patcher env)
516 (set! (patchers env) (cons patcher (patchers env))))
518 (define (push-ref! o env)
519 (set! (ref-stack env) (cons o (ref-stack env))))
521 (define (pop-ref! env)
522 (set! (ref-stack env) (cdr (ref-stack env))))
524 (define (container env)
525 (car (ref-stack env)))
527 (define-class <object-info> ()
528 (visiting #:accessor visiting
530 (binding #:accessor binding
532 (literal? #:accessor literal?
536 (define visiting? visiting)
538 (define-method (binding (info <boolean>))
541 (define-method (binding o env)
542 (binding (object-info o env)))
544 (define binding? binding)
546 (define-method (literal? (info <boolean>))
549 ;;; Note that this method is intended to be used only during the
552 (define-method (literal? o env)
555 (let ((info (object-info o env)))
556 ;; write-component sets all bindings first to #:defining,
558 (and (or (not (binding? info))
559 ;; we might be using `literal?' in a write-readably method
560 ;; to query about the object being defined
561 (and (eq? (visiting info) #:defining)
562 (null? (cdr (ref-stack env)))))
569 ;;; Enumeration has two passes.
571 ;;; Pass 1: Detect common substructure, circular references and order
573 ;;; Pass 2: Detect literals
575 (define (enumerate-component! o env)
576 (cond ((immediate? o) #t)
578 ((excluded? o env) #t)
580 (let ((info (object-info o env)))
582 ;; if circular reference, we print as a literal
583 ;; (note that during pass-2, circular references are
584 ;; forward references, i.e. *not* yet marked with #:pass-2
585 (not (eq? (visiting? info) #:pass-2))
586 (and (enumerate! o env)
588 (set! (literal? info) #t)
592 (set! (binding info) #t)
594 ;; circular reference--mark container
595 (set! (binding (object-info (container env) env)) #t))))
597 (let ((info (make <object-info>)))
598 (set! (object-info o env) info)
600 (set! (visiting? info) #t)
602 (set! (visiting? info) #f)
604 (set! (objects env) (cons o (objects env)))))))
606 (define (write-component-procedure o file env)
607 "Return #f if circular reference"
608 (cond ((immediate? o) (write o file) #t)
609 ((readable? o) (write (readable-expression o) file) #t)
610 ((excluded? o env) (display #f file) #t)
612 (let ((info (object-info o env)))
613 (cond ((not (binding? info)) (write-readably o file env) #t)
614 ((not (eq? (visiting info) #:defined)) #f) ;forward reference
615 (else (display (binding info) file) #t))))))
617 ;;; write-component OBJECT PATCHER FILE ENV
619 (define-macro (write-component object patcher file env)
620 `(or (write-component-procedure ,object ,file ,env)
623 (add-patcher! ,patcher ,env))))
629 (define binding-name car)
630 (define binding-object cdr)
632 (define (pass-1! alist env)
633 ;; Determine object order and necessary bindings
634 (for-each (lambda (binding)
635 (enumerate-component! (binding-object binding) env))
638 (define (make-local i)
639 (string->symbol (string-append "%o" (number->string i))))
641 (define (name-bindings! alist env)
642 ;; Name top-level bindings
643 (for-each (lambda (b)
644 (let ((o (binding-object b)))
645 (if (not (or (immediate? o)
648 (let ((info (object-info o env)))
649 (if (symbol? (binding info))
650 ;; already bound to a variable
651 (set! (multiple-bound env)
652 (acons (binding info)
654 (multiple-bound env)))
656 (binding-name b)))))))
658 ;; Name rest of bindings and create stand-in and definition lists
659 (let post-loop ((ls (objects env))
661 (cond ((or (null? ls)
662 (eq? (binding (car ls) env) #t))
663 (set! (post-defines env) post-defs)
664 (set! (objects env) ls))
665 ((not (binding (car ls) env))
666 (post-loop (cdr ls) post-defs))
668 (post-loop (cdr ls) (cons (car ls) post-defs)))))
669 (let pre-loop ((ls (reverse (objects env)))
676 (set! (pre-defines env) (reverse pre-defs))
677 (set! (locals env) (reverse locs))
678 (set! (stand-ins env) (reverse sins)))
679 (let ((info (object-info (car ls) env)))
680 (cond ((not (binding? info))
681 (pre-loop (cdr ls) i pre-defs locs sins))
682 ((boolean? (binding info))
684 (set! (binding info) (make-local i))
693 (cons (car ls) pre-defs)
697 (let ((real-name (binding info)))
698 (set! (binding info) (make-local i))
703 (acons (binding info) real-name sins)))))))))
705 (define (pass-2! env)
706 (set! (pass-2? env) #t)
707 (for-each (lambda (o)
708 (let ((info (object-info o env)))
709 (set! (literal? info) (enumerate! o env))
710 (set! (visiting info) #:pass-2)))
711 (append (pre-defines env)
713 (post-defines env))))
715 (define (write-define! name val literal? file)
716 (display "(define " file)
718 (display #\space file)
719 (if literal? (display #\' file))
721 (display ")\n" file))
723 (define (write-empty-defines! file env)
724 (for-each (lambda (stand-in)
725 (write-define! (cdr stand-in) #f #f file))
727 (for-each (lambda (o)
728 (write-define! (binding o env) #f #f file))
731 (define (write-definition! prefix o file env)
732 (display prefix file)
733 (let ((info (object-info o env)))
734 (display (binding info) file)
735 (display #\space file)
739 (set! (visiting info) #:defining)
740 (write-readably o file env)
741 (set! (visiting info) #:defined)
745 (define (write-let*-head! file env)
746 (display "(let* (" file)
747 (write-definition! "(" (car (locals env)) file env)
748 (for-each (lambda (o)
749 (write-definition! "\n (" o file env))
751 (display ")\n" file))
753 (define (write-rebindings! prefix bindings file env)
754 (for-each (lambda (patch)
755 (display prefix file)
756 (display (cdr patch) file)
757 (display #\space file)
758 (display (car patch) file)
759 (display ")\n" file))
762 (define (write-definitions! selector prefix file env)
763 (for-each (lambda (o)
764 (write-definition! prefix o file env)
768 (define (write-patches! prefix file env)
769 (for-each (lambda (patch)
770 (display prefix file)
771 (display (let name-objects ((patcher patch))
772 (cond ((binding patcher env)
774 (cond ((assq name (stand-ins env))
778 (cons (name-objects (car patcher))
779 (name-objects (cdr patcher))))
783 (reverse (patchers env))))
785 (define (write-immediates! alist file)
786 (for-each (lambda (b)
787 (if (immediate? (binding-object b))
788 (write-define! (binding-name b)
794 (define (write-readables! alist file env)
796 (for-each (lambda (b)
797 (cond ((not (readable? (binding-object b))))
798 ((assq (binding-object b) written)
800 (set! (multiple-bound env)
803 (multiple-bound env)))))
805 (write-define! (binding-name b)
806 (readable-expression (binding-object b))
809 (set! written (acons (binding-object b)
814 (define-method (save-objects (alist <pair>) (file <string>) . rest)
815 (let ((port (open-output-file file)))
816 (apply save-objects alist port rest)
820 (define-method (save-objects (alist <pair>) (file <output-port>) . rest)
821 (let ((excluded (if (>= (length rest) 1) (car rest) '()))
822 (uses (if (>= (length rest) 2) (cadr rest) '())))
823 (let ((env (make <environment> #:excluded excluded)))
825 (name-bindings! alist env)
827 (if (not (null? uses))
829 (write `(use-modules ,@uses) file)
831 (write-immediates! alist file)
832 (if (null? (locals env))
834 (write-definitions! post-defines "(define " file env)
835 (write-patches! "" file env))
837 (write-definitions! pre-defines "(define " file env)
838 (write-empty-defines! file env)
839 (write-let*-head! file env)
840 (write-rebindings! " (set! " (stand-ins env) file env)
841 (write-definitions! post-defines " (set! " file env)
842 (write-patches! " " file env)
843 (display " )\n" file)))
844 (write-readables! alist file env)
845 (write-rebindings! "(define " (reverse (multiple-bound env)) file env))))
847 (define-method (load-objects (file <string>))
848 (let* ((port (open-input-file file))
849 (objects (load-objects port)))
853 (define iface (module-public-interface (current-module)))
855 (define-method (load-objects (file <input-port>))
856 (let ((m (make-module)))
857 (module-use! m the-scm-module)
858 (module-use! m iface)
859 (save-module-excursion
861 (set-current-module m)
862 (let loop ((sexp (read file)))
863 (if (not (eof-object? sexp))
866 (loop (read file)))))))
867 (module-map (lambda (name var)
868 (cons name (variable-ref var)))