raco decompile: improve argument checking and reporting
Relevant to PR 14525
This commit is contained in:
parent
9b42fca050
commit
e4189afb15
|
@ -3,25 +3,80 @@
|
||||||
raco/command-name
|
raco/command-name
|
||||||
compiler/zo-parse
|
compiler/zo-parse
|
||||||
compiler/decompile
|
compiler/decompile
|
||||||
racket/pretty)
|
racket/pretty
|
||||||
|
racket/format)
|
||||||
|
|
||||||
|
(define (get-name)
|
||||||
|
(string->symbol (short-program+command-name)))
|
||||||
|
|
||||||
|
(define force? #f)
|
||||||
|
|
||||||
(define source-files
|
(define source-files
|
||||||
(command-line
|
(command-line
|
||||||
#:program (short-program+command-name)
|
#:program (short-program+command-name)
|
||||||
#:once-each
|
#:once-each
|
||||||
|
[("--force") "Ignore timestamp mimatch on associated \".zo\""
|
||||||
|
(set! force? #t)]
|
||||||
[("--columns" "-n") n "Format for <n> columns"
|
[("--columns" "-n") n "Format for <n> columns"
|
||||||
(let ([num (string->number n)])
|
(let ([num (string->number n)])
|
||||||
(unless (exact-positive-integer? num)
|
(unless (exact-positive-integer? num)
|
||||||
(raise-user-error (string->symbol (short-program+command-name))
|
(raise-user-error (get-name)
|
||||||
"not a valid column count: ~a" n))
|
"not a valid column count: ~a" n))
|
||||||
(pretty-print-columns num))]
|
(pretty-print-columns num))]
|
||||||
#:args source-or-bytecode-file
|
#:args source-or-bytecode-file
|
||||||
source-or-bytecode-file))
|
source-or-bytecode-file))
|
||||||
|
|
||||||
|
(define (check-files orig-file alt-file)
|
||||||
|
(cond
|
||||||
|
[(not (file-exists? alt-file))
|
||||||
|
(cond
|
||||||
|
[(file-exists? orig-file)
|
||||||
|
(unless (is-bytecode-file? orig-file)
|
||||||
|
(raise-user-error (get-name)
|
||||||
|
(~a "not a bytecode file, and no associated \".zo\" file\n"
|
||||||
|
" path: ~a\n"
|
||||||
|
" tried associated path: ~a")
|
||||||
|
orig-file
|
||||||
|
alt-file))]
|
||||||
|
[else
|
||||||
|
(raise-user-error (get-name)
|
||||||
|
(~a "no such file, and no associated \".zo\" file\n"
|
||||||
|
" path: ~a\n"
|
||||||
|
" tried associated path: ~a")
|
||||||
|
orig-file
|
||||||
|
alt-file)])]
|
||||||
|
[(not (is-bytecode-file? alt-file))
|
||||||
|
(raise-user-error (get-name)
|
||||||
|
(~a "associated \".zo\" file is not a bytecode file\n"
|
||||||
|
" original path: ~a\n"
|
||||||
|
" associated path: ~a")
|
||||||
|
orig-file
|
||||||
|
alt-file)]
|
||||||
|
[(and (not force?)
|
||||||
|
((file-or-directory-modify-seconds orig-file)
|
||||||
|
. > .
|
||||||
|
(file-or-directory-modify-seconds alt-file)))
|
||||||
|
;; return a warning:
|
||||||
|
(raise-user-error (get-name)
|
||||||
|
(~a "associated \".zo\" file's date is older than given file's date;\n"
|
||||||
|
" consider using `raco make` to rebuild the source file, or use `--force`\n"
|
||||||
|
" to skip the date check\n"
|
||||||
|
" original path: ~a\n"
|
||||||
|
" associated path: ~a")
|
||||||
|
orig-file
|
||||||
|
alt-file)]))
|
||||||
|
|
||||||
|
(define (is-bytecode-file? orig-file)
|
||||||
|
(call-with-input-file*
|
||||||
|
orig-file
|
||||||
|
(lambda (i)
|
||||||
|
(equal? #"#~" (read-bytes 2 i)))))
|
||||||
|
|
||||||
(for ([zo-file source-files])
|
(for ([zo-file source-files])
|
||||||
(let ([zo-file (path->complete-path zo-file)])
|
(let ([zo-file (path->complete-path zo-file)])
|
||||||
(let-values ([(base name dir?) (split-path zo-file)])
|
(let-values ([(base name dir?) (split-path zo-file)])
|
||||||
(let ([alt-file (build-path base "compiled" (path-add-suffix name #".zo"))])
|
(let ([alt-file (build-path base "compiled" (path-add-suffix name #".zo"))])
|
||||||
|
(check-files zo-file alt-file)
|
||||||
(parameterize ([current-load-relative-directory base]
|
(parameterize ([current-load-relative-directory base]
|
||||||
[print-graph #t])
|
[print-graph #t])
|
||||||
(pretty-write
|
(pretty-write
|
||||||
|
|
|
@ -9,13 +9,22 @@
|
||||||
|
|
||||||
@title[#:tag "decompile"]{@exec{raco decompile}: Decompiling Bytecode}
|
@title[#:tag "decompile"]{@exec{raco decompile}: Decompiling Bytecode}
|
||||||
|
|
||||||
The @exec{raco decompile} command takes a bytecode file (which usually
|
The @exec{raco decompile} command takes the path of a bytecode file (which usually
|
||||||
has the file extension @filepath{.zo}) or a source file with an
|
has the file extension @filepath{.zo}) or a source file with an
|
||||||
associated bytecode file (usually created with @exec{raco make}) and
|
associated bytecode file (usually created with @exec{raco make}) and
|
||||||
converts it back to an approximation of Racket code. Decompiled
|
converts the bytecode file's content back to an approximation of Racket code. Decompiled
|
||||||
bytecode is mostly useful for checking the compiler's transformation
|
bytecode is mostly useful for checking the compiler's transformation
|
||||||
and optimization of the source program.
|
and optimization of the source program.
|
||||||
|
|
||||||
|
The @exec{raco decompile} command accepts the following command-line flags:
|
||||||
|
|
||||||
|
@itemlist[
|
||||||
|
@item{@DFlag{force} --- skip modification-date comparison on the
|
||||||
|
given file's path and an associated @filepath{.zo} file (if any)}
|
||||||
|
@item{@Flag{n} @nonterm{n} or @DFlag{columns} @nonterm{n} --- format
|
||||||
|
output for a display with @nonterm{n} columns}
|
||||||
|
]
|
||||||
|
|
||||||
Many forms in the decompiled code, such as @racket[module],
|
Many forms in the decompiled code, such as @racket[module],
|
||||||
@racket[define], and @racket[lambda], have the same meanings as
|
@racket[define], and @racket[lambda], have the same meanings as
|
||||||
always. Other forms and transformations are specific to the rendering
|
always. Other forms and transformations are specific to the rendering
|
||||||
|
@ -104,14 +113,6 @@ Many forms in the decompiled code, such as @racket[module],
|
||||||
of functions without an @racket['%%inline-variant%%] are never
|
of functions without an @racket['%%inline-variant%%] are never
|
||||||
inlined across modules.}
|
inlined across modules.}
|
||||||
|
|
||||||
@item{Some applications of core primitives are annotated with
|
|
||||||
@racketidfont{#%in}, which indicates that the JIT compiler will
|
|
||||||
inline the operation. (Inlining information is not part of the
|
|
||||||
bytecode, but is instead based on an enumeration of primitives that
|
|
||||||
the JIT is known to handle specially.) Operations from
|
|
||||||
@racketmodname[racket/flonum] and @racketmodname[racket/unsafe/ops]
|
|
||||||
are always inlined, so @racketidfont{#%in} is not shown for them.}
|
|
||||||
|
|
||||||
@item{Function arguments and local bindings that are known to have a
|
@item{Function arguments and local bindings that are known to have a
|
||||||
particular type have names that embed the known type. For example, an
|
particular type have names that embed the known type. For example, an
|
||||||
argument might have a name that starts @racketidfont{argflonum} or a
|
argument might have a name that starts @racketidfont{argflonum} or a
|
||||||
|
@ -123,14 +124,6 @@ Many forms in the decompiled code, such as @racket[module],
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
Command-line flags:
|
|
||||||
|
|
||||||
@itemlist[
|
|
||||||
@item{@Flag{n} @nonterm{n} or @DFlag{columns} @nonterm{n} --- format output for a display with @nonterm{n} columns}
|
|
||||||
@item{@Flag{h} or @DFlag{help} --- show help information for this command}
|
|
||||||
@item{@DFlag{} --- do not treat remaining arguments as switches}
|
|
||||||
]
|
|
||||||
|
|
||||||
@; ------------------------------------------------------------
|
@; ------------------------------------------------------------
|
||||||
|
|
||||||
@section{API for Decompiling}
|
@section{API for Decompiling}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user