cs & raco decompile: expose more fasl content

Show the machine code that constructs lifted constants for a linket.

Also, add a `--partial-fasl` option that shows fasl content in a rawer
form, which is useful for checking how content is presented and that
nothing is getting lost in other reconstructed views.
This commit is contained in:
Matthew Flatt 2020-11-21 07:02:56 -07:00
parent a6e683cc71
commit b2a27ef05c
11 changed files with 352 additions and 75 deletions

View File

@ -30,6 +30,8 @@
(set! to-linklets? #t)] (set! to-linklets? #t)]
[("--no-disassemble") "Show machine code as-is" [("--no-disassemble") "Show machine code as-is"
(current-can-disassemble #f)] (current-can-disassemble #f)]
[("--partial-fasl") "Show more detail on fasl structure"
(current-partial-fasl #t)]
#:args source-or-bytecode-file #:args source-or-bytecode-file
source-or-bytecode-file)) source-or-bytecode-file))

View File

@ -253,11 +253,24 @@
[code [code
(case fmt (case fmt
[(compile) [(compile)
(define proc ((vm-eval `(load-compiled-from-port (open-bytevector-input-port ,code) ',sfd-paths)))) (cond
(let ([proc (decompile-chez-procedure (if (null? args) proc (apply proc args)))]) [(not (current-partial-fasl))
(if (null? args) ;; Note that applying the result of `vm-eval` no longer shows the setup of
proc ;; Racket level constants (like keywords):
(cons proc (map (vm-primitive 'force-unfasl) args))))] (define make-proc (vm-eval `(load-compiled-from-port (open-bytevector-input-port ,code) ',sfd-paths)))
(define proc (make-proc))
(let ([proc (decompile-chez-procedure (if (null? args) proc (apply proc args)) make-proc)])
(if (null? args)
proc
(cons proc (map (vm-primitive 'force-unfasl) args))))]
[else
(define desc (disassemble-in-description
`(#(FASL
#:length ,(bytes-length code)
,(vm-eval `(($primitive $describe-fasl-from-port) (open-bytevector-input-port ,code) ',sfd-paths))))))
(if (null? args)
desc
(cons desc (map (vm-primitive 'force-unfasl) args)))])]
[(interpret) [(interpret)
(define bytecode (vm-eval `(fasl-read (open-bytevector-input-port ,code) 'load ',sfd-paths))) (define bytecode (vm-eval `(fasl-read (open-bytevector-input-port ,code) 'load ',sfd-paths)))
(list `(#%interpret ,(unwrap-chez-interpret-jitified bytecode)))] (list `(#%interpret ,(unwrap-chez-interpret-jitified bytecode)))]

View File

@ -5,26 +5,28 @@
(provide decompile-chez-procedure (provide decompile-chez-procedure
unwrap-chez-interpret-jitified unwrap-chez-interpret-jitified
current-can-disassemble) current-can-disassemble
current-partial-fasl
disassemble-in-description)
(define current-can-disassemble (make-parameter #t)) (define current-can-disassemble (make-parameter #t))
(define current-partial-fasl (make-parameter #f))
(define (decompile-chez-procedure p) (define (decompile-chez-procedure p make-p)
(unless (procedure? p) (unless (procedure? p)
(error 'decompile-chez-procedure "not a procedure")) (error 'decompile-chez-procedure "not a procedure"))
(define seen (make-hasheq)) (define seen (make-hasheq))
((vm-primitive 'call-with-system-wind) ((vm-primitive 'call-with-system-wind)
(lambda () (lambda ()
(define make-proc ((vm-primitive 'inspect/object) make-p))
(define make-code (make-proc 'code))
(define proc ((vm-primitive 'inspect/object) p)) (define proc ((vm-primitive 'inspect/object) p))
(define code (proc 'code)) (define code (proc 'code))
(append (append
(apply (decompile-code make-code #f seen #:name "body-maker-that-creates-lifted-constants")
append (decompile-code code proc seen #:unwrap-body? #t)))))
(for/list ([i (in-range (code 'free-count))])
(decompile (proc 'ref i) seen)))
(decompile-code code seen #:unwrap-body? #t)))))
(define (decompile obj seen) (define (decompile obj closure seen)
(define type (obj 'type)) (define type (obj 'type))
(cond (cond
[(eq? type 'variable) [(eq? type 'variable)
@ -34,22 +36,30 @@
[else [else
(hash-set! seen (obj 'value) #t) (hash-set! seen (obj 'value) #t)
(case type (case type
[(code) (decompile-code obj seen)] [(code) (decompile-code obj closure seen)]
[(variable) [(variable)
(decompile (obj 'ref) seen)] (decompile (obj 'ref) #f seen)]
[(procedure) [(procedure)
(decompile (obj 'code) seen)] (decompile (obj 'code) obj seen)]
[else null])])) [else null])]))
(define (decompile-value v seen) (define (decompile-value v seen)
(decompile ((vm-primitive 'inspect/object) v) seen)) (decompile ((vm-primitive 'inspect/object) v) #f seen))
(define (decompile-code code seen (define (decompile-code code closure seen
#:unwrap-body? [unwrap-body? #f]) #:unwrap-body? [unwrap-body? #f]
(define name (code 'name)) #:name [name #f])
(define $generation (vm-eval '($primitive $generation))) (define $generation (vm-eval '($primitive $generation)))
(define $code? (vm-eval '($primitive $code?))) (define $code? (vm-eval '($primitive $code?)))
(define max-gen (vm-eval '(collect-maximum-generation))) (define max-gen (vm-eval '(collect-maximum-generation)))
(define captures (if (and closure (positive? (code 'free-count)))
`('(captures: ,@(for/list ([i (in-range (code 'free-count))])
(define v (closure 'ref i))
(let loop ([v v])
(case (v 'type)
[(variable) (loop (v 'ref))]
[else (v 'value)])))))
'()))
(append (append
(apply (apply
append append
@ -58,9 +68,11 @@
(($generation v) . > . max-gen))) (($generation v) . > . max-gen)))
(decompile-value v seen))) (decompile-value v seen)))
(if unwrap-body? (if unwrap-body?
(decompile-code-body code) (append
captures
(decompile-code-body code))
(list (list
`(define ,(let ([name (code 'name)]) `(define ,(let ([name (or name (code 'name))])
(if name (if name
(string->symbol (string->symbol
(if (and ((string-length name) . > . 0) (if (and ((string-length name) . > . 0)
@ -69,6 +81,7 @@
name)) name))
'....)) '....))
(lambda ,(arity-mask->args (code 'arity-mask)) (lambda ,(arity-mask->args (code 'arity-mask))
,@captures
,@(decompile-code-body code))))))) ,@(decompile-code-body code)))))))
(define (decompile-code-body code-obj) (define (decompile-code-body code-obj)
@ -112,12 +125,7 @@
(cond (cond
[(and (current-can-disassemble) [(and (current-can-disassemble)
(force disassemble-bytes)) (force disassemble-bytes))
=> (lambda (disassemble-bytes) (disassemble-bytes-to-assembly bstr #:relocations ((code-obj 'reloc+offset) 'value))]
(define o (open-output-bytes))
(parameterize ([current-output-port o])
(disassemble-bytes bstr #:relocations ((code-obj 'reloc+offset) 'value)))
(define strs (regexp-split #rx"\n" (get-output-string o)))
(list (cons '#%assembly-code strs)))]
[else [else
(list (list '#%machine-code bstr))]))) (list (list '#%machine-code bstr))])))
@ -126,6 +134,13 @@
(with-handlers ([exn:fail? (lambda (exn) #f)]) (with-handlers ([exn:fail? (lambda (exn) #f)])
(dynamic-require 'disassemble 'disassemble-bytes)))) (dynamic-require 'disassemble 'disassemble-bytes))))
(define (disassemble-bytes-to-assembly bstr #:relocations [relocations '()])
(define o (open-output-bytes))
(parameterize ([current-output-port o])
((force disassemble-bytes) bstr #:relocations relocations))
(define strs (regexp-split #rx"\n" (get-output-string o)))
(list (cons '#%assembly-code strs)))
(define (arity-mask->args mask) (define (arity-mask->args mask)
(cond (cond
[(zero? (bitwise-and mask (sub1 mask))) [(zero? (bitwise-and mask (sub1 mask)))
@ -136,6 +151,33 @@
;; multiple bits set ;; multiple bits set
'args])) 'args]))
;; ----------------------------------------
;; Look for 'CODE descriptions that have bytestrings and convert to disassembled.
;; This function mutates the description.
(define (disassemble-in-description v)
(when (and (current-can-disassemble)
(force disassemble-bytes))
(define ht (make-hasheq))
(let loop ([v v])
(cond
[(hash-ref ht v #f)
(void)]
[else
(hash-set! ht v #t)
(cond
[(pair? v)
(loop (car v))
(loop (cdr v))]
[(vector? v)
(when (and ((vector-length v) . >= . 8)
(eq? 'CODE (vector-ref v 0))
(bytes? (vector-ref v 7)))
(vector-set! v 7 (disassemble-bytes-to-assembly (vector-ref v 7))))
(for ([e (in-vector v)])
(loop e))])])))
v)
;; ---------------------------------------- ;; ----------------------------------------
;; The schemify interpreter's "bytecode" is fairly readable as-is, so ;; The schemify interpreter's "bytecode" is fairly readable as-is, so
;; just unpack compiled procedures at the leaves ;; just unpack compiled procedures at the leaves

View File

@ -13,4 +13,4 @@
(define pkg-authors '(mflatt)) (define pkg-authors '(mflatt))
(define version "1.8") (define version "1.9")

View File

@ -30,9 +30,37 @@ The @exec{raco decompile} command accepts the following command-line flags:
of decoding linklets to approximate Racket @racket[module] forms} of decoding linklets to approximate Racket @racket[module] forms}
@item{@DFlag{no-disassemble} --- show machine code as-is in a byte string, @item{@DFlag{no-disassemble} --- show machine code as-is in a byte string,
instead of attempting to disassemble} instead of attempting to disassemble}
@item{@DFlag{partial-fasl} --- preserve more of the original structure of
the bytecode file, instead of focusing on procedure bodies}
] ]
To the degree that it can be converted back to Racket code, @history[#:changed "1.8" @elem{Added @DFlag{no-disassemble}.}
#:changed "1.9" @elem{Added @DFlag{partial-fasl}.}]
@section{Racket CS Decompilation}
Decompilation of Racket CS bytecode mostly shows the structure of a
module around machine-code implementations of procedures.
@itemize[
@item{A @racketidfont{#%machine-code} form corresponds to machine code
that is not disassembled, where the machine code is in a byte string.}
@item{A @racketidfont{#%assembly-code} form corresponds to disassembled
machine code, where the assembly code is shown as a sequence of strings.}
@item{A @racketidfont{#%interpret} form corresponds to a compiled form
of a large procedure, where only smaller nested procedures are compiled
to machine code.}
]
@section{Racket BC Decompilation}
Racket BC bytecode has a structure that is close enough to Racket's
core language that it can more often be converted to an approximation
of Racket code. To the degree that it can be converted back,
many forms in the decompiled code have the same meanings as many forms in the decompiled code have the same meanings as
always, such as @racket[module], @racket[define], and @racket[lambda]. always, such as @racket[module], @racket[define], and @racket[lambda].
Other forms and transformations are specific to the rendering Other forms and transformations are specific to the rendering
@ -130,19 +158,8 @@ To the degree that it can be converted back to Racket code,
local binding might have a name that starts @racketidfont{flonum} to local binding might have a name that starts @racketidfont{flonum} to
indicate a flonum value.} indicate a flonum value.}
@item{A @racketidfont{#%decode-syntax} form corresponds to a syntax
object.}
@item{A @racketidfont{#%machine-code} form corresponds to machine code
that is not disassembled, where the machine code is in a byte string.}
@item{A @racketidfont{#%assembly-code} form corresponds to disassembled
machine code, where the assembly code is shown as a sequence of strings.}
] ]
@history[#:changed "1.8" @elem{Added @DFlag{no-disassemble}.}]
@; ------------------------------------------------------------ @; ------------------------------------------------------------
@section{API for Decompiling} @section{API for Decompiling}

View File

@ -0,0 +1,5 @@
#lang racket/base
(provide (struct-out opaque))
(struct opaque (bstr))

View File

@ -13,7 +13,8 @@
racket/set racket/set
racket/extflonum racket/extflonum
racket/private/truncate-path racket/private/truncate-path
racket/fasl) racket/fasl
"private/opaque.rkt")
(provide/contract (provide/contract
[zo-marshal ((or/c linkl-directory? linkl-bundle?) . -> . bytes?)] [zo-marshal ((or/c linkl-directory? linkl-bundle?) . -> . bytes?)]
@ -157,9 +158,10 @@
(s-exp->fasl (hash-remove top 'vm) outp)] (s-exp->fasl (hash-remove top 'vm) outp)]
[(#"chez-scheme") [(#"chez-scheme")
(write-bundle-header #"chez-scheme" outp) (write-bundle-header #"chez-scheme" outp)
(define bstr (hash-ref top 'opaque (define opqaue (hash-ref top 'opaque
(lambda () (lambda ()
(error 'zo-marshal "missing 'opaque for chez-scheme virtual-machine format")))) (error 'zo-marshal "missing 'opaque for chez-scheme virtual-machine format"))))
(define bstr (opaque-bstr opaque))
(write-bytes (integer->integer-bytes (bytes-length bstr) 4 #f #f) outp) (write-bytes (integer->integer-bytes (bytes-length bstr) 4 #f #f) outp)
(write-bytes bstr outp)] (write-bytes bstr outp)]
[else [else

View File

@ -7,7 +7,8 @@
racket/dict racket/dict
racket/set racket/set
racket/fasl racket/fasl
ffi/unsafe/vm) ffi/unsafe/vm
"private/opaque.rkt")
(provide zo-parse) (provide zo-parse)
(provide (all-from-out compiler/zo-structs)) (provide (all-from-out compiler/zo-structs))
@ -869,7 +870,7 @@
(open-input-bytes (bytes-append (open-input-bytes (bytes-append
(integer->integer-bytes len 4 #f #f) (integer->integer-bytes len 4 #f #f)
bstr))) bstr)))
'opaque bstr)] 'opaque (opaque bstr))]
[else [else
(hash 'opaque bstr)])] (hash 'opaque bstr)])]
[else [else

View File

@ -1916,6 +1916,7 @@
($current-stack-link [flags single-valued]) ($current-stack-link [flags single-valued])
($current-winders [flags single-valued]) ($current-winders [flags single-valued])
($dequeue-scheme-signals [flags]) ($dequeue-scheme-signals [flags])
($describe-fasl-from-port [sig [(input-port) (input-port vector) -> (ptr)]] [flags])
($distinct-bound-ids? [sig [(list) -> (boolean)]] [flags discard]) ($distinct-bound-ids? [sig [(list) -> (boolean)]] [flags discard])
($dofmt [flags single-valued]) ($dofmt [flags single-valued])
($do-wind [flags single-valued]) ($do-wind [flags single-valued])

View File

@ -757,12 +757,13 @@ floating point returns with (1 0 -1 ...).
[(let ([info ($code-info x)]) [(let ([info ($code-info x)])
(and (code-info? info) (code-info-src info))) => (and (code-info? info) (code-info-src info))) =>
(lambda (src) (lambda (src)
(fprintf p " at ~a:~a" (let ([fn (source-file-descriptor-name (source-sfd src))])
(let ([fn (source-file-descriptor-name (source-sfd src))]) (when (or (string? fn) (symbol? fn))
(if (string? fn) (path-last fn) fn)) (fprintf p " at ~a:~a"
(if (source-2d? src) (if (string? fn) (path-last fn) fn)
(format "~a.~a" (source-2d-line src) (source-2d-column src)) (if (source-2d? src)
(source-bfp src))))]))) (format "~a.~a" (source-2d-line src) (source-2d-column src))
(source-bfp src))))))])))
(define wrprocedure (define wrprocedure
(lambda (x p) (lambda (x p)

View File

@ -1,4 +1,4 @@
;;; strip.ss ;; strip.ss
;;; Copyright 1984-2017 Cisco Systems, Inc. ;;; Copyright 1984-2017 Cisco Systems, Inc.
;;; ;;;
;;; Licensed under the Apache License, Version 2.0 (the "License"); ;;; Licensed under the Apache License, Version 2.0 (the "License");
@ -30,6 +30,7 @@
(flvector vfl) (flvector vfl)
(bytevector ty bv) (bytevector ty bv)
(record maybe-uid size nflds rtd pad-ty* fld*) ; maybe-uid => rtd (record maybe-uid size nflds rtd pad-ty* fld*) ; maybe-uid => rtd
(rtd-ref uid) ; field info not recorded
(closure offset c) (closure offset c)
(flonum high low) (flonum high low)
(small-integer iptr) (small-integer iptr)
@ -124,7 +125,7 @@
((= i n)) ((= i n))
(string-set! s i (integer->char (read-uptr p)))) (string-set! s i (integer->char (read-uptr p))))
s)))) s))))
(define (read-entry p) (define (read-entry p init-g)
(let ([ty (read-byte-or-eof p)]) (let ([ty (read-byte-or-eof p)])
(if (eof-object? ty) (if (eof-object? ty)
ty ty
@ -146,8 +147,8 @@
(if (eqv? compressed-flag (constant fasl-type-gzip)) (if (eqv? compressed-flag (constant fasl-type-gzip))
(constant COMPRESS-GZIP) (constant COMPRESS-GZIP)
(constant COMPRESS-LZ4)))]) (constant COMPRESS-LZ4)))])
(fasl-entry situation (read-fasl (open-bytevector-input-port bv) #f))))] (fasl-entry situation (read-fasl (open-bytevector-input-port bv) init-g))))]
[(fasl-type-uncompressed) (fasl-entry situation (read-fasl p #f))] [(fasl-type-uncompressed) (fasl-entry situation (read-fasl p init-g))]
[else (bogus "expected compression flag in ~a" (port-name p))]))] [else (bogus "expected compression flag in ~a" (port-name p))]))]
[else (bogus "expected header or situation in ~a" (port-name p))])))) [else (bogus "expected header or situation in ~a" (port-name p))]))))
(define (read-header p) (define (read-header p)
@ -200,8 +201,8 @@
(vector-set! v i (vector-set! v i
(let ([key (read-fasl p g)]) (let ([key (read-fasl p g)])
(cons key (read-fasl p g)))))))) (cons key (read-fasl p g))))))))
(define (read-record p g maybe-uid) (define (read-record p g maybe-uid size)
(let* ([size (read-uptr p)] [nflds (read-uptr p)] [rtd (read-fasl p g)]) (let* ([nflds (read-uptr p)] [rtd (read-fasl p g)])
(let loop ([n nflds] [rpad-ty* '()] [rfld* '()]) (let loop ([n nflds] [rpad-ty* '()] [rfld* '()])
(if (fx= n 0) (if (fx= n 0)
(fasl-record maybe-uid size nflds rtd (reverse rpad-ty*) (reverse rfld*)) (fasl-record maybe-uid size nflds rtd (reverse rpad-ty*) (reverse rfld*))
@ -236,8 +237,12 @@
[(fasl-type-bytevector fasl-type-immutable-bytevector) [(fasl-type-bytevector fasl-type-immutable-bytevector)
(fasl-bytevector ty (read-bytevector p (read-uptr p)))] (fasl-bytevector ty (read-bytevector p (read-uptr p)))]
[(fasl-type-base-rtd) (fasl-tuple ty '#())] [(fasl-type-base-rtd) (fasl-tuple ty '#())]
[(fasl-type-rtd) (read-record p g (read-fasl p g))] [(fasl-type-rtd) (let* ([uid (read-fasl p g)]
[(fasl-type-record) (read-record p g #f)] [size (read-uptr p)])
(if (eqv? size 0)
(fasl-rtd-ref uid)
(read-record p g uid size)))]
[(fasl-type-record) (read-record p g #f (read-uptr p))]
[(fasl-type-closure) [(fasl-type-closure)
(let* ([offset (read-uptr p)] (let* ([offset (read-uptr p)]
[c (read-fasl p g)]) [c (read-fasl p g)])
@ -291,11 +296,19 @@
[item-offset (if (fxlogtest type-etc 2) (read-uptr p) 0)]) [item-offset (if (fxlogtest type-etc 2) (read-uptr p) 0)])
(loop (loop
(fx+ n (if (fxlogtest type-etc 1) 3 1)) (fx+ n (if (fxlogtest type-etc 1) 3 1))
(cons (fasl-reloc type-etc code-offset item-offset (read-fasl p g)) rls)))))]) (cons (fasl-reloc type-etc code-offset item-offset (read-fasl p g)) rls)))))]
)
(fasl-code flags free name arity-mask info pinfo* bytes m vreloc))] (fasl-code flags free name arity-mask info pinfo* bytes m vreloc))]
[(fasl-type-immediate fasl-type-entry fasl-type-library fasl-type-library-code) [(fasl-type-immediate fasl-type-entry fasl-type-library fasl-type-library-code)
(fasl-atom ty (read-uptr p))] (fasl-atom ty (read-uptr p))]
[(fasl-type-graph) (read-fasl p (make-vector (read-uptr p) #f))] [(fasl-type-graph) (read-fasl p (let ([new-g (make-vector (read-uptr p) #f)])
(when g
(let ([delta (fx- (vector-length new-g) (vector-length g))])
(let loop ([i 0])
(unless (fx= i (vector-length g))
(vector-set! new-g (fx+ i delta) (vector-ref g i))
(loop (fx+ i 1))))))
new-g))]
[(fasl-type-graph-def) [(fasl-type-graph-def)
(let ([n (read-uptr p)]) (let ([n (read-uptr p)])
(let ([x (read-fasl p g)]) (let ([x (read-fasl p g)])
@ -440,6 +453,7 @@
[ptr (fasl) (build! fasl t)] [ptr (fasl) (build! fasl t)]
[else (void)])) [else (void)]))
fld*))))] fld*))))]
[rtd-ref (uid) (build-graph! x t (lambda () (build! uid #t)))]
[closure (offset c) (build-graph! x t (lambda () (build! c t)))] [closure (offset c) (build-graph! x t (lambda () (build! c t)))]
[flonum (high low) (build-graph! x t void)] [flonum (high low) (build-graph! x t void)]
[small-integer (iptr) (void)] [small-integer (iptr) (void)]
@ -582,6 +596,11 @@
(put-uptr p high) (put-uptr p high)
(put-uptr p low)])) (put-uptr p low)]))
pad-ty* fld*))))] pad-ty* fld*))))]
[rtd-ref (uid)
(write-graph p t x
(lambda ()
(put-uptr p 0)
(write-fasl p t uid)))]
[closure (offset c) [closure (offset c)
(write-graph p t x (write-graph p t x
(lambda () (lambda ()
@ -686,25 +705,198 @@
[header (version machine dependencies) x] [header (version machine dependencies) x]
[else (sorry! "expected entry or header, got ~s" x)]))) [else (sorry! "expected entry or header, got ~s" x)])))
;; Almost the same as fasl-read, but in a rawer form that exposes
;; more of the encoding's structure
(define describe
(lambda (x)
(define-syntax constant-value-case
(syntax-rules (else)
[(_ e0 [(k ...) e1 e2 ...] ... [else ee1 ee2 ...])
(let ([x e0])
(cond
[(memv x (list (constant k) ...)) e1 e2 ...]
...
[else ee1 ee2 ...]))]))
(let ([ht (make-eq-hashtable)])
(define (build-flonum high low)
(let ([bv (make-bytevector 8)])
(bytevector-u64-native-set! bv 0 (bitwise-ior low (bitwise-arithmetic-shift high 32)))
(bytevector-ieee-double-native-ref bv 0)))
(define (describe x)
(cond
[(not (fasl? x))
;; Preumably from the vector of externals
x]
[else
(let ([p (eq-hashtable-cell ht x #f)])
(or (cdr p)
(let ([v (describe-next x)])
(set-cdr! p v)
v)))]))
(define (describe-next x)
(fasl-case x
[entry (situation fasl)
(vector 'ENTRY
situation
(describe fasl))]
[header (version machine dependencies)
(vector 'HEADER
version
machine
dependencies)]
[pair (vfasl)
(let ([len (vector-length vfasl)])
(let loop ([i 0])
(let ([e (describe (vector-ref vfasl i))]
[i (fx+ i 1)])
(if (fx= i len)
e
(cons e (loop i))))))]
[tuple (ty vfasl)
(constant-value-case ty
[(fasl-type-box fasl-type-immutable-box)
(box (describe (vector-ref vfasl 0)))]
[(fasl-type-ratnum)
(/ (describe (vector-ref vfasl 0))
(describe (vector-ref vfasl 1)))]
[(fasl-type-exactnum)
(make-rectangular (describe (vector-ref vfasl 0))
(describe (vector-ref vfasl 1)))]
[(fasl-type-inexactnum)
(make-rectangular (describe (vector-ref vfasl 0))
(describe (vector-ref vfasl 1)))]
[(fasl-type-weak-pair)
(weak-cons (describe (vector-ref vfasl 0))
(describe (vector-ref vfasl 1)))]
[(fasl-type-base-rtd)
#!base-rtd]
[else
'unknown])]
[string (ty string)
(constant-value-case ty
[(fasl-type-symbol) (string->symbol string)]
[else string])]
[gensym (pname uname) (gensym pname uname)]
[vector (ty vfasl) (vector-map describe vfasl)]
[fxvector (viptr) viptr]
[flvector (vfl) vfl]
[bytevector (ty bv) bv]
[record (maybe-uid size nflds rtd pad-ty* fld*)
(vector 'RECORD
(and maybe-uid (describe maybe-uid))
size
nflds
(describe rtd)
(map (lambda (fld)
(field-case fld
[ptr (fasl) (describe fasl)]
[byte (n) n]
[iptr (n) n]
[single (n) n]
[double (high low) (build-flonum high low)]))
fld*))]
[rtd-ref (uid) (vector 'RTD (describe uid))]
[closure (offset c)
(vector 'CLOSURE
offset
(describe c))]
[flonum (high low) (build-flonum high low)]
[large-integer (sign vuptr)
(let loop ([v 0] [i 0])
(cond
[(fx= i (vector-length vuptr))
(if (eqv? sign 1) (- v) v)]
[else (loop (bitwise-ior (bitwise-arithmetic-shift v (constant bigit-bits))
(vector-ref vuptr i))
(fx+ i 1))]))]
[eq-hashtable (mutable? subtype minlen veclen vpfasl)
(let ([ht (make-eq-hashtable)])
(vector-for-each
(lambda (pfasl)
(eq-hashtable-set! ht (car pfasl) (cdr pfasl)))
vpfasl)
ht)]
[symbol-hashtable (mutable? minlen equiv veclen vpfasl)
(let ([ht (make-eq-hashtable)])
(vector-for-each
(lambda (pfasl)
(eq-hashtable-set! ht (car pfasl) (cdr pfasl)))
vpfasl)
ht)]
[code (flags free name arity-mask info pinfo* bytes m vreloc)
(vector 'CODE
flags
free
(describe name)
(describe arity-mask)
(describe info)
(describe pinfo*)
bytes
m
(vector-map describe vreloc))]
[small-integer (iptr) iptr]
[atom (ty uptr)
(constant-value-case ty
[(fasl-type-immediate)
(constant-value-case uptr
[(snil) '()]
[(sfalse) #f]
[(strue) #f]
[(seof) #!eof]
[(sbwp) #!bwp]
[(svoid) (void)]
[else (vector 'IMMEDIATE uptr)])]
[(fasl-type-entry) (vector 'ENTRY uptr)]
[(fasl-type-library) (vector 'LIBRARY uptr)]
[(fasl-type-library-code) (vector 'LIBRARY-CODE uptr)]
[else x])]
[reloc (type-etc code-offset item-offset fasl)
(vector 'RELOC
type-etc
code-offset
item-offset
(describe fasl))]
[indirect (g i) (describe (vector-ref g i))]
[else x]))
(describe x))))
(set-who! $fasl-strip-options (make-enumeration '(inspector-source profile-source source-annotations compile-time-information))) (set-who! $fasl-strip-options (make-enumeration '(inspector-source profile-source source-annotations compile-time-information)))
(set-who! $make-fasl-strip-options (enum-set-constructor $fasl-strip-options)) (set-who! $make-fasl-strip-options (enum-set-constructor $fasl-strip-options))
(let () (let ()
(define read-and-strip-from-port
(lambda (ip ifn init-g)
(let* ([script-header (read-script-header ip)]
[mode (and script-header ifn (unless-feature windows (get-mode ifn)))])
(let loop ([rentry* '()])
(set! fasl-count (fx+ fasl-count 1))
(let ([entry (read-entry ip init-g)])
(if (eof-object? entry)
(begin
(close-port ip)
(values script-header mode (reverse rentry*)))
(let ([entry (if strip-compile-time-information? (keep-revisit-info entry) entry)])
(loop (if entry (cons entry rentry*) rentry*)))))))))
(define read-and-strip-file (define read-and-strip-file
(lambda (ifn) (lambda (ifn)
(let ([ip ($open-file-input-port fasl-who ifn)]) (let ([ip ($open-file-input-port fasl-who ifn)])
(on-reset (close-port ip) (on-reset (close-port ip)
(let* ([script-header (read-script-header ip)] (read-and-strip-from-port ip ifn #f)))))
[mode (and script-header (unless-feature windows (get-mode ifn)))]) (set-who! $describe-fasl-from-port
(let loop ([rentry* '()]) (rec $describe-fasl-from-port
(set! fasl-count (fx+ fasl-count 1)) (case-lambda
(let ([entry (read-entry ip)]) [(ip) ($describe-fasl-from-port ip '#())]
(if (eof-object? entry) [(ip externals)
(begin (unless (input-port? ip) ($oops who "~s is not an input port" ip))
(close-port ip) (fluid-let ([strip-inspector-information? #f]
(values script-header mode (reverse rentry*))) [strip-profile-information? #f]
(let ([entry (if strip-compile-time-information? (keep-revisit-info entry) entry)]) [strip-source-annotations? #f]
(loop (if entry (cons entry rentry*) rentry*))))))))))) [strip-compile-time-information? #f]
[fasl-who who]
[fasl-count 0])
(let-values ([(script-header mode entry*) (read-and-strip-from-port ip #f externals)])
(list (and script-header (describe script-header))
(map describe entry*))))])))
(set-who! strip-fasl-file (set-who! strip-fasl-file
(rec strip-fasl-file (rec strip-fasl-file
(lambda (ifn ofn options) (lambda (ifn ofn options)
@ -811,6 +1003,7 @@
(fasl=? rtd1 rtd2) (fasl=? rtd1 rtd2)
(andmap eqv? pad-ty*1 pad-ty*2) (andmap eqv? pad-ty*1 pad-ty*2)
(andmap fld=? fld*1 fld*2))] (andmap fld=? fld*1 fld*2))]
[rtd-ref (uid) (eq? uid1 uid2)]
[closure (offset c) (and (eqv? offset1 offset2) (fasl=? c1 c2))] [closure (offset c) (and (eqv? offset1 offset2) (fasl=? c1 c2))]
[flonum (high low) [flonum (high low)
(and (eqv? high1 high2) (and (eqv? high1 high2)
@ -895,7 +1088,7 @@
(if (equal? script-header1 script-header2) (if (equal? script-header1 script-header2)
(let loop () (let loop ()
(set! fasl-count (fx+ fasl-count 1)) (set! fasl-count (fx+ fasl-count 1))
(let ([entry1 (read-entry ip1)] [entry2 (read-entry ip2)]) (let ([entry1 (read-entry ip1 #f)] [entry2 (read-entry ip2 #f)])
(if (eof-object? entry1) (if (eof-object? entry1)
(or (eof-object? entry2) (or (eof-object? entry2)
(and error? (bogus "~a has fewer fasl entries than ~a" ifn1 ifn2))) (and error? (bogus "~a has fewer fasl entries than ~a" ifn1 ifn2)))