use crate::error::VmResult;
use crate::net::mpc_engine::MpcEngine;
use crate::output::{StdoutOutputSink, VmOutputResult, VmOutputSink};
use crate::program::{CallTarget, Program};
use crate::reveal_destination::FrameDepth;
use crate::runtime_hooks::{HookManager, InstructionCursor};
use crate::runtime_instruction::RuntimeFunction;
use crate::storage::LocalStorage;
use mpc_runtime::MpcRuntimeState;
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::sync::Arc;
#[cfg(test)]
use stoffel_vm_types::activations::ActivationRecord;
use stoffel_vm_types::activations::{ActivationStack, InstructionPointer};
use stoffel_vm_types::core_types::{ForeignObjectStorage, ObjectStore, TableMemory, Value};
use stoffel_vm_types::registers::{RegisterIndex, RegisterLayout};
mod calls;
mod effect;
mod execution;
mod foreign_objects;
mod frame;
mod hooks;
mod instructions;
mod local_storage;
mod mpc;
mod mpc_operation;
mod mpc_runtime;
mod program_registry;
mod register_access;
mod share_objects;
mod table_memory;
mod upvalues;
pub(crate) use effect::{
CompletedVmEffect, VmEffect, VmEffectKind, VmEffectSummary, VmExecutionBudget, VmRunSlice,
};
pub(crate) struct VMStateConfig {
table_memory: Box<dyn TableMemory>,
register_layout: RegisterLayout,
mpc_engine: Option<Arc<dyn MpcEngine>>,
output_sink: Arc<dyn VmOutputSink>,
local_storage: Option<Arc<Mutex<Box<dyn LocalStorage>>>>,
local_storage_party_id: Option<usize>,
}
impl Default for VMStateConfig {
fn default() -> Self {
Self {
table_memory: Box::new(ObjectStore::new()),
register_layout: RegisterLayout::default(),
mpc_engine: None,
output_sink: Arc::new(StdoutOutputSink),
local_storage: None,
local_storage_party_id: None,
}
}
}
impl VMStateConfig {
pub(crate) fn with_table_memory(mut self, table_memory: Box<dyn TableMemory>) -> Self {
self.table_memory = table_memory;
self
}
pub(crate) fn with_register_layout(mut self, register_layout: RegisterLayout) -> Self {
self.register_layout = register_layout;
self
}
pub(crate) fn with_mpc_engine(mut self, mpc_engine: Arc<dyn MpcEngine>) -> Self {
self.mpc_engine = Some(mpc_engine);
self
}
pub(crate) fn with_output_sink(mut self, output_sink: Arc<dyn VmOutputSink>) -> Self {
self.output_sink = output_sink;
self
}
pub(crate) fn with_shared_local_storage(
mut self,
local_storage: Arc<Mutex<Box<dyn LocalStorage>>>,
) -> Self {
self.local_storage = Some(local_storage);
self
}
pub(crate) fn with_local_storage_party_id(mut self, party_id: usize) -> Self {
self.local_storage_party_id = Some(party_id);
self
}
}
pub(crate) struct VMState {
program: Program,
last_call_target: Option<(Arc<str>, CallTarget)>,
call_stack: ActivationStack,
frame_runtime_functions: SmallVec<[Option<Arc<RuntimeFunction>>; 8]>,
current_instruction: Option<InstructionCursor>,
table_memory: Box<dyn TableMemory>,
foreign_objects: ForeignObjectStorage,
hook_manager: HookManager,
mpc_runtime: MpcRuntimeState,
register_layout: RegisterLayout,
output_sink: Arc<dyn VmOutputSink>,
local_storage: Option<Arc<Mutex<Box<dyn LocalStorage>>>>,
local_storage_party_id: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct CallStackCheckpoint {
frame_depth: FrameDepth,
}
impl CallStackCheckpoint {
pub(crate) const fn new(depth: usize) -> Self {
Self {
frame_depth: FrameDepth::new(depth),
}
}
const fn depth(self) -> usize {
self.frame_depth.depth()
}
const fn frame_depth_floor(self) -> FrameDepth {
self.frame_depth
}
const fn has_active_frame(self, current_depth: usize) -> bool {
current_depth > self.depth()
}
const fn is_current_depth(self, current_depth: usize) -> bool {
current_depth == self.depth()
}
fn is_returning_from_entry_frame(self, current_depth: usize) -> bool {
current_depth.checked_sub(self.depth()) == Some(1)
}
}
impl Default for VMState {
fn default() -> Self {
Self::new()
}
}
impl VMState {
#[inline]
pub(crate) fn new() -> Self {
Self::from_config(VMStateConfig::default())
}
pub(crate) fn from_config(config: VMStateConfig) -> Self {
let mut mpc_runtime = MpcRuntimeState::new();
if let Some(engine) = config.mpc_engine {
mpc_runtime.set_engine(engine);
}
VMState {
program: Program::new(),
last_call_target: None,
call_stack: ActivationStack::new(),
frame_runtime_functions: SmallVec::new(),
current_instruction: None,
table_memory: config.table_memory,
foreign_objects: ForeignObjectStorage::new(),
hook_manager: HookManager::new(),
mpc_runtime,
register_layout: config.register_layout,
output_sink: config.output_sink,
local_storage: config.local_storage,
local_storage_party_id: config.local_storage_party_id,
}
}
pub(crate) fn try_clone_with_independent_runtime(&self) -> VmResult<Self> {
let mut config = VMStateConfig::default()
.with_table_memory(self.table_memory.try_clone_empty()?)
.with_register_layout(self.register_layout)
.with_output_sink(Arc::clone(&self.output_sink));
if let Some(local_storage) = &self.local_storage {
config = config.with_shared_local_storage(Arc::clone(local_storage));
}
if let Some(party_id) = self.local_storage_party_id {
config = config.with_local_storage_party_id(party_id);
}
let mut cloned = Self::from_config(config);
cloned.program = self.program.clone();
cloned.mpc_runtime = self.mpc_runtime.clone_independent();
Ok(cloned)
}
#[cfg(test)]
pub(crate) fn push_activation_record(&mut self, record: ActivationRecord) {
self.push_activation_frame(record, None);
}
#[inline]
pub(crate) fn call_stack_depth(&self) -> usize {
self.call_stack.len()
}
pub(crate) fn unwind_call_stack_to(&mut self, checkpoint: CallStackCheckpoint) {
self.mpc_runtime
.clear_reveals_at_or_above(checkpoint.frame_depth_floor());
self.truncate_activation_frames(checkpoint.depth());
self.sync_current_instruction_to_current_frame();
}
#[cfg(test)]
pub(crate) fn set_register_layout(&mut self, layout: RegisterLayout) {
self.register_layout = layout;
}
#[cfg(test)]
pub(crate) fn register_layout(&self) -> RegisterLayout {
self.register_layout
}
pub(crate) fn prepare_register_arguments(
&self,
args: &[Value],
) -> VmResult<SmallVec<[Value; 8]>> {
self.prepare_register_arguments_for_layout(self.register_layout, args)
}
pub(crate) fn set_output_sink(&mut self, output_sink: Arc<dyn VmOutputSink>) {
self.output_sink = output_sink;
}
pub(crate) fn output_sink(&self) -> Arc<dyn VmOutputSink> {
Arc::clone(&self.output_sink)
}
pub(crate) fn write_output_line(&self, line: &str) -> VmOutputResult<()> {
self.output_sink.write_line(line)
}
pub(super) fn set_current_instruction(
&mut self,
function_name: impl Into<Arc<str>>,
instruction_pointer: InstructionPointer,
) {
self.current_instruction = Some(InstructionCursor::new(function_name, instruction_pointer));
}
pub(super) fn clear_current_instruction(&mut self) {
self.current_instruction = None;
}
pub(super) fn sync_current_instruction_to_current_frame(&mut self) {
self.current_instruction = self.call_stack.current().and_then(|record| {
record
.instruction_pointer()
.previous()
.map(|instruction_pointer| {
InstructionCursor::new(record.function_name_arc(), instruction_pointer)
})
});
}
pub(crate) fn prepare_register_arguments_for_layout(
&self,
layout: RegisterLayout,
args: &[Value],
) -> VmResult<SmallVec<[Value; 8]>> {
let mut prepared = SmallVec::with_capacity(args.len());
for (register, value) in args.iter().enumerate() {
prepared.push(self.prepare_register_write_value_for_layout(
layout,
RegisterIndex::new(register),
value.clone(),
)?);
}
Ok(prepared)
}
#[inline]
#[cfg(test)]
pub(crate) fn current_activation_record(&self) -> Option<&ActivationRecord> {
self.call_stack.current()
}
}
#[cfg(test)]
mod tests;