7 raco decompile: Decompiling Bytecode

▼ |  | raco: Racket Command-Line Tools

1 | raco make: Compiling Source to Bytecode
2 | raco exe: Creating Stand-Alone Executables
3 | raco distribute: Sharing Stand-Alone Executables
4 | raco planet: Automatic Package Distribution
5 | raco pkg: Package Management
6 | raco setup: Installation Management
7 | raco decompile: Decompiling Bytecode
8 | raco demod: Demodularizing Programs
9 | raco link: Library Collection Links
10 | raco pack: Packing Library Collections
11 | raco unpack: Unpacking Library Collections
12 | raco ctool: Working with C Code
13 | raco test: Run tests
14 | raco docs: Documentation Search
15 | raco expand: Macro Expansion
16 | raco read: Reading and Pretty-Printing
17 | raco scribble: Building Documentation
18 | Adding a raco Command
19 | Installation Configuration and Search Paths

► | 7 | raco decompile: Decompiling Bytecode

7.1 | API for Decompiling
7.2 | API for Parsing Bytecode
7.3 | API for Marshaling Bytecode
7.4 | Bytecode Representation

On this page:

7.1`` API for Decompiling
decompile
7.2`` API for Parsing Bytecode
zo-parse
decode-module-binding
7.3`` API for Marshaling Bytecode
zo-marshal-to
zo-marshal
7.4`` Bytecode Representation
zo
7.4.1`` Prefix
compilation-top
prefix
global-bucket
module-variable
function-shape
struct-shape
struct-type-shape
constructor-shape
predicate-shape
accessor-shape
mutator-shape
struct-other-shape
stx
7.4.2`` Forms
form
def-values
def-syntaxes
seq-for-syntax
req
seq
splice
inline-variant
mod
provided
7.4.3`` Expressions
expr
lam
closure
case-lam
let-one
let-void
install-value
let-rec
boxenv
localref
toplevel
topsyntax
application
branch
with-cont-mark
beg0
varref
assign
apply-values
with-immed-mark
primval
7.4.4`` Syntax Objects
stx-obj
wrap
module-shift
scope
multi-scope
binding
module-binding
decoded-module-binding
local-binding
free-id=?-binding
all-from-module

top ← prev up next →

### 7`` raco decompile: Decompiling Bytecode

The raco decompile command takes the path of a bytecode file (which usually has the file extension ".zo") or a source file with an associated bytecode file (usually created with raco make) and converts the bytecode file’s content back to an approximation of Racket code. Decompiled bytecode is mostly useful for checking the compiler’s transformation and optimization of the source program.

The raco decompile command accepts the following command-line flags:

- --force — skip modification-date comparison on the given file’s path and an associated ".zo" file (if any)

- -n ‹n› or --columns ‹n› — format output for a display with ‹n› columns

Many forms in the decompiled code, such as module, define, and lambda, have the same meanings as always. Other forms and transformations are specific to the rendering of bytecode, and they reflect a specific execution model:

- Top-level variables, variables defined within the module, and variables imported from other modules are prefixed with _, which helps expose the difference between uses of local variables versus other variables. Variables imported from other modules, moreover, have a suffix starting with @ that indicates the source module. Finally, imported variables with constantness have a midfix: :c to indicate constant shape across all instantiations, :f to indicate a fixed value after initialization, :p to indicate a procedure, :P to indicate a procedure that preserves continuation marks on return, :t to indicate a structure type, :mk to indicate a structure constructor, :? to indicate a structure predicate, :ref to indicate a structure accessor, or :set! to indicate a structure mutator.

  Non-local variables are always accessed indirectly though an implicit #%globals or #%modvars variable that resides on the value stack (which otherwise contains local variables). Variable accesses are further wrapped with #%checked when the compiler cannot prove that the variable will be defined before the access.

  Uses of core primitives are shown without a leading _, and they are never wrapped with #%checked.

- Local-variable access may be wrapped with #%sfs-clear, which indicates that the variable-stack location holding the variable will be cleared to prevent the variable’s value from being retained by the garbage collector. Variables whose name starts with unused are never actually stored on the stack, and so they never have #%sfs-clear annotations. (The bytecode compiler normally eliminates such bindings, but sometimes it cannot, either because it cannot prove that the right-hand side produces the right number of values, or the discovery that the variable is unused happens too late with the compiler.)

  Mutable variables are converted to explicitly boxed values using #%box, #%unbox, and #%set-boxes! (which works on multiple boxes at once). A set!-rec-values operation constructs mutually-recursive closures and simultaneously updates the corresponding variable-stack locations that bind the closures. A set!, set!-values, or set!-rec-values form is always used on a local variable before it is captured by a closure; that ordering reflects how closures capture values in variable-stack locations, as opposed to stack locations.

- In a lambda form, if the procedure produced by the lambda has a name (accessible via object-name) and/or source-location information, then it is shown as a quoted constant at the start of the procedure’s body. Afterward, if the lambda form captures any bindings from its context, those bindings are also shown in a quoted constant. Neither constant corresponds to a computation when the closure is called, though the list of captured bindings corresponds to a closure allocation when the lambda form itself is evaluated.

  A lambda form that closes over no bindings is wrapped with #%closed plus an identifier that is bound to the closure. The binding’s scope covers the entire decompiled output, and it may be referenced directly in other parts of the program; the binding corresponds to a constant closure value that is shared, and it may even contain cyclic references to itself or other constant closures.

- A form (#%apply-values proc expr) is equivalent to (call-with-values (lambda () expr) proc), but the run-time system avoids allocating a closure for expr. Similarly, a #%call-with-immediate-continuation-mark call is equivalent to a call-with-immediate-continuation-mark call, but avoiding a closure allocation.

- A define-values form may have (begin '%%inline-variant%% expr1 expr2) for its expression, in which case expr2 is the normal result, but expr1 may be inlined for calls to the definition from other modules. Definitions of functions without an '%%inline-variant%% are never inlined across modules.

- Function arguments and local bindings that are known to have a particular type have names that embed the known type. For example, an argument might have a name that starts argflonum or a local binding might have a name that starts flonum to indicate a flonum value.

- A #%decode-syntax form corresponds to a syntax object.

#### 7.1`` API for Decompiling

(require compiler/decompile) | package: compiler-lib

> procedure (decompile top) → any/c
> top : compilation-top?

Consumes the result of parsing bytecode and returns an S-expression (as described above) that represents the compiled code.

#### 7.2`` API for Parsing Bytecode

(require compiler/zo-parse) | package: zo-lib

The compiler/zo-parse module re-exports compiler/zo-structs in addition to zo-parse.

> procedure (zo-parse [in]) → compilation-top?
> in : input-port? = (current-input-port)

Parses a port (typically the result of opening a ".zo" file) containing bytecode. Beware that the structure types used to represent the bytecode are subject to frequent changes across Racket versons.

The parsed bytecode is returned in a compilation-top structure. For a compiled module, the compilation-top structure will contain a mod structure. For a top-level sequence, it will normally contain a seq or splice structure with a list of top-level declarations and expressions.

The bytecode representation of an expression is closer to an S-expression than a traditional, flat control string. For example, an if form is represented by a branch structure that has three fields: a test expression, a “then” expression, and an “else” expression. Similarly, a function call is represented by an application structure that has a list of argument expressions.

Storage for local variables or intermediate values (such as the arguments for a function call) is explicitly specified in terms of a stack. For example, execution of an application structure reserves space on the stack for each argument result. Similarly, when a let-one structure (for a simple let) is executed, the value obtained by evaluating the right-hand side expression is pushed onto the stack, and then the body is evaluated. Local variables are always accessed as offsets from the current stack position. When a function is called, its arguments are passed on the stack. A closure is created by transferring values from the stack to a flat closure record, and when a closure is applied, the saved values are restored on the stack (though possibly in a different order and likely in a more compact layout than when they were captured).

When a sub-expression produces a value, then the stack pointer is restored to its location from before evaluating the sub-expression. For example, evaluating the right-hand size for a let-one structure may temporarily push values onto the stack, but the stack is restored to its pre-let-one position before pushing the resulting value and continuing with the body. In addition, a tail call resets the stack pointer to the position that follows the enclosing function’s arguments, and then the tail call continues by pushing onto the stack the arguments for the tail-called function.

Values for global and module-level variables are not put directly on the stack, but instead stored in “buckets,” and an array of accessible buckets is kept on the stack. When a closure body needs to access a global variable, the closure captures and later restores the bucket array in the same way that it captured and restores a local variable. Mutable local variables are boxed similarly to global variables, but individual boxes are referenced from the stack and closures.

Quoted syntax (in the sense of quote-syntax) is treated like a global variable, because it must be instantiated for an appropriate phase. A prefix structure within a compilation-top or mod structure indicates the list of global variables and quoted syntax that need to be instantiated (and put into an array on the stack) before evaluating expressions that might use them.

> procedure (decode-module-binding binding name) → decoded-module-binding?
> binding : module-binding?
> name : symbol?

Given a compact-form representation of a module binding and the name from which the binding is mapped, returns a normalized form of the binding.

#### 7.3`` API for Marshaling Bytecode

(require compiler/zo-marshal) | package: zo-lib

> procedure (zo-marshal-to top out) → void?
> top : compilation-top?
> out : output-port?

Consumes a representation of bytecode and writes it to out.

> procedure (zo-marshal top) → bytes?
> top : compilation-top?

Consumes a representation of bytecode and generates a byte string for the marshaled bytecode.

#### 7.4`` Bytecode Representation

(require compiler/zo-structs) | package: zo-lib

The compiler/zo-structs library defines the bytecode structures that are produced by zo-parse and consumed by decompile and zo-marshal.

> Warning: The compiler/zo-structs library exposes internals of the Racket bytecode abstraction. Unlike other Racket libraries, compiler/zo-structs is subject to incompatible changes across Racket versions.

> > struct
> > (struct |  | zo ()
> #:extra-constructor-name make-zo
> #:prefab)

A supertype for all forms that can appear in compiled code.

##### 7.4.1`` Prefix

> > struct
> > (struct |  | compilation-top zo | ( | max-let-depth
> > |  |  |  | binding-namess
> > |  |  |  | prefix
> > |  |  |  | code)
> #:extra-constructor-name make-compilation-top
> #:prefab)
> max-let-depth : exact-nonnegative-integer?
> | binding-namess |  | : |  |
> (hash/c exact-nonnegative-integer?
> (hash/c symbol? stx?))
> prefix : prefix?
> code : (or/c form? any/c)

Wraps compiled code.

The max-let-depth field indicates the maximum stack depth that code creates (not counting the prefix array).

The binding-namess field provides a per-phase mapping from symbols that appear in prefix for top-level def-values forms and in top-level def-syntaxes forms. Each symbol is mapped to an identifier that will be bound (after introduction into the namespace) by the definition.

The prefix field describes top-level variables, module-level variables, and quoted syntax-objects accessed by code.

The code field contains executable code; it is normally a form, but a literal value is represented as itself.

> > struct
> > (struct |  | prefix zo (num-lifts toplevels stxs src-inspector-desc)
> #:extra-constructor-name make-prefix
> #:prefab)
> num-lifts : exact-nonnegative-integer?
> | toplevels |  | : |  |
> (listof (or/c #f symbol? global-bucket?
> module-variable?))
> stxs : (listof (or stx? #f))
> src-inspector-desc : symbol?

Represents a “prefix” that is pushed onto the stack to initiate evaluation. The prefix is an array, where buckets holding the values for toplevels are first, then the buckets for the stxs, then a bucket for another array if stxs is non-empty, then num-lifts extra buckets for lifted local procedures.

In toplevels, each element is one of the following:

- a #f, which indicates a dummy variable that is used to access the enclosing module/namespace at run time;

- a symbol, which is a reference to a variable defined in the enclosing module;

- a global-bucket, which is a top-level variable (appears only outside of modules); or

- a module-variable, which indicates a variable imported from another module.

The variable buckets and syntax objects that are recorded in a prefix are accessed by toplevel and topsyntax expression forms.

When an element of stxs is #f, it coresponds to a syntax object that was optimized away at the last minute. The slot must not be referenced vt a topsyntax form.

The src-inspector-desc field provides an inspector name that is used within syntax-object bindings. At run time, the prefix gets an inspector, and bindings that reference the same inspector name are granted access capabilities through that inspector.

> > struct
> > (struct |  | global-bucket zo (name)
> #:extra-constructor-name make-global-bucket
> #:prefab)
> name : symbol?

Represents a top-level variable, and used only in a prefix.

> > struct
> > (struct |  | module-variable zo (modidx sym pos phase constantness)
> #:extra-constructor-name make-module-variable
> #:prefab)
> modidx : module-path-index?
> sym : symbol?
> pos : exact-integer?
> phase : exact-nonnegative-integer?
> | constantness |  | : |  |
> (or/c #f 'constant 'fixed
> function-shape? struct-shape?)

Represents a top-level variable, and used only in a prefix. The pos may record the variable’s offset within its module, or it can be -1 if the variable is always located by name. The phase indicates the phase level of the definition within its module. The constantness field is either 'constant, a function-shape value, or a struct-shape value to indicate that variable’s value is always the same for every instantiation of its module; 'fixed to indicate that it doesn’t change within a particular instantiation of the module; or #f to indicate that the variable’s value can change even for one particular instantiation of its module.

> > struct
> > (struct |  | function-shape (arity preserves-marks?)
> #:extra-constructor-name make-function-shape
> #:prefab)
> arity : procedure-arity?
> preserves-marks? : boolean?

Represents the shape of an expected import, which should be a function having the arity specified by arity. The preserves-marks? field is true if calling the function is expected to leave continuation marks unchanged by the time it returns.

> > struct
> > (struct |  | struct-shape ()
> #:extra-constructor-name make-struct-shape
> #:prefab)
> > struct
> > (struct |  | struct-type-shape struct-shape (field-count)
> #:extra-constructor-name make-struct-type-shape
> #:prefab)
> field-count : exact-nonnegative-integer?
> > struct
> > (struct |  | constructor-shape struct-shape (arity)
> #:extra-constructor-name make-constructor-shape
> #:prefab)
> arity : exact-nonnegative-integer?
> > struct
> > (struct |  | predicate-shape struct-shape ()
> #:extra-constructor-name make-predicate-shape
> #:prefab)
> > struct
> > (struct |  | accessor-shape struct-shape (field-count)
> #:extra-constructor-name make-accessor-shape
> #:prefab)
> field-count : exact-nonnegative-integer?
> > struct
> > (struct |  | mutator-shape struct-shape (field-count)
> #:extra-constructor-name make-mutator-shape
> #:prefab)
> field-count : exact-nonnegative-integer?
> > struct
> > (struct |  | struct-other-shape struct-shape ()
> #:extra-constructor-name make-struct-other-shape
> #:prefab)

Represents the shape of an expected import as a structure-type binding, constructor, etc.

> > struct
> > (struct |  | stx zo (content)
> #:extra-constructor-name make-stx
> #:prefab)
> content : stx-obj?

Wraps a syntax object as it appears in a prefix.

##### 7.4.2`` Forms

> > struct
> > (struct |  | form zo ()
> #:extra-constructor-name make-form
> #:prefab)

A supertype for all forms that can appear in compiled code (including exprs), except for literals that are represented as themselves.

> > struct
> > (struct |  | def-values form (ids rhs)
> #:extra-constructor-name make-def-values
> #:prefab)
> ids : (listof toplevel?)
> rhs : (or/c expr? seq? inline-variant? any/c)

Represents a define-values form. Each element of ids will reference via the prefix either a top-level variable or a local module variable.

After rhs is evaluated, the stack is restored to its depth from before evaluating rhs.

> > struct
> > (struct |  | def-syntaxes form (ids rhs prefix max-let-depth dummy)
> #:extra-constructor-name make-def-syntaxes
> #:prefab)
> ids : (listof symbol?)
> rhs : (or/c expr? seq? any/c)
> prefix : prefix?
> max-let-depth : exact-nonnegative-integer?
> dummy : (or/c toplevel? #f)
> > struct
> > (struct |  | seq-for-syntax form (forms prefix max-let-depth dummy)
> #:extra-constructor-name make-seq-for-syntax
> #:prefab)
> forms : (listof (or/c form? any/c))
> prefix : prefix?
> max-let-depth : exact-nonnegative-integer?
> dummy : (or/c toplevel? #f)

Represents a define-syntaxes or begin-for-syntax form. The rhs expression or set of forms forms has its own prefix, which is pushed before evaluating rhs or the forms; the stack is restored after obtaining the result values. The max-let-depth field indicates the maximum size of the stack that will be created by rhs (not counting prefix). The dummy variable is used to access the enclosing namespace.

> > struct
> > (struct |  | req form (reqs dummy)
> #:extra-constructor-name make-req
> #:prefab)
> reqs : stx?
> dummy : toplevel?

Represents a top-level #%require form (but not one in a module form) with a sequence of specifications reqs. The dummy variable is used to access the top-level namespace.

> > struct
> > (struct |  | seq form (forms)
> #:extra-constructor-name make-seq
> #:prefab)
> forms : (listof (or/c form? any/c))

Represents a begin form, either as an expression or at the top level (though the latter is more commonly a splice form). When a seq appears in an expression position, its forms are expressions.

After each form in forms is evaluated, the stack is restored to its depth from before evaluating the form.

> > struct
> > (struct |  | splice form (forms)
> #:extra-constructor-name make-splice
> #:prefab)
> forms : (listof (or/c form? any/c))

Represents a top-level begin form where each evaluation is wrapped with a continuation prompt.

After each form in forms is evaluated, the stack is restored to its depth from before evaluating the form.

> > struct
> > (struct |  | inline-variant form (direct inline)
> #:extra-constructor-name make-inline-variant
> #:prefab)
> direct : expr?
> inline : expr?

Represents a function that is bound by define-values, where the function has two variants. The first variant is used for normal calls to the function. The second may be used for cross-module inlining of the function.

> > struct
> > (struct |  | mod form | ( | name
> > |  |  |  | srcname
> > |  |  |  | self-modidx
> > |  |  |  | prefix
> > |  |  |  | provides
> > |  |  |  | requires
> > |  |  |  | body
> > |  |  |  | syntax-bodies
> > |  |  |  | unexported
> > |  |  |  | max-let-depth
> > |  |  |  | dummy
> > |  |  |  | lang-info
> > |  |  |  | internal-context
> > |  |  |  | binding-names
> > |  |  |  | flags
> > |  |  |  | pre-submodules
> > |  |  |  | post-submodules)
> #:extra-constructor-name make-mod
> #:prefab)
> name : (or/c symbol? (listof symbol?))
> srcname : symbol?
> self-modidx : module-path-index?
> prefix : prefix?
> | provides |  | : |  |
> (listof (list/c (or/c exact-integer? #f)
> (listof provided?)
> (listof provided?)))
> | requires |  | : |  |
> (listof (cons/c (or/c exact-integer? #f)
> (listof module-path-index?)))
> body : (listof (or/c form? any/c))
> | syntax-bodies |  | : |  |
> (listof (cons/c exact-positive-integer?
> (listof (or/c def-syntaxes?
> seq-for-syntax?))))
> | unexported |  | : |  |
> (listof (list/c exact-nonnegative-integer?
> (listof symbol?)
> (listof symbol?)))
> max-let-depth : exact-nonnegative-integer?
> dummy : toplevel?
> lang-info : (or/c #f (vector/c module-path? symbol? any/c))
> internal-context : (or/c #f #t stx? (vectorof stx?))
> | binding-names |  | : |  |
> (hash/c exact-integer?
> (hash/c symbol? (or/c #t stx?)))
> flags : (listof (or/c 'cross-phase))
> pre-submodules : (listof mod?)
> post-submodules : (listof mod?)

Represents a module declaration.

The provides and requires lists are each an association list from phases to exports or imports. In the case of provides, each phase maps to two lists: one for exported variables, and another for exported syntax. In the case of requires, each phase maps to a list of imported module paths.

The body field contains the module’s run-time (i.e., phase 0) code. The syntax-bodies list has a list of forms for each higher phase in the module body; the phases are in order starting with phase 1. The body forms use prefix, rather than any prefix in place for the module declaration itself, while members of lists in syntax-bodies have their own prefixes. After each form in body or syntax-bodies is evaluated, the stack is restored to its depth from before evaluating the form.

The unexported list contains lists of symbols for unexported definitions that can be accessed through macro expansion and that are implemented through the forms in body and syntax-bodies. Each list in unexported starts with a phase level.

The max-let-depth field indicates the maximum stack depth created by body forms (not counting the prefix array). The dummy variable is used to access to the top-level namespace.

The lang-info value specifies an optional module path that provides information about the module’s implementation language.

The internal-context value describes the lexical context of the body of the module. This value is used by module->namespace. A #f value means that the context is unavailable or empty. A #t value means that the context is computed by re-importing all required modules. A syntax-object value embeds lexical information; the syntax object should contain a vector of two elements, where the first element of the vector is a syntax object for the module’s body, which includes the outside-edge and inside-edge scopes, and the second element of the vector is a syntax object that has just the module’s inside-edge scope.

The binding-names value provides additional information to module->namespace to correlate symbol names for variables and syntax definitions to identifiers that map to those variables. A separate table of names exists for each phase, and a #t mapping for a name indicates that it is mapped but inaccessible (because the relevant scopes are inaccessible).

The flags field records certain properties of the module. The 'cross-phase flag indicates that the module body is evaluated once and the results shared across instances for all phases; such a module contains only definitions of functions, structure types, and structure type properties.

The pre-submodules field records module-declared submodules, while the post-submodules field records module*-declared submodules.

> > struct
> > (struct |  | provided zo | ( | name
> > |  |  |  | src
> > |  |  |  | src-name
> > |  |  |  | nom-src
> > |  |  |  | src-phase
> > |  |  |  | protected?)
> #:extra-constructor-name make-provided
> #:prefab)
> name : symbol?
> src : (or/c module-path-index? #f)
> src-name : symbol?
> nom-src : (or/c module-path-index? #f)
> src-phase : exact-nonnegative-integer?
> protected? : boolean?

Describes an individual provided identifier within a mod instance.

##### 7.4.3`` Expressions

> > struct
> > (struct |  | expr form ()
> #:extra-constructor-name make-expr
> #:prefab)

A supertype for all expression forms that can appear in compiled code, except for literals that are represented as themselves and some seq structures (which can appear as an expression as long as it contains only other things that can be expressions).

> > struct
> > (struct |  | lam expr | ( | name
> > |  |  |  | flags
> > |  |  |  | num-params
> > |  |  |  | param-types
> > |  |  |  | rest?
> > |  |  |  | closure-map
> > |  |  |  | closure-types
> > |  |  |  | toplevel-map
> > |  |  |  | max-let-depth
> > |  |  |  | body)
> #:extra-constructor-name make-lam
> #:prefab)
> name : (or/c symbol? vector?)
> | flags |  | : |  |
> (listof (or/c 'preserves-marks 'is-method 'single-result
> 'only-rest-arg-not-used 'sfs-clear-rest-args))
> num-params : exact-nonnegative-integer?
> param-types : (listof (or/c 'val 'ref 'flonum 'fixnum 'extflonum))
> rest? : boolean?
> closure-map : (vectorof exact-nonnegative-integer?)
> closure-types : (listof (or/c 'val/ref 'flonum 'fixnum 'extflonum))
> toplevel-map : (or/c #f (set/c exact-nonnegative-integer?))
> max-let-depth : exact-nonnegative-integer?
> body : (or/c expr? seq? any/c)

Represents a lambda form. The name field is a name for debugging purposes. The num-params field indicates the number of arguments accepted by the procedure, not counting a rest argument; the rest? field indicates whether extra arguments are accepted and collected into a “rest” variable. The param-types list contains num-params symbols indicating the type of each argumet, either 'val for a normal argument, 'ref for a boxed argument (representing a mutable local variable), 'flonum for a flonum argument, or 'extflonum for an extflonum argument.

The closure-map field is a vector of stack positions that are captured when evaluating the lambda form to create a closure. The closure-types field provides a corresponding list of types, but no distinction is made between normal values and boxed values; also, this information is redundant, since it can be inferred by the bindings referenced though closure-map.

When a closure captures top-level or module-level variables or refers to a syntax-object constant, the variables and constants are represented in the closure by capturing a prefix (in the sense of prefix). The toplevel-map field indicates which top-level and lifted variables are actually used by the closure (so that variables in a prefix can be pruned by the run-time system if they become unused) and whether any syntax objects are used (so that the syntax objects as a group can be similarly pruned). A #f value indicates either that no prefix is captured or all variables and syntax objects in the prefix should be considered used. Otherwise, numbers in the set indicate which variables and lifted variables are used. Variables are numbered consecutively by position in the prefix starting from 0, but the number equal to the number of non-lifted variables corresponds to syntax objects (i.e., the number is include if any syntax-object constant is used). Lifted variables are numbered immediately afterward—which means that, if the prefix contains any syntax objects, lifted-variable numbers are shifted down relative to a toplevel by the number of syntax object in the prefix (which makes the toplevel-map set more compact).

When the function is called, the rest-argument list (if any) is pushed onto the stack, then the normal arguments in reverse order, then the closure-captured values in reverse order. Thus, when body is run, the first value on the stack is the first value captured by the closure-map array, and so on.

The max-let-depth field indicates the maximum stack depth created by body plus the arguments and closure-captured values pushed onto the stack. The body field is the expression for the closure’s body.

Changed in version 6.1.1.8 of package zo-lib: Added a number to toplevel-map to indicate whether any syntax object is used, shifting numbers for lifted variables up by one if any syntax object is in the prefix.

> > struct
> > (struct |  | closure expr (code gen-id)
> #:extra-constructor-name make-closure
> #:prefab)
> code : lam?
> gen-id : symbol?

A lambda form with an empty closure, which is a procedure constant. The procedure constant can appear multiple times in the graph of expressions for bytecode, and the code field can be a cycle for a recursive constant procedure; the gen-id is different for each such constant.

> > struct
> > (struct |  | case-lam expr (name clauses)
> #:extra-constructor-name make-case-lam
> #:prefab)
> name : (or/c symbol? vector?)
> clauses : (listof lam?)

Represents a case-lambda form as a combination of lambda forms that are tried (in order) based on the number of arguments given.

> > struct
> > (struct |  | let-one expr (rhs body type unused?)
> #:extra-constructor-name make-let-one
> #:prefab)
> rhs : (or/c expr? seq? any/c)
> body : (or/c expr? seq? any/c)
> type : (or/c #f 'flonum 'fixnum 'extflonum)
> unused? : boolean?

Pushes an uninitialized slot onto the stack, evaluates rhs and puts its value into the slot, and then runs body. If type is not #f, then rhs must produce a value of the corresponding type, and the slot must be accessed by localrefs that expect the type. If unused? is #t, then the slot must not be used, and the value of rhs is not actually pushed onto the stack (but rhs is constrained to produce a single value).

After rhs is evaluated, the stack is restored to its depth from before evaluating rhs. Note that the new slot is created before evaluating rhs.

> > struct
> > (struct |  | let-void expr (count boxes? body)
> #:extra-constructor-name make-let-void
> #:prefab)
> count : exact-nonnegative-integer?
> boxes? : boolean?
> body : (or/c expr? seq? any/c)

Pushes count uninitialized slots onto the stack and then runs body. If boxes? is #t, then the slots are filled with boxes that contain #<undefined>.

> > struct
> > (struct |  | install-value expr (count pos boxes? rhs body)
> #:extra-constructor-name make-install-value
> #:prefab)
> count : exact-nonnegative-integer?
> pos : exact-nonnegative-integer?
> boxes? : boolean?
> rhs : (or/c expr? seq? any/c)
> body : (or/c expr? seq? any/c)

Runs rhs to obtain count results, and installs them into existing slots on the stack in order, skipping the first pos stack positions. If boxes? is #t, then the values are put into existing boxes in the stack slots.

After rhs is evaluated, the stack is restored to its depth from before evaluating rhs.

> > struct
> > (struct |  | let-rec expr (procs body)
> #:extra-constructor-name make-let-rec
> #:prefab)
> procs : (listof lam?)
> body : (or/c expr? seq? any/c)

Represents a letrec form with lambda bindings. It allocates a closure shell for each lambda form in procs, installs each onto the stack in previously allocated slots in reverse order (so that the closure shell for the last element of procs is installed at stack position 0), fills out each shell’s closure (where each closure normally references some other just-created closures, which is possible because the shells have been installed on the stack), and then evaluates body.

> > struct
> > (struct |  | boxenv expr (pos body)
> #:extra-constructor-name make-boxenv
> #:prefab)
> pos : exact-nonnegative-integer?
> body : (or/c expr? seq? any/c)

Skips pos elements of the stack, setting the slot afterward to a new box containing the slot’s old value, and then runs body. This form appears when a lambda argument is mutated using set! within its body; calling the function initially pushes the value directly on the stack, and this form boxes the value so that it can be mutated later.

> > struct
> > (struct |  | localref expr (unbox? pos clear? other-clears? type)
> #:extra-constructor-name make-localref
> #:prefab)
> unbox? : boolean?
> pos : exact-nonnegative-integer?
> clear? : boolean?
> other-clears? : boolean?
> type : (or/c #f 'flonum 'fixnum 'extflonum)

Represents a local-variable reference; it accesses the value in the stack slot after the first pos slots. If unbox? is #t, the stack slot contains a box, and a value is extracted from the box. If clear? is #t, then after the value is obtained, the stack slot is cleared (to avoid retaining a reference that can prevent reclamation of the value as garbage). If other-clears? is #t, then some later reference to the same stack slot may clear after reading. If type is not #f, the slot is known to hold a specific type of value.

> > struct
> > (struct |  | toplevel expr (depth pos const? ready?)
> #:extra-constructor-name make-toplevel
> #:prefab)
> depth : exact-nonnegative-integer?
> pos : exact-nonnegative-integer?
> const? : boolean?
> ready? : boolean?

Represents a reference to a top-level or imported variable via the prefix array. The depth field indicates the number of stack slots to skip to reach the prefix array, and pos is the offset into the array.

When the toplevel is an expression, if both const? and ready? are #t, then the variable definitely will be defined, its value stays constant, and the constant is effectively the same for every module instantiation. If only const? is #t, then the value is constant, but it may vary across instantiations. If only ready? is #t, then the variable definitely will be defined, but its value may change. If const? and ready? are both #f, then a check is needed to determine whether the variable is defined.

When the toplevel is the right-hand side for def-values, then const? is #f. If ready? is #t, the variable is marked as immutable after it is defined.

> > struct
> > (struct |  | topsyntax expr (depth pos midpt)
> #:extra-constructor-name make-topsyntax
> #:prefab)
> depth : exact-nonnegative-integer?
> pos : exact-nonnegative-integer?
> midpt : exact-nonnegative-integer?

Represents a reference to a quoted syntax object via the prefix array. The depth field indicates the number of stack slots to skip to reach the prefix array, and pos is the offset into the array. The midpt value is used internally for lazy calculation of syntax information.

> > struct
> > (struct |  | application expr (rator rands)
> #:extra-constructor-name make-application
> #:prefab)
> rator : (or/c expr? seq? any/c)
> rands : (listof (or/c expr? seq? any/c))

Represents a function call. The rator field is the expression for the function, and rands are the argument expressions. Before any of the expressions are evaluated, (length rands) uninitialized stack slots are created (to be used as temporary space).

> > struct
> > (struct |  | branch expr (test then else)
> #:extra-constructor-name make-branch
> #:prefab)
> test : (or/c expr? seq? any/c)
> then : (or/c expr? seq? any/c)
> else : (or/c expr? seq? any/c)

Represents an if form.

After test is evaluated, the stack is restored to its depth from before evaluating test.

> > struct
> > (struct |  | with-cont-mark expr (key val body)
> #:extra-constructor-name make-with-cont-mark
> #:prefab)
> key : (or/c expr? seq? any/c)
> val : (or/c expr? seq? any/c)
> body : (or/c expr? seq? any/c)

Represents a with-continuation-mark expression.

After each of key and val is evaluated, the stack is restored to its depth from before evaluating key or val.

> > struct
> > (struct |  | beg0 expr (seq)
> #:extra-constructor-name make-beg0
> #:prefab)
> seq : (listof (or/c expr? seq? any/c))

Represents a begin0 expression.

After each expression in seq is evaluated, the stack is restored to its depth from before evaluating the expression.

Unlike the begin0 source form, the first expression in seq is never in tail position, even if it is the only expression in the list.

> > struct
> > (struct |  | varref expr (toplevel dummy)
> #:extra-constructor-name make-varref
> #:prefab)
> toplevel : (or/c toplevel? #t)
> dummy : (or/c toplevel? #f)

Represents a #%variable-reference form. The toplevel field is #t if the original reference was to a constant local binding. The dummy field accesses a variable bucket that strongly references its namespace (as opposed to a normal variable bucket, which only weakly references its namespace); it can be #f.

> > struct
> > (struct |  | assign expr (id rhs undef-ok?)
> #:extra-constructor-name make-assign
> #:prefab)
> id : toplevel?
> rhs : (or/c expr? seq? any/c)
> undef-ok? : boolean?

Represents a set! expression that assigns to a top-level or module-level variable. (Assignments to local variables are represented by install-value expressions.) If undef-ok? is true, the assignment to id succeeds even if id was not previously defined (see also compile-allow-set!-undefined).

After rhs is evaluated, the stack is restored to its depth from before evaluating rhs.

> > struct
> > (struct |  | apply-values expr (proc args-expr)
> #:extra-constructor-name make-apply-values
> #:prefab)
> proc : (or/c expr? seq? any/c)
> args-expr : (or/c expr? seq? any/c)

Represents (call-with-values (lambda () args-expr) proc), which is handled specially by the run-time system.

> > struct
> > (struct |  | with-immed-mark expr (key val body)
> #:extra-constructor-name make-with-immed-mark
> #:prefab)
> key : (or/c expr? seq? any/c)
> val : (or/c expr? seq? any/c)
> body : (or/c expr? seq? any/c)

Represents a (call-with-immediate-continuation-mark key (lambda (arg) body) val) expression that is handled specially by the run-time system to avoid a closure allocation. One initialized slot is pushed onto the stack after expr and val are evaluated and before body is evaluated.

After each of key and val is evaluated, the stack is restored to its depth from before evaluating key or val.

> > struct
> > (struct |  | primval expr (id)
> #:extra-constructor-name make-primval
> #:prefab)
> id : exact-nonnegative-integer?

Represents a direct reference to a variable imported from the run-time kernel.

##### 7.4.4`` Syntax Objects

> > struct
> > (struct |  | stx-obj zo (datum wrap srcloc props tamper-status)
> #:extra-constructor-name make-stx-obj
> #:prefab)
> datum : any/c
> wrap : wrap?
> srcloc : (or/c #f srcloc?)
> props : (hash/c symbol? any/c)
> tamper-status : (or/c 'clean 'armed 'tainted)

Represents a syntax object, where wrap contains lexical information, srcloc is the source location, props contains preserved properties, and tamper-status is taint information. When the datum part is itself compound, its pieces are wrapped as stx-objs, too.

The content of wrap is typically cyclic, since it includes scopes that contain bindings that refer to scopes.

> > struct
> > (struct |  | wrap zo (shifts simple-scopes multi-scopes)
> #:extra-constructor-name make-wrap
> #:prefab)
> shifts : (listof module-shift?)
> simple-scopes : (listof scope?)
> multi-scopes : (listof (list/c multi-scope? (or/c #f exact-integer?)))

Lexical information for a syntax object. The shifts field allows binding information to be relative to the enclosing module’s run-time path. The simple-scopes field records scopes that are attached to the syntax object at all phases, and multi-scopes records phase-specific scopes (which are always attached as a group) along with a phase shift for every scope within the group).

> > struct
> > (struct |  | module-shift zo | ( | from
> > |  |  |  | to
> > |  |  |  | from-inspector-desc
> > |  |  |  | to-inspector-desc)
> #:extra-constructor-name make-module-shift
> #:prefab)
> from : (or/c #f module-path-index?)
> to : (or/c #f module-path-index?)
> from-inspector-desc : (or/c #f symbol?)
> to-inspector-desc : (or/c #f symbol?)

Records a history of module path index replacements. These replacements are applied in reverse order, and a module instantiation typically adds one more shift to replace the current “self” module path index with a run-time module path. The from and to fields should be both #f or both non-#f.

The from-inspector-desc and to-inspector-desc fields similarly should be both #f or both non-#f. They record a history of code-inspector replacements.

> > struct
> > (struct |  | scope zo (name kind bindings bulk-bindings multi-owner)
> #:extra-constructor-name make-scope
> #:prefab)
> name : (or/c 'root exact-nonnegative-integer?)
> kind : symbol?
> bindings : (listof (list/c symbol? (listof scope?) binding?))
> bulk-bindings : (listof (list/c (listof scope?) all-from-module?))
> multi-owner : (or/c #f multi-scope?)

Represents a scope. When name is 'root then the scope represents the unique all-phases scope that is shared among non-module namespaces. Otherwise, name is intended to be distinct for each scope instance within a module or top-level compilation, but the eq?-identity of the scope instance ultimately determines its identity. The kind symbol similarly acts as a debugging hint in the same way as for syntax-debug-info.

The bindings list indicates some bindings that are associated with the scope. Each element of the list includes a symbolic name, a list of scopes (including the enclosing one), and the binding for the combination of name and scope set. A given symbol can appear in multiple elements of bindings, but the combination of the symbol and scope set are unique within bindings and across all scopes. The mapping of a symbol and scope set to a binding is recorded with an arbitrary member of the scope set.

The bulk-bindings field lists bindings of all exports from a given module, which is an optimization over including each export in bindings. Elements of bindings take precedence over elements of bulk-bindings, and earlier elements of bulk-bindings take precedence over later elements.

If the scope represents a scope at a particular phase for a group of phase-specific scopes, mark-owner refers to the group.

> > struct
> > (struct |  | multi-scope zo (name src-name scopes)
> #:extra-constructor-name make-multi-scope
> #:prefab)
> name : exact-nonnegative-integer?
> src-name : any/c
> scopes : (listof (list/c (or/c #f exact-integer?) scope?))

Represents a set of phase-specific scopes that are added or removed from lexical information as a group. As for scope, the name field is intended to be distinct for different groups, but the eq? identity of the multi-scope record ultimately determines its identity. The src-name field similarly acts as a debugging hint in the same way as for syntax-debug-info.

Scopes within the group are instantiated at different phases on demand. The scopes field lists all of the scopes instantiated for the group, and the phase at which it is instantiated. Each element of scopes must have a multi-owner field value that refers back to the multi-scope.

> > struct
> > (struct |  | binding zo ()
> #:extra-constructor-name make-binding
> #:prefab)

A supertype for all binding representations.

> > struct
> > (struct |  | module-binding binding (encoded)
> #:extra-constructor-name make-module-binding
> #:prefab)
> encoded : any/c

Represents a binding to a module or top-level definition. The encoded field can be unpacked using decode-module-binding, providing the symbol name for which the binding is the target (since encoded can be relative to that name).

> > struct
> > (struct |  | decoded-module-binding binding | ( | path
> > |  |  |  | name
> > |  |  |  | phase
> > |  |  |  | nominal-path
> > |  |  |  | nominal-export-name
> > |  |  |  | nominal-phase
> > |  |  |  | import-phase
> > |  |  |  | inspector-desc)
> #:extra-constructor-name make-decoded-module-binding
> #:prefab)
> path : (or/c #f module-path-index?)
> name : symbol?
> phase : exact-integer?
> nominal-path : (or/c #f module-path-index?)
> nominal-export-name : symbol?
> nominal-phase : (or/c #f exact-integer?)
> import-phase : (or/c #f exact-integer?)
> inspector-desc : (or/c #f symbol?)

Represents a binding to a module or top-level definition—like module-binding, but in normalized form:

- path: the referenced module.

- name: the referenced definition within its module.

- phase: the phase of the referenced definition within its module.

- nominal-path: the module that was explicitly imported into the binding context; this path can be different from path when a definition is re-exported.

- nominal-export-name: the name of the binding as exported from nominal-path, which can be different from name due to renaming on export.

- nominal-phase: the phase of the export from nominal-path, which can be different from phase due to re-export from a module that imports at a phase level other than 0.

- import-phase: the phase of the import of nominal-path, which shifted (if non-0) the binding phase relative to the export phase from nominal-path.

- inspector-desc: a name for an inspector (mapped to a specific inspector at run time) that determines access to the definition.

> > struct
> > (struct |  | local-binding binding (name)
> #:extra-constructor-name make-local-binding
> #:prefab)
> name : symbol?

Represents a local binding (i.e., not at the top level or module level). Such bindings rarely appear in bytecode, since quote-syntax prunes them.

> > struct
> > (struct |  | free-id=?-binding binding (base id phase)
> #:extra-constructor-name make-free-id=?-binding
> #:prefab)
> | base |  | : |  |
> (and/c binding?
> (not/c free-id=?-binding?))
> id : stx-obj?
> phase : (or/c #f exact-integer?)

Represents a binding that includes a free-identifier=? alias (to an identifier with a particular phase shift) as well as a base binding.

> > struct
> > (struct |  | all-from-module zo | ( | path
> > |  |  |  | phase
> > |  |  |  | src-phase
> > |  |  |  | inspector-desc
> > |  |  |  | exceptions
> > |  |  |  | prefix)
> #:extra-constructor-name make-all-from-module
> #:prefab)
> path : module-path-index?
> phase : (or/c exact-integer? #f)
> src-phase : (or/c exact-integer? #f)
> inspector-desc : symbol?
> exceptions : (listof symbol?)
> prefix : (or/c symbol? #f)

Describes a bulk import as an optimization over individual imports of a module’s exports:

- path: the imported module.

- phase: the phase of the import module’s exports.

- src-phase: the phase at which path was imported; src-phase combined with phase determines the phase of the bindings.

- inspector-desc: a name for an inspector (mapped to a specific inspector at run time) that determines access to the definition.

- exceptions: exports of path that are omitted from the bulk import.

- prefix: a prefix, if any, applied (after exceptions) to each of the imported names.

top ← prev up next →