mod additional;
mod arithmetic;
mod async_ops;
mod builtins;
mod call_convention;
mod comparison;
mod control_flow;
pub(crate) mod dispatch;
mod exceptions;
pub(crate) mod ic_fast_paths;
mod jit_ops;
mod logical;
mod loops;
pub(crate) mod objects;
mod osr;
mod resume;
mod snapshot;
mod stack_ops;
pub mod state_builtins;
pub mod time_travel;
mod trait_object_ops;
pub mod v2_handlers;
mod variables;
pub(crate) mod vm_state_snapshot;
mod window_join;
pub mod debugger_integration;
pub mod gc_integration;
pub mod module_registry;
pub mod printing;
pub mod task_scheduler;
pub mod typed_object_ops;
pub mod utils;
#[cfg(test)]
mod tests;
pub use async_ops::{AsyncExecutionResult, SuspensionInfo, WaitType};
pub use control_flow::foreign_marshal;
pub use control_flow::native_abi;
pub use task_scheduler::{TaskScheduler, TaskStatus};
pub const SNAPSHOT_FUTURE_ID: u64 = u64::MAX;
#[derive(Debug, Clone)]
pub enum PermissionError {
InsufficientPermissions {
required: shape_abi_v1::PermissionSet,
granted: shape_abi_v1::PermissionSet,
missing: shape_abi_v1::PermissionSet,
},
LinkError(String),
}
impl std::fmt::Display for PermissionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PermissionError::InsufficientPermissions { missing, .. } => {
let names: Vec<&str> = missing.iter().map(|p| p.name()).collect();
write!(
f,
"program requires permissions not granted: {}",
names.join(", ")
)
}
PermissionError::LinkError(msg) => write!(f, "link error: {msg}"),
}
}
}
impl std::error::Error for PermissionError {}
#[derive(Debug, Clone)]
pub enum ExecutionResult {
Completed(shape_value::KindedSlot),
Suspended {
future_id: u64,
resume_ip: usize,
},
}
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::AtomicU8;
use crate::{
bytecode::{
BuiltinFunction, BytecodeProgram, FunctionBlob, FunctionHash, Instruction, Operand,
},
debugger::VMDebugger,
memory::{GCConfig, GarbageCollector},
tier::TierManager,
};
use shape_ast::data::Timeframe;
use crate::constants::{DEFAULT_GC_TRIGGER_THRESHOLD, MAX_CALL_STACK_DEPTH, MAX_STACK_SIZE};
use shape_value::{KindedSlot, NativeKind};
#[derive(Debug, Clone)]
pub struct VMConfig {
pub max_stack_size: usize,
pub max_call_depth: usize,
pub debug_mode: bool,
pub trace_execution: bool,
pub gc_config: GCConfig,
pub auto_gc: bool,
pub gc_trigger_threshold: usize,
pub metrics_enabled: bool,
pub use_tracing_gc: bool,
}
impl Default for VMConfig {
fn default() -> Self {
Self {
max_stack_size: MAX_STACK_SIZE,
max_call_depth: MAX_CALL_STACK_DEPTH,
debug_mode: false,
trace_execution: false,
gc_config: GCConfig::default(),
auto_gc: true,
gc_trigger_threshold: DEFAULT_GC_TRIGGER_THRESHOLD,
metrics_enabled: false,
use_tracing_gc: false,
}
}
}
#[derive(Debug)]
pub struct CallFrame {
pub return_ip: usize,
pub base_pointer: usize,
pub locals_count: usize,
pub function_id: Option<u16>,
pub upvalues: Option<Vec<u64>>,
pub blob_hash: Option<FunctionHash>,
pub closure_heap_bits: Option<u64>,
pub closure_heap_kind: Option<shape_value::NativeKind>,
}
#[cfg(feature = "jit")]
pub type JitFnPtr = unsafe extern "C" fn(*mut u8, *const u8) -> u64;
#[derive(Clone)]
pub(crate) enum ForeignFunctionHandle {
Runtime {
runtime: std::sync::Arc<shape_runtime::plugins::language_runtime::PluginLanguageRuntime>,
compiled: shape_runtime::plugins::language_runtime::CompiledForeignFunction,
},
Native(std::sync::Arc<control_flow::native_abi::NativeLinkedFunction>),
}
pub struct VirtualMachine {
config: VMConfig,
pub(crate) program: BytecodeProgram,
ip: usize,
pub(crate) stack: Vec<u64>,
pub(crate) kinds: Vec<shape_value::NativeKind>,
pub(crate) sp: usize,
pub(crate) module_bindings: Vec<u64>,
pub(crate) module_binding_kinds: Vec<NativeKind>,
pub(crate) shared_module_bindings: std::collections::HashSet<usize>,
call_stack: Vec<CallFrame>,
loop_stack: Vec<LoopContext>,
timeframe_stack: Vec<Option<Timeframe>>,
debugger: Option<VMDebugger>,
gc: GarbageCollector,
instruction_count: usize,
exception_handlers: Vec<ExceptionHandler>,
pub(crate) builtin_schemas: shape_runtime::type_schema::BuiltinSchemaIds,
last_error_line: Option<u32>,
last_error_file: Option<String>,
last_uncaught_exception: Option<KindedSlot>,
module_init_done: bool,
output_buffer: Option<Vec<String>>,
module_registry: shape_runtime::module_exports::ModuleExportRegistry,
module_fn_table: Vec<shape_runtime::module_exports::ModuleFnEntry>,
pub(crate) function_name_index: HashMap<String, u16>,
extension_methods: HashMap<String, HashMap<String, shape_runtime::module_exports::ModuleFn>>,
merged_schema_cache: HashMap<(u32, u32), u32>,
interrupt: Arc<AtomicU8>,
future_id_counter: u64,
async_scope_stack: Vec<Vec<u64>>,
pub(crate) task_scheduler: task_scheduler::TaskScheduler,
pub(crate) foreign_fn_handles: Vec<Option<ForeignFunctionHandle>>,
function_hashes: Vec<Option<FunctionHash>>,
function_hash_raw: Vec<Option<[u8; 32]>>,
function_id_by_hash: HashMap<FunctionHash, u16>,
function_entry_points: Vec<usize>,
program_entry_ip: usize,
pub resource_usage: Option<crate::resource_limits::ResourceUsage>,
pub(crate) time_travel: Option<time_travel::TimeTravel>,
#[cfg(feature = "gc")]
gc_heap: Option<shape_gc::GcHeap>,
#[cfg(feature = "jit")]
jit_compiled: bool,
#[cfg(feature = "jit")]
jit_dispatch_table: std::collections::HashMap<u16, JitFnPtr>,
tier_manager: Option<TierManager>,
pub(crate) pending_resume: Option<KindedSlot>,
pub(crate) pending_frame_resume: Option<FrameResumeData>,
pub metrics: Option<crate::metrics::VmMetrics>,
feedback_vectors: Vec<Option<crate::feedback::FeedbackVector>>,
megamorphic_cache: crate::megamorphic_cache::MegamorphicCache,
pub(crate) shape_table: std::sync::Arc<shape_value::ShapeTableHandle>,
}
pub(crate) struct FrameResumeData {
pub ip_offset: usize,
pub locals: Vec<KindedSlot>,
}
#[derive(Debug, Clone)]
struct ExceptionHandler {
catch_ip: usize,
stack_size: usize,
call_depth: usize,
}
#[derive(Debug)]
struct LoopContext {
start: usize,
end: usize,
}
#[derive(Debug)]
pub struct DebugVMState {
pub ip: usize,
pub call_stack_depth: usize,
}
pub(crate) mod vm_impl;
impl Drop for VirtualMachine {
fn drop(&mut self) {
use shape_value::v2::closure_layout::SharedCell;
for &idx in &self.shared_module_bindings {
if idx >= self.module_bindings.len() {
continue;
}
let bits = self.module_bindings[idx];
self.module_bindings[idx] = 0u64;
if idx < self.module_binding_kinds.len() {
self.module_binding_kinds[idx] = NativeKind::Bool;
}
let cell_ptr = bits as *const SharedCell;
if cell_ptr.is_null() {
continue;
}
unsafe {
drop(std::sync::Arc::from_raw(cell_ptr));
}
}
self.shared_module_bindings.clear();
let live = self.sp.min(self.stack.len()).min(self.kinds.len());
for i in 0..live {
let bits = self.stack[i];
let kind = self.kinds[i];
vm_impl::stack::drop_with_kind(bits, kind);
self.stack[i] = Self::NONE_BITS;
self.kinds[i] = NativeKind::Bool;
}
debug_assert_eq!(
self.module_bindings.len(),
self.module_binding_kinds.len(),
"ADR-006 §2.7.8 / Q10 lockstep invariant violated at \
VirtualMachine::Drop: module_bindings.len() ({}) != \
module_binding_kinds.len() ({}). A push/resize site in \
cluster-B-round-2 territory (executor/variables/mod.rs) \
grew the bits vec without growing the kinds vec.",
self.module_bindings.len(),
self.module_binding_kinds.len(),
);
let bound = self
.module_bindings
.len()
.min(self.module_binding_kinds.len());
for i in 0..bound {
let bits = self.module_bindings[i];
let kind = self.module_binding_kinds[i];
vm_impl::stack::drop_with_kind(bits, kind);
self.module_bindings[i] = Self::NONE_BITS;
self.module_binding_kinds[i] = NativeKind::Bool;
}
for slot in self.module_bindings[bound..].iter_mut() {
*slot = Self::NONE_BITS;
}
}
}
impl VirtualMachine {
#[inline]
pub(crate) fn module_binding_pad_to_kinded(&mut self, index: usize) {
while self.module_bindings.len() <= index {
self.module_bindings.push(Self::NONE_BITS);
self.module_binding_kinds.push(NativeKind::Bool);
}
debug_assert_eq!(
self.module_bindings.len(),
self.module_binding_kinds.len(),
"ADR-006 §2.7.8 / Q10 lockstep invariant",
);
}
#[inline]
pub(crate) fn module_binding_write_kinded(
&mut self,
index: usize,
bits: u64,
kind: NativeKind,
) {
self.module_binding_pad_to_kinded(index);
let old_bits = self.module_bindings[index];
let old_kind = self.module_binding_kinds[index];
vm_impl::stack::drop_with_kind(old_bits, old_kind);
self.module_bindings[index] = bits;
self.module_binding_kinds[index] = kind;
}
#[inline]
pub(crate) fn module_binding_read_kinded_raw(&self, index: usize) -> (u64, NativeKind) {
if index >= self.module_bindings.len() {
return (0u64, NativeKind::Bool);
}
debug_assert_eq!(
self.module_bindings.len(),
self.module_binding_kinds.len(),
"ADR-006 §2.7.8 / Q10 lockstep invariant violated at \
module_binding_read_kinded_raw",
);
let kind = self
.module_binding_kinds
.get(index)
.copied()
.unwrap_or(NativeKind::Bool);
(self.module_bindings[index], kind)
}
#[inline]
pub(crate) fn module_binding_read_owned_kinded(&self, index: usize) -> KindedSlot {
let (bits, kind) = self.module_binding_read_kinded_raw(index);
vm_impl::stack::clone_with_kind(bits, kind);
KindedSlot::new(shape_value::ValueSlot::from_raw(bits), kind)
}
#[inline]
pub(crate) fn module_binding_take_kinded(&mut self, index: usize) -> (u64, NativeKind) {
if index >= self.module_bindings.len() {
return (0u64, NativeKind::Bool);
}
let bits = self.module_bindings[index];
let kind = self
.module_binding_kinds
.get(index)
.copied()
.unwrap_or(NativeKind::Bool);
self.module_bindings[index] = Self::NONE_BITS;
if index < self.module_binding_kinds.len() {
self.module_binding_kinds[index] = NativeKind::Bool;
}
(bits, kind)
}
#[inline]
pub(crate) fn module_bindings_len(&self) -> usize {
debug_assert_eq!(
self.module_bindings.len(),
self.module_binding_kinds.len(),
"ADR-006 §2.7.8 / Q10 lockstep invariant",
);
self.module_bindings.len()
}
}
pub fn set_transport_provider(
provider: std::sync::Arc<dyn builtins::transport_provider::WireTransportProvider>,
) {
builtins::transport_provider::set_transport_provider(provider);
}
pub fn reset_transport_provider() {
builtins::transport_provider::reset_transport_provider();
}
#[cfg(feature = "quic")]
pub fn configure_quic_transport(
server_name: String,
root_certs_der: Vec<Vec<u8>>,
connect_timeout: Option<std::time::Duration>,
) {
builtins::transport_provider::configure_quic_transport(
server_name,
root_certs_der,
connect_timeout,
);
}
#[cfg(feature = "quic")]
pub fn clear_quic_transport_config() {
builtins::transport_provider::clear_quic_transport_config();
}
pub(crate) fn create_transport_module_exports() -> shape_runtime::module_exports::ModuleExports {
builtins::transport_builtins::create_transport_module()
}
pub(crate) fn create_remote_module_exports() -> shape_runtime::module_exports::ModuleExports {
builtins::remote_builtins::create_remote_module()
}
fn remap_operand(operand: &mut Option<Operand>, const_offset: usize, string_offset: usize) {
let Some(op) = operand.as_mut() else {
return;
};
match op {
Operand::Const(idx) => {
*idx = (*idx as usize + const_offset) as u16;
}
Operand::Property(idx) => {
*idx = (*idx as usize + string_offset) as u16;
}
Operand::Name(sid) => {
sid.0 = (sid.0 as usize + string_offset) as u32;
}
Operand::TypedMethodCall { string_id, .. } => {
*string_id = (*string_id as usize + string_offset) as u16;
}
_ => {}
}
}
#[cfg(test)]
mod v2_stack_tests;