scsh-0.6/scheme/vm/image.scm

329 lines
10 KiB
Scheme

; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.
; Write-image and read-image
(define (s48-image-writing-okay?)
(not (have-static-areas?)))
(define *status* (enum errors no-errors))
(define-syntax write-check
(syntax-rules ()
((write-check exp)
(if (eq? *status* (enum errors no-errors))
(set! *status* exp)))))
(define (write-heap-integer n port)
(write-check (write-integer n port))
(write-check (write-char #\newline port)))
(define (s48-write-image resume-proc port)
(begin-writing-image)
(let ((resume-proc (s48-trace-value resume-proc))
(exported-bindings (s48-trace-value (s48-exported-bindings))))
(s48-do-gc)
(s48-mark-traced-channels-closed!)
(let ((symbols (s48-copy-symbol-table))
(imported-bindings (s48-cleaned-imported-bindings))
(resumer-records (find-resumer-records)))
(if (vm-eq? false resumer-records)
(begin
(abort-collection)
(enum errors out-of-memory))
(let ((status (really-write-image port resume-proc
symbols
imported-bindings
exported-bindings
resumer-records)))
(abort-collection)
status)))))
(define (really-write-image port restart-proc
symbols imported-bindings exported-bindings
resumer-records)
(set! *status* (enum errors no-errors))
(write-check (write-char #\newline port))
(write-check (write-page port))
(write-check (write-char #\newline port))
(write-check (write-string architecture-version port))
(write-check (write-char #\newline port))
(write-heap-integer bytes-per-cell port)
(write-heap-integer (a-units->cells (address->integer (heap-start))) port)
(write-heap-integer (a-units->cells (address->integer (heap-pointer))) port)
(write-heap-integer symbols port)
(write-heap-integer imported-bindings port)
(write-heap-integer exported-bindings port)
(write-heap-integer resumer-records port)
(write-heap-integer restart-proc port)
(write-check (write-page port))
(store! (heap-pointer) 1) ; used to detect endianess of image
(write-check (write-block port
(heap-pointer)
(address-difference (address1+ (heap-pointer)) (heap-pointer))))
(write-check (write-block port
(heap-start)
(address-difference (heap-pointer) (heap-start))))
*status*)
; Make sure the image file is okay and return the size of the heap it
; contains.
(define *eof?* #f)
(define-syntax read-check
(syntax-rules ()
((read-check exp)
(read-check exp -1))
((read-check exp losing-value)
(let ((lose losing-value))
(if *eof?*
lose
(receive (thing eof? status)
exp
(cond (eof?
(set! *eof?* #t)
lose)
((eq? *status* (enum errors no-errors))
thing)
(else
(set! *eof?* #t)
(set! *status* status)
lose))))))))
; Read in the ASCII portion of the image and make sure that it is compatible
; with this version of the VM.
(define (s48-check-image-header filename)
(receive (port status)
(open-input-file filename)
(cond ((error? status)
(error-message "Can't open heap image file")
(error-message (error-string status))
-1)
(else
(set! *status* (enum errors no-errors))
(set! *eof?* #f)
(read-check (read-page port))
(read-check (read-newline port)) ; version starts on next line
(let* ((same-version? (read-check (check-image-version port) #f))
(old-bytes-per-cell (read-check (read-integer port))))
(set! *old-begin*
(integer->address
(cells->a-units (read-check (read-integer port)))))
(set! *old-hp*
(integer->address
(cells->a-units (read-check (read-integer port)))))
(set! *symbols* (read-check (read-integer port)))
(set! *imported-bindings* (read-check (read-integer port)))
(set! *exported-bindings* (read-check (read-integer port)))
(set! *resumer-records* (read-check (read-integer port)))
(set! *startup-procedure* (read-check (read-integer port)))
(set! *image-port* port)
(read-check (read-page port)) ; read to beginning of heap
(cond ((error? *status*)
(read-lost "Error reading from image file" port))
(*eof?* ; has to come after *status* check
(set! *status* (enum errors parse-error))
(read-lost "Premature EOF when reading image file" port))
((not same-version?)
(read-lost "Format of image is incompatible with this version of system" port))
((not (= old-bytes-per-cell bytes-per-cell))
(read-lost "Incompatible bytes-per-cell in image" port))
(else
(address-difference *old-hp* *old-begin*))))))))
(define (read-page port)
(read-this-character page-character port))
(define (read-newline port)
(read-this-character #\newline port))
(define (read-this-character char port)
(let loop ()
(receive (ch eof? status)
(read-char port)
(cond ((or eof? (error? status))
(values -1 eof? status))
((char=? char ch)
(values -1 #f status))
(else
(loop))))))
(define (check-image-version port)
(let ((len (string-length architecture-version)))
(let loop ((i 0))
(receive (ch eof? status)
(read-char port)
(cond ((or eof? (error? status))
(values #f eof? status))
((= i len)
(values (char=? #\newline ch) #f status))
((char=? ch (string-ref architecture-version i))
(loop (+ i 1)))
(else
(values #f #f status)))))))
(define *image-port*)
(define *old-begin*)
(define *old-hp*)
;----------------
; Values provided by the image file.
(define *startup-procedure*)
(define *symbols*)
(define *imported-bindings*)
(define *exported-bindings*)
(define *resumer-records*)
(define *initializing?* #t)
; Save initial values across any GC's.
(define (s48-initializing-gc-root)
(if *initializing?*
(begin
(set! *startup-procedure* (s48-trace-value *startup-procedure*))
(set! *symbols* (s48-trace-value *symbols*))
(set! *imported-bindings* (s48-trace-value *imported-bindings*))
(set! *exported-bindings* (s48-trace-value *exported-bindings*))
(set! *resumer-records* (s48-trace-value *resumer-records*)))))
; For the outside world.
(define (s48-startup-procedure) *startup-procedure*)
(define (s48-initial-symbols) *symbols*)
(define (s48-initial-imported-bindings) *imported-bindings*)
(define (s48-initial-exported-bindings) *exported-bindings*)
(define (s48-resumer-records) *resumer-records*)
(define (s48-initialization-complete!)
(set! *initializing?* #f))
; For resuming static images.
(define (s48-set-image-values! startup-proc symbols imports exports records)
(set! *startup-procedure* startup-proc)
(set! *symbols* symbols)
(set! *imported-bindings* imports)
(set! *exported-bindings* exports)
(set! *resumer-records* records))
;----------------
(define (s48-read-image)
(let ((port *image-port*))
(receive (okay? string)
(image-read-block port (cells->a-units 1))
(cond ((not okay?)
(read-lost string port))
((= (fetch (heap-pointer)) 1)
(really-read-image port #f))
(else
(reverse-descriptor-byte-order! (heap-pointer))
(if (= (fetch (heap-pointer)) 1)
(really-read-image port #t)
(read-lost "Unable to correct byte order" port)))))))
(define (really-read-image port reverse?)
(let* ((delta (address-difference (heap-pointer) *old-begin*))
(new-hp (address+ *old-hp* delta))
(new-limit (heap-limit))
(start (heap-pointer)))
(if (address>= new-hp new-limit)
(read-lost "Heap not big enough to restore this image" port)
(receive (okay? string)
(image-read-block port (address-difference *old-hp* *old-begin*))
(receive (ch eof? status)
(read-char port)
(cond ((not okay?)
(read-lost string port))
((error? status)
(read-lost "Error reading from image file" port))
((not eof?)
(read-lost "Image file has extraneous data after image" port))
((error? (close-input-port port))
(read-lost "Error closing image file" port))
(else
(if reverse?
(reverse-byte-order! start new-hp))
(if (not (= delta 0))
(relocate-image delta start new-hp))
(set-heap-pointer! new-hp)
(set! *startup-procedure* (adjust *startup-procedure* delta))
(set! *symbols* (adjust *symbols* delta))
(set! *imported-bindings* (adjust *imported-bindings* delta))
(set! *exported-bindings* (adjust *exported-bindings* delta))
(set! *resumer-records* (adjust *resumer-records* delta))
0)))))))
(define (image-read-block port need)
(receive (got eof? status)
(read-block port (heap-pointer) need)
(cond ((error? status)
(set! *status* status)
(values #f "Error reading from image file"))
(eof?
(values #f "Premature EOF when reading image file"))
((< got need)
(values #f "Read returned too few bytes"))
(else
(values #t "")))))
(define (read-lost message port)
(error-message message)
(if (error? *status*)
(error-message (error-string *status*)))
(if (error? (close-input-port port))
(error-message "Error closing image file"))
-1)
; ABCD => DCBA
; memory intensive, but independent of Scheme's integer size
(define (reverse-descriptor-byte-order! addr)
(let ((x (fetch-byte addr)))
(store-byte! addr (fetch-byte (address+ addr 3)))
(store-byte! (address+ addr 3) x))
(let ((x (fetch-byte (address+ addr 1))))
(store-byte! (address+ addr 1) (fetch-byte (address+ addr 2)))
(store-byte! (address+ addr 2) x)))
(define (reverse-byte-order! start end)
(error-message "Correcting byte order of resumed image.")
(let loop ((ptr start))
(if (address< ptr end)
(begin
(reverse-descriptor-byte-order! ptr)
(loop (let ((value (fetch ptr))
(next (address1+ ptr)))
(if (b-vector-header? value)
(address+ next (header-length-in-a-units value))
next)))))))
(define (adjust descriptor delta)
(if (stob? descriptor)
(address->stob-descriptor
(address+ (address-after-header descriptor) delta))
descriptor))
(define (relocate-image delta start end)
(let loop ((ptr start))
(if (address< ptr end)
(let ((d (adjust (fetch ptr) delta)))
(store! ptr d)
(loop (if (b-vector-header? d)
(address+ (address1+ ptr) (header-length-in-a-units d))
(address1+ ptr)))))))
; The page character is used to mark the ends of the user and prelude sections
; of image files.
(define page-character (ascii->char 12))
(define (write-page port)
(write-char page-character port))