ivm_vm/
lib.rs

1use ivm_compile::byte_id;
2use ivm_compile::options::ProgramOptions;
3
4use crate::ivm_ext_x32::IvmX32ExternMap;
5
6pub mod ivm_ext_x32;
7
8// Wrapper for now.
9pub struct StackElement {
10    pub bytes: Vec<u8>,
11}
12
13impl StackElement {
14    pub fn new(bytes: Vec<u8>) -> Self {
15        Self { bytes }
16    }
17}
18
19pub trait ExternMap {
20    fn handle(&mut self, call_id: usize, stack: &mut Vec<StackElement>) -> Result<(), String>;
21}
22
23pub struct VmInstance {
24    pub options: ProgramOptions,
25    pub mem_pool: Vec<u8>,
26    pub execution_index: usize,
27    pub extern_map: Box<dyn ExternMap>,
28    pub stack: Vec<StackElement>,
29}
30
31impl VmInstance {
32    /// Introduce new bytes to the memory pool.
33    pub fn introduce<I>(&mut self, bytes: I)
34    where
35        I: IntoIterator<Item = u8>,
36    {
37        self.mem_pool.extend(bytes);
38    }
39
40    /// Returns a tuple containing the read bytes, and how many bytes were traversed.
41    fn handle_read_op(&self, mut index: usize) -> (Vec<u8>, usize) {
42        let identifier = self.mem_pool[index];
43        index += 1;
44
45        let arg = self.options.ptr_len.extract(index, &self.mem_pool);
46        index += self.options.ptr_len.get_span();
47
48        match identifier {
49            byte_id::RDOP_LOCAL => {
50                // `arg` is the size of the read operation.
51
52                let data = &self.mem_pool[index..][..arg];
53                (data.to_vec(), 1 + self.options.ptr_len.get_span() + arg)
54            }
55
56            byte_id::RDOP_POINT => {
57                // `arg` is the memory pointer index of the data we are pointing to.
58
59                let rd_size = self.options.ptr_len.extract(arg, &self.mem_pool);
60                let data = &self.mem_pool[arg..][..rd_size];
61
62                (data.to_vec(), 1 + self.options.ptr_len.get_span())
63            }
64            _ => panic!("unrecognized read operation '{identifier:02x}'"),
65        }
66    }
67
68    pub fn continue_execution(&mut self) -> Result<(), String> {
69        while self.execution_index < self.mem_pool.len() {
70            let byte_instruction = self.mem_pool[self.execution_index];
71            self.execution_index += 1;
72
73            match byte_instruction {
74                byte_id::I_VISIT => {
75                    self.execution_index = self
76                        .options
77                        .ptr_len
78                        .extract(self.execution_index, &self.mem_pool)
79                }
80
81                byte_id::I_MUTATE => {
82                    let dest = self
83                        .options
84                        .ptr_len
85                        .extract(self.execution_index, &self.mem_pool);
86
87                    self.execution_index += self.options.ptr_len.get_span();
88
89                    let (data, skip) = self.handle_read_op(self.execution_index);
90                    self.execution_index += skip;
91
92                    for i in 0..data.len() {
93                        self.mem_pool[dest + i] = data[i];
94                    }
95                }
96
97                byte_id::I_PUSH => {
98                    let (data, skip) = self.handle_read_op(self.execution_index);
99
100                    self.stack.push(StackElement::new(data.to_vec()));
101                    self.execution_index += skip;
102                }
103
104                byte_id::I_EXTERN_CALL => {
105                    let call_id = self
106                        .options
107                        .ptr_len
108                        .extract(self.execution_index, &self.mem_pool);
109
110                    self.extern_map.handle(call_id, &mut self.stack)?;
111                    self.execution_index += self.options.ptr_len.get_span();
112                }
113
114                _ => {
115                    return Err(format!(
116                        "unrecognized instruction (hex): {byte_instruction:02x}"
117                    ))
118                }
119            }
120        }
121        Ok(())
122    }
123
124    pub fn from_raw(
125        program_options: ProgramOptions,
126        mem_pool: Vec<u8>,
127        ptr_index: usize,
128        extern_map: Box<dyn ExternMap>,
129    ) -> Self {
130        Self {
131            options: program_options,
132            mem_pool,
133            execution_index: ptr_index,
134            extern_map,
135            stack: Vec::new(),
136        }
137    }
138
139    /// Uses the latest ivm_ext_x32 extern map.
140    ///
141    /// See [ivm_ext_x32], [IvmX32ExternMap].
142    pub fn init(program_options: ProgramOptions) -> Self {
143        Self::from_raw(
144            program_options,
145            Vec::with_capacity(128),
146            0,
147            Box::new(IvmX32ExternMap),
148        )
149    }
150}