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>
impl<'a> Vm<'a>
Sourcepub fn new(
runtime: &'a Runtime,
hosts: &'a HostRegistry,
program: &'a Program,
) -> Vm<'a>
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.
Sourcepub fn with_heap_words(
runtime: &'a Runtime,
hosts: &'a HostRegistry,
program: &'a Program,
heap_words: usize,
) -> Vm<'a>
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.
Sourcepub fn debugged(
runtime: &'a Runtime,
hosts: &'a HostRegistry,
program: &'a Program,
debugger: &'a dyn Debugger,
) -> Vm<'a>
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.
Sourcepub fn run_entry(
&mut self,
module: &str,
name: &str,
args: Vec<Rc<str>>,
) -> Result<Value, RuntimeError>
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.
Sourcepub fn invoke(
&mut self,
module: &str,
name: &str,
args: Vec<Value>,
) -> Result<Value, RuntimeError>
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.
Sourcepub fn run_entry_within(
&mut self,
budget: Budget,
module: &str,
name: &str,
args: Vec<Rc<str>>,
) -> Result<Value, RuntimeError>
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.
Sourcepub fn invoke_within(
&mut self,
budget: Budget,
module: &str,
name: &str,
args: Vec<Value>,
) -> Result<Value, RuntimeError>
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.
Sourcepub fn instructions(&self) -> u64
pub fn instructions(&self) -> u64
How many instructions this run has executed.
Sourcepub fn heap_words(&self) -> u64
pub fn heap_words(&self) -> u64
Words the heap region occupies, free blocks included.
Sourcepub fn allocated_words(&self) -> u64
pub fn allocated_words(&self) -> u64
Words handed out over the whole run, reuse counted each time.
Sourcepub fn collections(&self) -> u64
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.
Sourcepub fn live_words(&self) -> Option<u64>
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.
Sourcepub fn assertion_failure(&self) -> Option<(Span, &str)>
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.