use std::any::Any;
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use crate::script_engine::CompiledScript;
use super::instruction::ScriptError;
pub use super::instruction::{InstructionHandler, InstructionRegistry};
#[derive(Debug, Default)]
pub struct VMRegisters {
pub general: [u64; 16],
named: HashMap<String, Box<dyn Any + Send + Sync>>,
}
impl VMRegisters {
pub fn new() -> Self {
Self {
general: [0; 16],
named: HashMap::new(),
}
}
pub fn set_named<T: Any + Send + Sync>(&mut self, key: &str, value: T) {
self.named.insert(key.to_string(), Box::new(value));
}
pub fn get_named<T: Any>(&self, key: &str) -> Option<&T> {
self.named
.get(key)
.and_then(|boxed| boxed.downcast_ref::<T>())
}
pub fn clear_named(&mut self) {
self.named.clear();
}
pub fn general_count(&self) -> usize {
self.general.len()
}
}
pub type PersistentState = HashMap<String, Box<dyn Any>>;
pub type InstructionScopes = HashMap<usize, Box<dyn Any>>;
#[derive(Debug, Default)]
pub struct BlockLocalContext {
pub data: HashMap<String, Box<dyn Any + Send + Sync>>,
}
impl BlockLocalContext {
pub fn new() -> Self {
Self::default()
}
}
pub struct VMContext {
pub ip: usize,
pub registers: VMRegisters,
pub persistent_state: PersistentState,
pub execution_shared_state: HashMap<String, Box<dyn Any>>,
pub instruction_scopes: InstructionScopes,
pub block_context_stack: Vec<BlockLocalContext>,
#[cfg(feature = "script_process_context")]
pub process: crate::script_engine::process_ctx::ProcessContext,
}
impl VMContext {
pub fn new() -> Self {
Self {
ip: 0,
registers: VMRegisters::new(),
persistent_state: HashMap::new(),
execution_shared_state: HashMap::new(),
instruction_scopes: HashMap::new(),
block_context_stack: Vec::new(),
#[cfg(feature = "script_process_context")]
process: crate::script_engine::process_ctx::ProcessContext::new(),
}
}
pub fn get_persistent_state<T: Any>(&self, key: &str) -> Option<&T> {
self.persistent_state
.get(key)
.and_then(|boxed| boxed.downcast_ref::<T>())
}
pub fn has_persistent_state(&self, key: &str) -> bool {
self.persistent_state.contains_key(key)
}
pub fn set_persistent_state<T: Any>(&mut self, key: &str, value: T) {
self.persistent_state
.insert(key.to_string(), Box::new(value));
}
pub fn clear_persistent_state(&mut self) {
self.persistent_state.clear();
}
pub fn get_persistent_state_mut<T: Any>(&mut self, key: &str) -> Option<&mut T> {
self.persistent_state
.get_mut(key)
.and_then(|boxed| boxed.downcast_mut::<T>())
}
#[inline]
pub fn get_or_create_execution_state<T: Any + Default>(&mut self, key: &str) -> &mut T {
if !self.execution_shared_state.contains_key(key) {
self.execution_shared_state
.insert(key.to_string(), Box::new(T::default()));
}
self.execution_shared_state
.get_mut(key)
.and_then(|boxed| boxed.downcast_mut::<T>())
.expect("Execution shared state type mismatch")
}
pub fn get_execution_state_mut<T: Any>(&mut self, key: &str) -> Option<&mut T> {
self.execution_shared_state
.get_mut(key)
.and_then(|boxed| boxed.downcast_mut::<T>())
}
pub fn has_execution_state(&self, key: &str) -> bool {
self.execution_shared_state.contains_key(key)
}
pub fn clear_execution_shared_state(&mut self) {
self.execution_shared_state.clear();
}
pub fn get_or_init_instruction_scope<T: Any + Send + Sync, F: FnOnce() -> T>(
&mut self,
ip: usize,
init_fn: F,
) -> &mut T {
if !self.instruction_scopes.contains_key(&ip) {
let value = init_fn();
self.instruction_scopes.insert(ip, Box::new(value));
}
self.instruction_scopes
.get_mut(&ip)
.and_then(|boxed| boxed.downcast_mut::<T>())
.expect("Instruction scope type mismatch")
}
#[inline]
pub fn get_or_init_current_scope<T: Any + Send + Sync, F: FnOnce() -> T>(
&mut self,
init_fn: F,
) -> &mut T {
let ip = self.ip;
self.get_or_init_instruction_scope(ip, init_fn)
}
pub fn get_instruction_scope_mut<T: Any>(&mut self, ip: usize) -> Option<&mut T> {
self.instruction_scopes
.get_mut(&ip)
.and_then(|boxed| boxed.downcast_mut::<T>())
}
pub fn clear_instruction_scopes(&mut self) {
self.instruction_scopes.clear();
}
#[inline]
pub fn enter_block(&mut self) {
self.block_context_stack.push(BlockLocalContext::new());
}
#[inline]
pub fn leave_block(&mut self) {
self.block_context_stack.pop();
}
pub fn push_to_current_block<T: Any + Send + Sync>(&mut self, key: &str, value: T) {
if let Some(ctx) = self.block_context_stack.last_mut() {
ctx.data.insert(key.to_string(), Box::new(value));
}
}
pub fn get_from_current_block<T: Any>(&self, key: &str) -> Option<&T> {
self.block_context_stack
.last()
.and_then(|ctx| ctx.data.get(key))
.and_then(|boxed| boxed.downcast_ref::<T>())
}
pub fn is_block_context_empty(&self) -> bool {
self.block_context_stack.is_empty()
}
pub fn is_in_block(&self) -> bool {
!self.block_context_stack.is_empty()
}
pub fn clear_block_contexts(&mut self) {
self.block_context_stack.clear();
}
pub fn reset_execution(&mut self) {
self.ip = 0;
self.registers = VMRegisters::new();
self.clear_instruction_scopes();
self.clear_execution_shared_state();
self.clear_block_contexts();
}
pub fn reset_all(&mut self) {
self.reset_execution();
self.clear_persistent_state();
#[cfg(feature = "script_process_context")]
self.process.clear();
}
}
impl Default for VMContext {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct VMConfig {
pub max_steps: usize,
}
impl Default for VMConfig {
fn default() -> Self {
Self {
max_steps: 1_000_000,
}
}
}
pub struct VM<'a> {
config: VMConfig,
registry: &'a InstructionRegistry,
context: VMContext,
steps_executed: usize,
interrupt_flag: Option<Arc<AtomicBool>>,
}
impl<'a> VM<'a> {
pub fn new(config: VMConfig, registry: &'a InstructionRegistry) -> Self {
Self {
config,
registry,
context: VMContext::new(),
steps_executed: 0,
interrupt_flag: None,
}
}
pub fn new_with_interrupt(
config: VMConfig,
registry: &'a InstructionRegistry,
interrupt_flag: Arc<AtomicBool>,
) -> Self {
Self {
config,
registry,
context: VMContext::new(),
steps_executed: 0,
interrupt_flag: Some(interrupt_flag),
}
}
#[inline]
fn check_interrupt(&self) -> Result<(), ScriptError> {
if let Some(ref flag) = self.interrupt_flag {
if flag.load(Ordering::SeqCst) {
return Err(ScriptError::Interrupted(
"Script execution interrupted by user".into(),
));
}
}
Ok(())
}
pub fn execute(&mut self, script: &CompiledScript) -> Result<(), ScriptError> {
self.context.reset_execution();
self.steps_executed = 0;
while self.context.ip < script.instructions.len() {
self.check_interrupt()?;
self.steps_executed += 1;
if self.steps_executed > self.config.max_steps {
return Err(ScriptError::ExecutionError(format!(
"Max execution steps ({}) exceeded",
self.config.max_steps
)));
}
let instr = &script.instructions[self.context.ip];
let ip_before = self.context.ip;
let handler = self.registry.get_handler(&instr.name).ok_or_else(|| {
ScriptError::ExecutionError(format!("Handler not found for '{}'", instr.name))
})?;
handler.execute(&mut self.context, &instr.data, instr.metadata.as_ref())?;
if self.context.ip == ip_before {
self.context.ip += 1;
}
}
Ok(())
}
pub fn get_context(&self) -> &VMContext {
&self.context
}
pub fn get_context_mut(&mut self) -> &mut VMContext {
&mut self.context
}
pub fn steps_executed(&self) -> usize {
self.steps_executed
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vm_creation() {
let registry = InstructionRegistry::new();
let vm = VM::new(VMConfig::default(), ®istry);
assert_eq!(vm.steps_executed(), 0);
}
#[test]
fn test_registers_general() {
let mut regs = VMRegisters::new();
regs.general[0] = 42;
regs.general[1] = 100;
assert_eq!(regs.general[0], 42);
assert_eq!(regs.general[1], 100);
}
#[test]
fn test_registers_named() {
let mut regs = VMRegisters::new();
regs.set_named("x", 100i32);
regs.set_named("y", 200i32);
assert_eq!(regs.get_named::<i32>("x"), Some(&100));
assert_eq!(regs.get_named::<i32>("y"), Some(&200));
assert_eq!(regs.get_named::<i32>("z"), None);
}
#[test]
fn test_vm_context_persistent_state() {
let mut ctx = VMContext::new();
ctx.set_persistent_state("hwnd", 0x12345usize);
ctx.set_persistent_state("process_id", 1234u32);
assert_eq!(ctx.get_persistent_state::<usize>("hwnd"), Some(&0x12345));
assert_eq!(ctx.get_persistent_state::<u32>("process_id"), Some(&1234));
}
#[test]
fn test_instruction_scopes_lifecycle() {
let mut ctx = VMContext::new();
ctx.set_persistent_state("persistent_key", "persistent_value");
let ip = 0;
let scope_data = ctx.get_or_init_instruction_scope(ip, || "scope_value".to_string());
assert_eq!(scope_data, "scope_value");
ctx.reset_execution();
assert_eq!(
ctx.get_persistent_state::<&str>("persistent_key"),
Some(&"persistent_value")
);
assert!(ctx.get_instruction_scope_mut::<String>(ip).is_none());
}
#[test]
fn test_clear_persistent_state() {
let mut ctx = VMContext::new();
ctx.set_persistent_state("key1", "value1");
ctx.set_persistent_state("key2", "value2");
assert_eq!(ctx.get_persistent_state::<&str>("key1"), Some(&"value1"));
ctx.clear_persistent_state();
assert!(ctx.get_persistent_state::<&str>("key1").is_none());
assert!(ctx.get_persistent_state::<&str>("key2").is_none());
}
}
#[cfg(test)]
mod benchmarks {
use super::*;
use std::time::Instant;
#[test]
fn benchmark_get_or_init_instruction_scope() {
let mut vm = VMContext::new();
let iterations = 1_000_000;
let ip = 0;
let start = Instant::now();
for _ in 0..iterations {
let scope_data = vm.get_or_init_instruction_scope(ip, || 42i32);
*scope_data += 1;
}
let elapsed = start.elapsed();
println!(
"get_or_init_instruction_scope: {} iterations in {:?}, avg: {:?}",
iterations,
elapsed,
elapsed / iterations as u32
);
assert!(elapsed.as_nanos() / (iterations as u128) < 1000);
}
#[test]
fn benchmark_persistent_state_access() {
let mut vm = VMContext::new();
vm.set_persistent_state("test_key", 42i32);
let iterations = 1_000_000;
let start = Instant::now();
for _ in 0..iterations {
let _value = vm
.persistent_state
.get("test_key")
.and_then(|boxed| boxed.downcast_ref::<i32>())
.unwrap();
}
let hashmap_time = start.elapsed();
println!(
"Persistent state access: {} iterations in {:?}, avg: {:?}",
iterations,
hashmap_time,
hashmap_time / iterations as u32
);
assert!(hashmap_time.as_nanos() / (iterations as u128) < 500);
}
}