Skip to main content

Vm

Struct Vm 

Source
pub struct Vm<'a> { /* private fields */ }
Expand description

One run of a lowered program.

This is the type above the machine: it holds the program, the memory the run executes over, and the two things that make a run a run rather than a dispatch loop — the boundary a Value crosses, and the accounting a safepoint charges. The dispatch loop underneath knows nothing about any of them.

The two ways in are the two the language has, and they are the same two crate::interp::Interpreter offers. Vm::run_entry is how a command speaks to a program: the arguments are process arguments, which are strings. Vm::invoke is how an application does: the arguments are values the host built, held to the types the checker resolved before the first instruction runs. Everything below the two is one path.

Implementations§

Source§

impl<'a> Vm<'a>

Source

pub fn new( runtime: &'a Runtime, hosts: &'a HostRegistry, program: &'a Program, ) -> Vm<'a>

A run of program, over runtime’s checked program and hosts.

program is encoded and verified here, once, into the fixed-width form ADR 0041 decides and issue #245’s Phase 5 made the only one a run executes. There is no second representation to choose and no flag that selects one: Inst is what the lowering produced and what a listing and the debugger show, and what runs is sixteen bytes per instruction whose operands were checked before the first of them ran.

This stays infallible, and what that costs is stated where it is paid. A program with no encoding — one whose frame is wider than a sixteen-bit slot names, which cove_ir::lower already refuses with a diagnostic — is refused by Vm::run_entry and Vm::invoke before a frame is pushed, rather than by this constructor. The alternative was a Result at every call site for a failure the compiler in front of it has already made impossible.

The heap budget is this module’s DEFAULT_HEAP_WORDS. Vm::with_heap_words is the constructor for a caller that needs a different one.

Source

pub fn with_heap_words( runtime: &'a Runtime, hosts: &'a HostRegistry, program: &'a Program, heap_words: usize, ) -> Vm<'a>

The same run, over a heap that may grow only to heap_words words rather than DEFAULT_HEAP_WORDS.

This is deliberately not a Limits field. ADR 0011’s amendment retracted Limits::max_memory because a number that bounds only what one collector’s table can see is not a memory ceiling; it is that instrument’s readout wearing a ceiling’s name. Nothing about the linear-memory backend changes that argument for an embedder: its heap is a fuller account of a run’s Cove-owned values than the old per-task heap ever was, per ADR 0034, but a Host’s own allocations, open resources and each task’s stack region still sit outside it, so naming heap_words beside fuel and max_host_calls would still promise a bound this number cannot back.

What this constructor is for is what mem::STACK_WORDS already is — an implementation choice a test may need to name to provoke the behaviour it bounds, not a knob an embedder is invited to reach for. Prefer Vm::new unless the caller is deliberately forcing a small heap so a collection has something to be tested against.

Source

pub fn debugged( runtime: &'a Runtime, hosts: &'a HostRegistry, program: &'a Program, debugger: &'a dyn Debugger, ) -> Vm<'a>

The same run, watched by debugger.

A second constructor rather than a parameter on Vm::new, for the reason the heap budget is not one either: no existing caller has a debugger to name, and a parameter every caller passes None to is a question every caller is asked and none of them answers.

What it costs the run is stated where it is paid, in the debugger’s own module: the machine asks before every instruction for as long as the debugger is installed, so a debugged run is slower by whatever the debugger does per instruction. A run built with Vm::new is unchanged — the loop’s comparison is the same one it was, against the next safepoint.

Source

pub fn run_entry( &mut self, module: &str, name: &str, args: Vec<Rc<str>>, ) -> Result<Value, RuntimeError>

Runs module.name with the process arguments args.

An entry takes either no parameters or one Array<String>, and that rule is the language’s rather than a backend’s — the oracle refuses the third shape in these words, at this span.

Source

pub fn invoke( &mut self, module: &str, name: &str, args: Vec<Value>, ) -> Result<Value, RuntimeError>

Calls module.name with values the host built.

The arguments are held to what the checker resolved about the declaration — the shape it has to be callable at all, the count, and each value’s type followed as deeply as the type goes — before anything runs. That check is the crate’s own invoke, shared with the oracle so that a host that gets it wrong reads one answer and not one per backend.

One refusal belongs to this backend rather than to the language, and it is about the lowering rather than about the program. cove_ir::lower_entry lowers what one entry can reach and nothing else, so a run built for one entry cannot invoke a function no path from that entry leads to. Saying the package does not declare it would be false and would send an embedder to the wrong file, so this says which of the two is missing and what to lower instead.

Source

pub fn run_entry_within( &mut self, budget: Budget, module: &str, name: &str, args: Vec<Rc<str>>, ) -> Result<Value, RuntimeError>

Vm::run_entry, bounded by budget and by nothing else.

The command-shaped way in, bounded the way Vm::invoke_within bounds the application-shaped one. Issue #152 is why both exist: an application that runs somebody else’s Cove wants the request bounded, not the session, and a session is built once and invoked many times.

Source

pub fn invoke_within( &mut self, budget: Budget, module: &str, name: &str, args: Vec<Value>, ) -> Result<Value, RuntimeError>

Vm::invoke, bounded by budget and by nothing else.

The check runs before the budget is installed, so a call refused for a wrong argument spends none of the budget it was handed and leaves whatever bounded this backend where it was.

Source

pub fn instructions(&self) -> u64

How many instructions this run has executed.

Source

pub fn heap_words(&self) -> u64

Words the heap region occupies, free blocks included.

Source

pub fn allocated_words(&self) -> u64

Words handed out over the whole run, reuse counted each time.

Source

pub fn collections(&self) -> u64

How many collections this run’s heap has done.

Vm::live_words is None exactly when this is 0: a heap that has never collected has nothing that measured what is live.

Source

pub fn live_words(&self) -> Option<u64>

Words the most recent collection found alive, or None if the heap has never collected.

Issue #248 is why this exists as its own accessor rather than only inside the trace’s heap_summary event: Runtime::heap_stats is filled in only by the tree-walking backend (see its doc comment), so a Vm embedder asking “does this run still hold what an early invocation allocated” has nothing else public to read. heap_words and allocated_words answer capacity and a monotonic total; this is the one that answers what is live right now, as of the last sweep.

Source

pub fn assertion_failure(&self) -> Option<(Span, &str)>

Where the most recent failed assertion was written, together with the message it produced, or None when no assertion has failed.

The same answer crate::interp::Interpreter::assertion_failure gives, and it is here for the same caller: a test runner points at the assertion the way every other error points at source. An assertion that failed and was then handled inside the program is still recorded, which is why the message is part of the answer — a caller reports at this span only when the failure it is holding is this one.

Auto Trait Implementations§

§

impl<'a> !RefUnwindSafe for Vm<'a>

§

impl<'a> !UnwindSafe for Vm<'a>

§

impl<'a> Freeze for Vm<'a>

§

impl<'a> Send for Vm<'a>

§

impl<'a> Sync for Vm<'a>

§

impl<'a> Unpin for Vm<'a>

§

impl<'a> UnsafeUnpin for Vm<'a>

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.