pub struct Runtime { /* private fields */ }Expand description
The owner of the heap and the immortal singletons.
The entry point for runtime code: construct a Runtime, allocate values
through it, root them in a crate::RootScope, and collect when needed.
Runtime::context produces the RuntimeContext handed to generated code.
Implementations§
Source§impl Runtime
impl Runtime
Sourcepub fn set_stack_budget(&mut self, budget: StackBudget)
pub fn set_stack_budget(&mut self, budget: StackBudget)
Lower the native-stack budget every context this runtime mints will start with (ADR-105).
For a host that knows its stack is smaller than the one
STACK_BUDGET_BYTES assumes, and for tests that want to reach the
guard without recursing eight thousand times. It cannot be raised past
the default — StackBudget::new refuses, because the shadow-stack
reservation is sized from that figure.
Sourcepub fn stack_budget(&self) -> StackBudget
pub fn stack_budget(&self) -> StackBudget
The native-stack budget this runtime hands to a new context.
Sourcepub fn collect_now(&mut self)
pub fn collect_now(&mut self)
Force a mark-and-sweep collection (§12.1) rooted from everything this runtime owns — the shadow stack, the ambient input buffer, a parse failure’s partial value, the crash snapshot, and the native root store.
This is the host’s collection entry point. It takes no root set: a host that could name its own would be choosing which of the runtime’s owners to honour, and choosing wrong frees a live object.
Sourcepub fn context(&mut self) -> RuntimeContext
pub fn context(&mut self) -> RuntimeContext
A RuntimeContext view of this runtime, suitable for generated code.
pending_fault points at this runtime’s fault slot; shadow points at
this runtime’s shadow-stack header, which every generated prologue
bump-allocates from and which the collector scans; debug_frames and
debug_values point at the crash debugger’s two stacks, which the
prologue claims from and which praxis_snapshot_debug_chain reads.
parse_detail points at this runtime’s ParseDetail slot so the
parser interpreter can record the richest ParseFailed detail.
Every context this mints shares the three stacks and the native root
store, so a context taken while generated code or a runtime wrapper is
running (as Runtime::collect_now does) sees the frames and scopes
already on them.
Two contexts must never execute over these stacks concurrently. That
holds because a Praxis program is single-threaded and every host that
mints a second context (crate::Runtime::collect_now, the debugger’s
p EXPR and restart) does so only when the previous run has fully
unwound. A second context therefore starts with the full stack
budget rather than the running one’s remainder, which is correct for the
two callers that mint one while frames are live: both do so from the host,
on the host’s own stack, not from underneath the frames.
Sourcepub fn fault(&self) -> FaultKind
pub fn fault(&self) -> FaultKind
The current fault state (§10.4). FaultKind::None when no fault is set.
Sourcepub fn has_pending_fault(&self) -> bool
pub fn has_pending_fault(&self) -> bool
True iff a fault is pending.
Sourcepub fn take_fault(&mut self) -> Option<FaultKind>
pub fn take_fault(&mut self) -> Option<FaultKind>
Clear any pending fault, returning the kind that was pending (if any).
Sourcepub fn fault_message(&self) -> Option<&str>
pub fn fault_message(&self) -> Option<&str>
The message a panic/assert fault carried (§9.1), or None for a
fault kind that carries none. The host renders it beside the fault line.
Sourcepub fn parse_detail(&self) -> &ParseDetail
pub fn parse_detail(&self) -> &ParseDetail
Borrow the rich parse-failure detail slot (§7.11). The host reads this
after a FaultKind::ParseFailed to render the input/parser span, the
expected description, and the actual preview. Returns None when no
detail was recorded (e.g. a non-parser ParseFailed path).
Sourcepub fn parse_detail_mut(&mut self) -> &mut ParseDetail
pub fn parse_detail_mut(&mut self) -> &mut ParseDetail
Mutably borrow the parse-detail slot (so the host can clear it before a rerun, or the debugger can read the partial root value).
Sourcepub fn crash_snapshot(&self) -> Option<&CrashSnapshot>
pub fn crash_snapshot(&self) -> Option<&CrashSnapshot>
Borrow the crash-snapshot slot (§9.3). None when no fault snapshotted
this run (the program completed cleanly, or faulted before any debug
frame was pushed). The host reads this after a fault for the
noninteractive render / crash REPL.
Sourcepub fn take_crash_snapshot(&mut self) -> Option<CrashSnapshot>
pub fn take_crash_snapshot(&mut self) -> Option<CrashSnapshot>
Take the crash snapshot out of the runtime (the host owns it after).
Returns None when no snapshot was taken.
Sourcepub fn clear_for_rerun(&mut self)
pub fn clear_for_rerun(&mut self)
Reset the fault, crash-snapshot, and parse-detail slots so the next
main call starts from a clean slate (§9.7 restart/reload). The
heap is not collected — old allocations (and the snapshot the host
may still hold as a root set) survive until an explicit collect.
Call this before re-executing main.
Sourcepub fn shadow_stack(&self) -> &ShadowStack
pub fn shadow_stack(&self) -> &ShadowStack
The shadow stack every generated frame bump-allocates from (ADR-101).
Read-only, and the reason it is exposed at all is that “the stack is empty again” is the observable form of “every prologue was balanced by an epilogue” — an unbalanced prologue must be a test failure, not a slow leak that only shows up as a wrong root set thousands of calls later.
Sourcepub fn native_root_store(&self) -> &NativeRootStore
pub fn native_root_store(&self) -> &NativeRootStore
The native root store every crate::roots::NativeScope claims from
(ADR-114). Read-only, and exposed for Runtime::shadow_stack’s reason
— “the store is empty again” is the observable form of “every scope was
dropped” — plus one this region has and the others do not: its
capacity is the observable
form of “this program made the store grow”, which is the state a
pointer-shaped watermark would not have survived.
Sourcepub fn debug_frame_stack(&self) -> &DebugFrameStack
pub fn debug_frame_stack(&self) -> &DebugFrameStack
The crash debugger’s frame stack (§9.3, ADR-104). Read-only, and exposed
for the same reason as Runtime::shadow_stack: “the stack is empty
again” is the observable form of “every prologue was balanced”.
Sourcepub fn debug_value_stack(&self) -> &DebugValueStack
pub fn debug_value_stack(&self) -> &DebugValueStack
The crash debugger’s value stack (§9.3, ADR-104). See
Runtime::debug_frame_stack.
Sourcepub fn teardown(self) -> HeapDrained
pub fn teardown(self) -> HeapDrained
Consume the runtime, drop the heap, and return the proof that no live object can still name a JIT generation’s arena (F13, hazard H15).
This is the only constructor of [HeapDrained], and reclaiming a
generation requires one. Dropping the heap runs every finalizer
(Heap::drop), so after this call no RecordPayload or TuplePayload
survives to dereference a schema pointer.
A host that never calls this loses nothing but memory: an un-retired generation leaks its arena.
Source§impl Runtime
impl Runtime
Sourcepub fn alloc_int(&self, value: i64) -> GcRef
pub fn alloc_int(&self, value: i64) -> GcRef
Allocate an Int (§4.3), or answer the interned immortal when value is
small (crate::small_int).
The interning is here and not only in praxis_alloc_int so that the host
helper and the ABI wrapper answer the same object for the same small
value, exactly as Runtime::alloc_bool already does. Two allocators
disagreeing about whether 5 is shared would be a wart with no upside:
nothing can observe the sharing (that is small_int’s argument), so the
only thing a split would buy is two behaviours to remember.
Sourcepub fn alloc_bool(&self, value: bool) -> GcRef
pub fn alloc_bool(&self, value: bool) -> GcRef
Allocate a Bool as the corresponding immortal singleton (§4.3). Booleans
are always the immortals — there is never a fresh Bool allocation.
Sourcepub fn alloc_byte(&self, value: u8) -> GcRef
pub fn alloc_byte(&self, value: u8) -> GcRef
Allocate a Byte (§4.3).
Sourcepub fn alloc_char(&self, value: u32) -> GcRef
pub fn alloc_char(&self, value: u32) -> GcRef
Allocate a Char (§4.3), or answer the interned immortal when value is
ASCII (crate::small_char). Panics if value is not a valid scalar
value.
The validity assert stays in front of the table lookup rather than being
absorbed into it: index_of answers “is it interned”, which for a value
above the range is None and therefore says nothing at all about
validity. An out-of-range invalid code point must still panic here.
The interning is here and not only in praxis_alloc_char for
Runtime::alloc_int’s reason — the host helper and the ABI wrapper must
answer the same object for the same small value. Nothing can observe the
sharing (that is small_char’s argument), so a split would buy nothing
but two behaviours to remember.
Sourcepub fn alloc_float(&self, value: f64) -> GcRef
pub fn alloc_float(&self, value: f64) -> GcRef
Allocate a Float (§4.3, §4.12). All finite values, ±infinity, and NaN
are valid payloads — Float arithmetic never faults (IEEE-754).
Sourcepub fn alloc_unit(&self) -> GcRef
pub fn alloc_unit(&self) -> GcRef
The immortal Unit (§4.3).
Sourcepub fn alloc_text(&self, value: &str) -> GcRef
pub fn alloc_text(&self, value: &str) -> GcRef
Allocate an owned Text (§4.3, ADR-013).
Sourcepub unsafe fn alloc_text_slice(
&self,
owner: GcRef,
start: usize,
len: usize,
) -> Option<GcRef>
pub unsafe fn alloc_text_slice( &self, owner: GcRef, start: usize, len: usize, ) -> Option<GcRef>
Allocate a source-slice Text — a zero-copy view into owner’s bytes
spanning [start, start+len) (§7.10, ADR-013). The slice’s descriptor
traces owner, keeping the backing alive.
Returns None if the range is not a Text: past the owner’s end, an
overflowing length, or ends that split a multi-byte scalar. The check is
unconditional, not a debug_assert — a release build must not slice out
of range.
§Safety
owner must be a live Text GcRef.
Sourcepub fn alloc_vec(
&self,
element_descriptor: &'static TypeDescriptor,
items: Vec<GcRef>,
) -> GcRef
pub fn alloc_vec( &self, element_descriptor: &'static TypeDescriptor, items: Vec<GcRef>, ) -> GcRef
Allocate a Vec[T] from a slice of already-allocated element refs and the
element descriptor (§11.2, ADR-013).
Sourcepub fn alloc_grid(
&self,
element_descriptor: &'static TypeDescriptor,
items: Vec<GcRef>,
width: usize,
) -> GcRef
pub fn alloc_grid( &self, element_descriptor: &'static TypeDescriptor, items: Vec<GcRef>, width: usize, ) -> GcRef
Allocate a Grid[T] from a flat row-major list of cells, the element
descriptor, and the column count (§7.5). items.len() must be a
multiple of width.
Sourcepub fn alloc_record(
&self,
schema: &'static RecordSchema,
items: Vec<GcRef>,
) -> GcRef
pub fn alloc_record( &self, schema: &'static RecordSchema, items: Vec<GcRef>, ) -> GcRef
Allocate a provisional structural Record from field values and a static
schema (§7.8). items.len() must equal schema.arity().