use crate::{
frame::Frame, memory::MemoryManager, stack::Stack, CapabilitySet, Instruction, RuntimeError,
};
use glyph_intrinsics::IntrinsicRegistry;
use glyph_types::Value;
use std::collections::HashMap;
use std::sync::Arc;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum VMError {
#[error("Runtime error: {0}")]
Runtime(#[from] RuntimeError),
#[error("Invalid bytecode")]
InvalidBytecode,
#[error("Execution limit exceeded")]
ExecutionLimitExceeded,
}
#[derive(Debug, Clone)]
pub struct VMConfig {
pub max_stack_size: usize,
pub max_call_depth: usize,
pub max_execution_steps: usize,
pub capabilities: CapabilitySet,
}
impl Default for VMConfig {
fn default() -> Self {
Self {
max_stack_size: 10_000,
max_call_depth: 1_000,
max_execution_steps: 1_000_000,
capabilities: CapabilitySet::new(),
}
}
}
pub struct VM {
config: VMConfig,
stack: Stack,
frames: Vec<Frame>,
globals: HashMap<String, Value>,
ip: usize,
current_function: usize,
steps: usize,
memory: Arc<MemoryManager>,
bytecode: Vec<Vec<Instruction>>,
telemetry: Vec<(String, Value)>,
intrinsics: Arc<IntrinsicRegistry>,
}
impl VM {
pub fn new(config: VMConfig) -> Self {
let memory = Arc::new(MemoryManager::new(
config
.capabilities
.iter()
.find_map(|cap| match cap {
crate::Capability::MemoryLimited(limit) => Some(*limit),
crate::Capability::MemoryUnlimited => Some(usize::MAX),
_ => None,
})
.unwrap_or(10 * 1024 * 1024), ));
Self {
stack: Stack::new(config.max_stack_size),
frames: Vec::new(),
globals: HashMap::new(),
ip: 0,
current_function: 0,
steps: 0,
memory,
bytecode: Vec::new(),
telemetry: Vec::new(),
config,
intrinsics: Arc::new(IntrinsicRegistry::new()),
}
}
pub fn load_bytecode(&mut self, bytecode: Vec<Vec<Instruction>>) {
self.bytecode = bytecode;
}
pub fn execute(&mut self) -> Result<Value, VMError> {
self.current_function = 0;
self.ip = 0;
self.steps = 0;
if self.frames.is_empty() {
self.frames.push(Frame::new(0, 0));
}
loop {
if self.steps >= self.config.max_execution_steps {
return Err(VMError::ExecutionLimitExceeded);
}
self.steps += 1;
let instruction = self.fetch_instruction()?;
eprintln!("[VM] Executing instruction: {instruction:?}");
match self.execute_instruction(instruction)? {
ExecutionResult::Continue => {}
ExecutionResult::Halt(value) => {
eprintln!("[VM] Halting with value: {value:?}");
return Ok(value);
}
}
}
}
fn fetch_instruction(&mut self) -> Result<Instruction, VMError> {
let function = self
.bytecode
.get(self.current_function)
.ok_or(VMError::Runtime(RuntimeError::FunctionNotFound(
self.current_function,
)))?;
let instruction = function
.get(self.ip)
.ok_or(VMError::InvalidBytecode)?
.clone();
self.ip += 1;
Ok(instruction)
}
fn execute_instruction(
&mut self,
instruction: Instruction,
) -> Result<ExecutionResult, VMError> {
use Instruction::*;
match instruction {
Push(value) => self.push(value)?,
Pop => {
self.pop()?;
}
Dup => self
.stack
.dup()
.map_err(|_| VMError::Runtime(RuntimeError::StackUnderflow))?,
Swap => self
.stack
.swap()
.map_err(|_| VMError::Runtime(RuntimeError::StackUnderflow))?,
Rot3 => self.rotate_three()?,
Add => self.binary_op(|a, b| match (a, b) {
(Value::Int(x), Value::Int(y)) => Ok(Value::Int(x + y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)),
(Value::Str(x), Value::Str(y)) => Ok(Value::Str(x + &y)),
_ => Err(RuntimeError::TypeError(
"Invalid types for addition".to_string(),
)),
})?,
Sub => self.binary_op(|a, b| match (a, b) {
(Value::Int(x), Value::Int(y)) => Ok(Value::Int(x - y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x - y)),
_ => Err(RuntimeError::TypeError(
"Invalid types for subtraction".to_string(),
)),
})?,
Mul => self.binary_op(|a, b| match (a, b) {
(Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
_ => Err(RuntimeError::TypeError(
"Invalid types for multiplication".to_string(),
)),
})?,
Div => self.binary_op(|a, b| match (a, b) {
(Value::Int(x), Value::Int(y)) => {
if y == 0 {
return Err(RuntimeError::DivisionByZero);
}
Ok(Value::Int(x / y))
}
(Value::Float(x), Value::Float(y)) => {
if y == 0.0 {
return Err(RuntimeError::DivisionByZero);
}
Ok(Value::Float(x / y))
}
_ => Err(RuntimeError::TypeError(
"Invalid types for division".to_string(),
)),
})?,
Eq => self.binary_op(|a, b| Ok(Value::Bool(a == b)))?,
Ne => self.binary_op(|a, b| Ok(Value::Bool(a != b)))?,
Lt => self.binary_op(|a, b| match (a, b) {
(Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x < y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x < y)),
_ => Err(RuntimeError::TypeError(
"Invalid types for less than".to_string(),
)),
})?,
Le => self.binary_op(|a, b| match (a, b) {
(Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x <= y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x <= y)),
_ => Err(RuntimeError::TypeError(
"Invalid types for less than or equal".to_string(),
)),
})?,
Gt => self.binary_op(|a, b| match (a, b) {
(Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x > y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x > y)),
_ => Err(RuntimeError::TypeError(
"Invalid types for greater than".to_string(),
)),
})?,
Ge => self.binary_op(|a, b| match (a, b) {
(Value::Int(x), Value::Int(y)) => Ok(Value::Bool(x >= y)),
(Value::Float(x), Value::Float(y)) => Ok(Value::Bool(x >= y)),
_ => Err(RuntimeError::TypeError(
"Invalid types for greater than or equal".to_string(),
)),
})?,
Jump(target) => {
self.ip = target;
}
JumpIf(target) => {
let condition = self.pop()?;
if condition.is_truthy() {
self.ip = target;
}
}
JumpIfNot(target) => {
let condition = self.pop()?;
if !condition.is_truthy() {
self.ip = target;
}
}
BindLocal(name) => {
let value = self.pop()?;
let frame = self
.frames
.last_mut()
.ok_or(VMError::Runtime(RuntimeError::StackUnderflow))?;
frame.set_local(name, value);
}
LoadLocal(name) => {
let value = self
.frames
.last()
.and_then(|f| f.get_local(&name))
.cloned()
.ok_or(VMError::Runtime(RuntimeError::UndefinedVariable(name)))?;
self.push(value)?;
}
LoadGlobal(name) => {
let value = self
.globals
.get(&name)
.cloned()
.ok_or(VMError::Runtime(RuntimeError::UndefinedVariable(name)))?;
self.push(value)?;
}
MakeList(count) => {
let items = self
.stack
.pop_n(count)
.map_err(|_| VMError::Runtime(RuntimeError::StackUnderflow))?;
self.push(Value::List(items))?;
}
MakeDict(count) => {
let pairs = self
.stack
.pop_n(count * 2)
.map_err(|_| VMError::Runtime(RuntimeError::StackUnderflow))?;
let mut dict = HashMap::new();
for chunk in pairs.chunks(2) {
if let (Value::Str(key), value) = (&chunk[0], &chunk[1]) {
dict.insert(key.clone(), value.clone());
} else {
return Err(VMError::Runtime(RuntimeError::TypeError(
"Dict keys must be strings".to_string(),
)));
}
}
self.push(Value::Dict(dict))?;
}
RequireCapability(cap) => {
self.check_capability(&cap)?;
}
CheckCapability(cap) => {
let has_cap = self.config.capabilities.has_by_name(&cap);
self.push(Value::Bool(has_cap))?;
}
TraceValue(label) => {
let value = self
.stack
.peek()
.map_err(|_| VMError::Runtime(RuntimeError::StackUnderflow))?
.clone();
self.telemetry.push((label, value));
}
RecordTelemetry(event) => {
self.telemetry.push((event, Value::None));
}
Return => {
if let Some(frame) = self.frames.pop() {
if let (Some(ret_ip), Some(ret_fn)) = (frame.return_ip, frame.return_function) {
self.ip = ret_ip;
self.current_function = ret_fn;
self.stack.truncate(frame.bp);
} else {
let result = self.pop().unwrap_or(Value::None);
eprintln!("[VM] Return from main with result: {result:?}");
return Ok(ExecutionResult::Halt(result));
}
} else {
let result = self.pop().unwrap_or(Value::None);
eprintln!("[VM] Return with no frames, result: {result:?}");
return Ok(ExecutionResult::Halt(result));
}
}
Halt => {
let result = self.pop().unwrap_or(Value::None);
return Ok(ExecutionResult::Halt(result));
}
Nop => {}
And => self.binary_op(|a, b| match (a, b) {
(Value::Bool(x), Value::Bool(y)) => Ok(Value::Bool(x && y)),
_ => Err(RuntimeError::TypeError("Invalid types for AND".to_string())),
})?,
Or => self.binary_op(|a, b| match (a, b) {
(Value::Bool(x), Value::Bool(y)) => Ok(Value::Bool(x || y)),
_ => Err(RuntimeError::TypeError("Invalid types for OR".to_string())),
})?,
Not => {
let value = self.pop()?;
match value {
Value::Bool(b) => self.push(Value::Bool(!b))?,
_ => {
return Err(VMError::Runtime(RuntimeError::TypeError(
"NOT requires boolean".to_string(),
)))
}
}
}
ListAppend => {
let item = self.pop()?;
let list = self.pop()?;
match list {
Value::List(mut items) => {
items.push(item);
self.push(Value::List(items))?;
}
_ => {
return Err(VMError::Runtime(RuntimeError::TypeError(
"ListAppend requires a list".to_string(),
)))
}
}
}
ListConcat => self.binary_op(|a, b| match (a, b) {
(Value::List(mut list1), Value::List(list2)) => {
list1.extend(list2);
Ok(Value::List(list1))
}
_ => Err(RuntimeError::TypeError(
"ListConcat requires two lists".to_string(),
)),
})?,
DictInsert => {
let value = self.pop()?;
let key = self.pop()?;
let dict = self.pop()?;
match (dict, key) {
(Value::Dict(mut map), Value::Str(key_str)) => {
map.insert(key_str, value);
self.push(Value::Dict(map))?;
}
_ => {
return Err(VMError::Runtime(RuntimeError::TypeError(
"DictInsert requires dict and string key".to_string(),
)))
}
}
}
DictMerge => self.binary_op(|a, b| match (a, b) {
(Value::Dict(mut dict1), Value::Dict(dict2)) => {
dict1.extend(dict2);
Ok(Value::Dict(dict1))
}
_ => Err(RuntimeError::TypeError(
"DictMerge requires two dicts".to_string(),
)),
})?,
Call(function_idx) => {
if self.frames.len() >= self.config.max_call_depth {
return Err(VMError::Runtime(RuntimeError::StackOverflow));
}
let frame = Frame::with_return(
function_idx,
self.stack.depth(),
self.ip,
self.current_function,
);
self.frames.push(frame);
self.current_function = function_idx;
self.ip = 0;
}
GetIndex => {
let index = self.pop()?;
let collection = self.pop()?;
match (&collection, &index) {
(Value::List(items), Value::Int(idx)) => {
if *idx < 0 || *idx as usize >= items.len() {
return Err(VMError::Runtime(RuntimeError::IndexOutOfBounds {
index: *idx,
length: items.len(),
}));
}
self.push(items[*idx as usize].clone())?;
}
(Value::Str(s), Value::Int(idx)) => {
if *idx < 0 || *idx as usize >= s.len() {
return Err(VMError::Runtime(RuntimeError::IndexOutOfBounds {
index: *idx,
length: s.len(),
}));
}
let ch = s.chars().nth(*idx as usize).unwrap();
self.push(Value::Str(ch.to_string()))?;
}
(Value::Dict(map), Value::Str(key)) => match map.get(key) {
Some(value) => self.push(value.clone())?,
None => {
return Err(VMError::Runtime(RuntimeError::KeyNotFound(key.clone())))
}
},
_ => {
return Err(VMError::Runtime(RuntimeError::TypeError(format!(
"Invalid types for indexing: {collection:?}[{index:?}]"
))))
}
}
}
CallNative(name) => match name.as_str() {
"len" => {
let value = self.pop()?;
let length = match value {
Value::List(ref items) => items.len() as i64,
Value::Str(ref s) => s.len() as i64,
Value::Dict(ref map) => map.len() as i64,
_ => {
return Err(VMError::Runtime(RuntimeError::TypeError(format!(
"len() not supported for {value:?}"
))))
}
};
self.push(Value::Int(length))?;
}
_ => {
return Err(VMError::Runtime(RuntimeError::NativeFunctionError(
format!("Unknown native function: {name}"),
)))
}
},
CallIntrinsic { name, capability } => {
if let Some(cap) = capability {
self.check_capability(&cap)?;
}
eprintln!("[VM] Looking for intrinsic: {name}");
eprintln!("[VM] Available intrinsics: {:?}", self.intrinsics.names());
let intrinsic = self.intrinsics.get(&name).ok_or(
VMError::Runtime(RuntimeError::NativeFunctionError(format!(
"Unknown intrinsic: {name}"
)))
)?;
let args = match name.as_str() {
"voice.speak" => {
vec![self.pop()?]
}
"display.chart" | "display.image" => {
let title = self.pop()?;
let data = self.pop()?;
vec![data, title]
}
"net.fetch" => {
vec![self.pop()?]
}
"wait.confirm" => {
vec![self.pop()?]
}
_ => {
return Err(VMError::Runtime(RuntimeError::NativeFunctionError(
format!("No argument handling for intrinsic: {name}"),
)));
}
};
eprintln!("[VM] Executing intrinsic {name} with args: {args:?}");
let result = tokio::task::block_in_place(|| {
tokio::runtime::Handle::current()
.block_on(async { intrinsic.execute(args).await })
})
.map_err(|e| {
VMError::Runtime(RuntimeError::NativeFunctionError(format!(
"Intrinsic error: {e}"
)))
})?;
eprintln!("[VM] Intrinsic result: {result:?}");
self.push(result)?;
}
_ => {
return Err(VMError::Runtime(RuntimeError::NativeFunctionError(
format!("Instruction {instruction:?} not yet implemented"),
)))
}
}
Ok(ExecutionResult::Continue)
}
pub fn push(&mut self, value: Value) -> Result<(), VMError> {
self.stack
.push(value)
.map_err(|_| VMError::Runtime(RuntimeError::StackOverflow))
}
pub fn pop(&mut self) -> Result<Value, VMError> {
self.stack
.pop()
.map_err(|_| VMError::Runtime(RuntimeError::StackUnderflow))
}
pub fn check_capability(&self, capability: &str) -> Result<(), VMError> {
if self.config.capabilities.has_by_name(capability) {
Ok(())
} else {
Err(VMError::Runtime(RuntimeError::CapabilityError(format!(
"Capability '{capability}' not granted"
))))
}
}
fn binary_op<F>(&mut self, op: F) -> Result<(), VMError>
where
F: FnOnce(Value, Value) -> Result<Value, RuntimeError>,
{
let b = self.pop()?;
let a = self.pop()?;
let result = op(a, b).map_err(VMError::Runtime)?;
self.push(result)
}
fn rotate_three(&mut self) -> Result<(), VMError> {
let c = self.pop()?;
let b = self.pop()?;
let a = self.pop()?;
self.push(b)?;
self.push(c)?;
self.push(a)?;
Ok(())
}
pub fn telemetry(&self) -> &[(String, Value)] {
&self.telemetry
}
pub fn clear_telemetry(&mut self) {
self.telemetry.clear();
}
pub fn memory_usage(&self) -> usize {
self.memory.current_usage()
}
pub fn memory_usage_percentage(&self) -> f64 {
self.memory.usage_percentage()
}
}
enum ExecutionResult {
Continue,
Halt(Value),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Capability;
fn create_test_vm() -> VM {
let config = VMConfig {
max_stack_size: 1000,
max_call_depth: 100,
max_execution_steps: 10000,
capabilities: CapabilitySet::new(),
};
VM::new(config)
}
#[test]
fn test_arithmetic_operations() {
let mut vm = create_test_vm();
vm.load_bytecode(vec![vec![
Instruction::Push(Value::Int(5)),
Instruction::Push(Value::Int(3)),
Instruction::Add,
Instruction::Halt,
]]);
let result = vm.execute().unwrap();
assert_eq!(result, Value::Int(8));
}
#[test]
fn test_immutable_bindings() {
let mut vm = create_test_vm();
vm.frames.push(Frame::new(0, 0));
vm.load_bytecode(vec![vec![
Instruction::Push(Value::Int(42)),
Instruction::BindLocal("x".to_string()),
Instruction::LoadLocal("x".to_string()),
Instruction::Push(Value::Int(10)),
Instruction::Add,
Instruction::Halt,
]]);
let result = vm.execute().unwrap();
assert_eq!(result, Value::Int(52));
}
#[test]
fn test_list_operations() {
let mut vm = create_test_vm();
vm.load_bytecode(vec![vec![
Instruction::Push(Value::Int(1)),
Instruction::Push(Value::Int(2)),
Instruction::Push(Value::Int(3)),
Instruction::MakeList(3),
Instruction::Halt,
]]);
let result = vm.execute().unwrap();
match result {
Value::List(items) => {
assert_eq!(items.len(), 3);
assert_eq!(items[0], Value::Int(1));
assert_eq!(items[1], Value::Int(2));
assert_eq!(items[2], Value::Int(3));
}
_ => panic!("Expected list"),
}
}
#[test]
fn test_dict_operations() {
let mut vm = create_test_vm();
vm.load_bytecode(vec![vec![
Instruction::Push(Value::Str("key1".to_string())),
Instruction::Push(Value::Int(100)),
Instruction::Push(Value::Str("key2".to_string())),
Instruction::Push(Value::Str("value2".to_string())),
Instruction::MakeDict(2),
Instruction::Halt,
]]);
let result = vm.execute().unwrap();
match result {
Value::Dict(map) => {
assert_eq!(map.len(), 2);
assert_eq!(map.get("key1"), Some(&Value::Int(100)));
assert_eq!(map.get("key2"), Some(&Value::Str("value2".to_string())));
}
_ => panic!("Expected dict"),
}
}
#[test]
fn test_capability_check() {
let mut config = VMConfig::default();
config.capabilities.grant(Capability::AudioSpeak);
let mut vm = VM::new(config);
vm.load_bytecode(vec![vec![
Instruction::CheckCapability("audio.speak".to_string()),
Instruction::Halt,
]]);
let result = vm.execute().unwrap();
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_capability_requirement_failure() {
let mut vm = create_test_vm();
vm.load_bytecode(vec![vec![
Instruction::RequireCapability("network.fetch".to_string()),
Instruction::Halt,
]]);
let result = vm.execute();
assert!(matches!(
result,
Err(VMError::Runtime(RuntimeError::CapabilityError(_)))
));
}
#[test]
fn test_control_flow() {
let mut vm = create_test_vm();
vm.load_bytecode(vec![vec![
Instruction::Push(Value::Bool(true)),
Instruction::JumpIf(3),
Instruction::Push(Value::Int(1)), Instruction::Push(Value::Int(2)), Instruction::Halt,
]]);
let result = vm.execute().unwrap();
assert_eq!(result, Value::Int(2));
}
#[test]
fn test_execution_limit() {
let config = VMConfig {
max_execution_steps: 5,
..Default::default()
};
let mut vm = VM::new(config);
vm.load_bytecode(vec![vec![
Instruction::Push(Value::Int(1)),
Instruction::Jump(0),
]]);
let result = vm.execute();
assert!(matches!(result, Err(VMError::ExecutionLimitExceeded)));
}
#[test]
fn test_telemetry() {
let mut vm = create_test_vm();
vm.load_bytecode(vec![vec![
Instruction::Push(Value::Int(42)),
Instruction::TraceValue("debug_value".to_string()),
Instruction::RecordTelemetry("test_event".to_string()),
Instruction::Halt,
]]);
vm.execute().unwrap();
let telemetry = vm.telemetry();
assert_eq!(telemetry.len(), 2);
assert_eq!(telemetry[0].0, "debug_value");
assert_eq!(telemetry[0].1, Value::Int(42));
assert_eq!(telemetry[1].0, "test_event");
}
}