How it works
Reflection.Emit needs a JIT, so ilrepl separates the Native AOT terminal frontend from a framework-dependent execution host.
The frontend owns the editor, transcript, and acknowledged source workspace. An embedded opcode and command catalog makes the prompt editable before the host connects. Operand completion and analysis read immutable metadata snapshots without waiting for a running cell. Caret movement reuses analysis of the same source.
JSON-RPC travels directly between the frontend and host over a private Unix domain socket on Windows, Linux, and macOS. User console output streams separately from replies, so a long-running cell can print before it returns. Replies carry document and runtime identities to prevent stale results from replacing current ones.
Cells run on one persistent thread with an explicitly requested 8 MiB stack. Thread-static values, thread-local values, culture, and the thread name persist between cells. The operating system and runtime still determine usable stack space; unbounded recursion can overflow it.
On Unix, a separate lifetime supervisor launches and tracks execution process groups. It does not relay RPC or console output. If the supervisor exits, the frontend can start a replacement and adopt the existing groups without replacing a healthy host. Windows uses a job owned by the frontend. Losing the frontend stops its execution processes even when user code never returns.
Before entering user code, the host waits for the frontend to acknowledge the source checkpoint. Recovery reconstructs definitions and edits without replaying cells. See Restart and recovery.
The cell
Section titled “The cell”Every line is validated against a replayable model of the cell: declared locals and arguments,
the instructions, the labels, the open exception blocks, and the simulated stack. When the cell
runs, the accepted lines are replayed against a fresh MethodBuilder, so generic parameters bind
to the method that is emitted and the same cell can compile more than once. Compiled cells are
collectible AssemblyBuilder types with one static Run method returning object; the epilogue
boxes a value type left on the stack, and vararg cells get a standard-convention wrapper, because
reflection cannot invoke a vararg method directly.
The editor analyzes the whole unsent document through a separate request. A fixed-point worklist merges incoming stacks and retains producer locations; runtime and symbolic operands share the transfer rules. Requests yield during large analyses and release their metadata leases when cancelled. Cached presentation facts let caret movement reuse the graph without retaining types. Replies carry document and session identities, so stale diagnostics cannot replace current ones.
Definitions
Section titled “Definitions”Methods defined with .method and types defined with .class are not recreated on each cell run. Each is kept as
a replayable model while its block is open and, when the block closes, written once with
Mono.Cecil into a small assembly of its own and loaded into a collectible load context. A type is
therefore one runtime type for the whole session, with its statics and its instances, and a
saved assembly is produced by the same writer, so the file carries the metadata the session ran.
A session method’s callers bind to a stable trampoline; redefining the method with the same signature compiles a new body and points the trampoline at it, so existing callers, delegates included, see the new body. Redefining a type, or a method with a new signature, creates new identities and rebuilds the affected dependency group against the new definitions, including cycles; one refusal restores the session. Instances and delegates a cell still holds keep the previous definitions alive until they are released.
Cells and definitions skip the runtime’s access checks for session assemblies, so the REPL
enforces ECMA-335 accessibility as you type. The names are unique per definition, so a
load context never binds the wrong one, and .reset unloads what the session held.
Method edits
Section titled “Method edits”.edit keeps the original method and copies its declaring type and required helpers. Changes pass
through the same parser, stack checks, and writer as other methods. A successful revision updates
dependent callers; a failed one leaves the previous version in place.
.compare runs each version in a fresh process. Both start with the
same inputs. The report compares their arguments, results, exceptions, and console output. Previously
run cells are not replayed, and ending a comparison leaves the session available.
Native inspection
Section titled “Native inspection”Native inspection runs in a fresh CoreCLR worker with the terminal host’s runtime and architecture. It preserves captured method images and re-emits retained cells without replaying earlier execution. The report combines native listings with observed runtime compilation events. See Native code for compilation settings, execution boundaries, and comparison behavior.
Projects
Section titled “Projects”| Project | Purpose |
|---|---|
IlRepl.Protocol |
The transcript model, completion requests and pages, the caret classifier, and the JSON-RPC contract. |
IlRepl.Engine |
Parsing, resolution, simulation, compilation, the session, and the REPL core. |
IlRepl.Host |
Runs user IL and serves the engine over a direct socket connection. |
IlRepl.Tui |
The Hex1b widgets: transcript, prompt with completion palette, status bar. |
IlRepl |
The tool: command line, host client, batch mode. |