Skip to main content

Runtime

Struct Runtime 

Source
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

Source

pub fn new() -> Self

Create a runtime with a fresh heap and the immortal singletons allocated.

Source

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.

Source

pub fn stack_budget(&self) -> StackBudget

The native-stack budget this runtime hands to a new context.

Source

pub fn heap(&self) -> &Heap

Borrow the heap.

Source

pub fn immortals(&self) -> &Immortals

The immortal singletons (§4.3).

Source

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.

Source

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.

Source

pub fn fault(&self) -> FaultKind

The current fault state (§10.4). FaultKind::None when no fault is set.

Source

pub fn has_pending_fault(&self) -> bool

True iff a fault is pending.

Source

pub fn take_fault(&mut self) -> Option<FaultKind>

Clear any pending fault, returning the kind that was pending (if any).

Source

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.

Source

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).

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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”.

Source

pub fn debug_value_stack(&self) -> &DebugValueStack

The crash debugger’s value stack (§9.3, ADR-104). See Runtime::debug_frame_stack.

Source

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

Source

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.

Source

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.

Source

pub fn alloc_byte(&self, value: u8) -> GcRef

Allocate a Byte (§4.3).

Source

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.

Source

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).

Source

pub fn alloc_unit(&self) -> GcRef

The immortal Unit (§4.3).

Source

pub fn alloc_text(&self, value: &str) -> GcRef

Allocate an owned Text (§4.3, ADR-013).

Source

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.

Source

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).

Source

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.

Source

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().

Trait Implementations§

Source§

impl Default for Runtime

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.