1mod options;
2mod runner;
3mod values;
4
5use crate::call_frame::Frames;
6use crate::lifecycle::{Lifecycle, TestResults};
7use crate::process::{ModulesMap, Process, SpawnedProcess};
8use crate::{generate_builder, CallFrame, Instruction, Runner, Scope, VMStack, Variable};
9use crate::{handle_js, out, outln, Module, RigzBuilder, VMError, Value};
10use derive_more::IntoIterator;
11use std::cell::RefCell;
12use std::collections::HashMap;
13use std::fmt::Debug;
14use std::time::Duration;
15
16pub use options::VMOptions;
17pub use values::*;
18
19#[derive(Debug)]
20pub struct VM<'vm> {
21 pub scopes: Vec<Scope<'vm>>,
22 pub frames: Frames<'vm>,
23 pub modules: ModulesMap<'vm>,
24 pub stack: VMStack,
25 pub sp: usize,
26 pub options: VMOptions,
27 pub lifecycles: Vec<Lifecycle>,
28 pub constants: Vec<Value>,
29 pub(crate) processes: Vec<SpawnedProcess<'vm>>,
30}
31
32impl<'vm> RigzBuilder<'vm> for VM<'vm> {
33 generate_builder!();
34
35 #[inline]
36 fn build(self) -> VM<'vm> {
37 self
38 }
39}
40
41impl Default for VM<'_> {
42 #[inline]
43 fn default() -> Self {
44 Self {
45 scopes: vec![Scope::default()],
46 frames: Default::default(),
47 modules: Default::default(),
48 sp: 0,
49 options: Default::default(),
50 lifecycles: Default::default(),
51 constants: Default::default(),
52 stack: Default::default(),
53 processes: vec![],
54 }
55 }
56}
57
58impl<'vm> VM<'vm> {
59 #[inline]
60 pub fn new() -> Self {
61 Self::default()
62 }
63
64 #[inline]
65 pub fn from_scopes(scopes: Vec<Scope<'vm>>) -> Self {
66 Self {
67 scopes,
68 ..Default::default()
69 }
70 }
71
72 pub fn process_ret(&mut self, ran: bool) -> VMState {
73 match self.frames.pop() {
74 None => {
75 let source = self.next_value("process_ret - empty stack");
76 VMState::Done(source.resolve(self))
77 }
78 Some(c) => {
79 let c = c;
80 let pc = self.frames.current.borrow().pc;
81 let mut updated = false;
82 loop {
83 let sp = self.sp;
84 let scope = &self.scopes[sp];
85 let len = scope.instructions.len();
86 let propagate = len != pc && matches!(scope.named, "if" | "unless" | "else");
87 if propagate {
88 match self.frames.pop() {
89 None => {
90 let source = self.next_value("process_ret - empty stack");
91 return VMState::Done(source.resolve(self));
92 }
93 Some(next) => {
94 self.sp = next.borrow().scope_id;
95 self.frames.current = next;
96 updated = true;
97 }
98 }
99 } else {
100 break;
101 }
102 }
103 if !updated {
104 self.sp = c.borrow().scope_id;
105 self.frames.current = c;
106 }
107 match ran {
108 false => VMState::Running,
109 true => {
110 let source = self.next_value("process_ret - ran");
111 VMState::Ran(source.resolve(self))
112 }
113 }
114 }
115 }
116 }
117
118 #[inline]
119 fn process_instruction(&mut self, instruction: Instruction<'vm>) -> VMState {
120 match instruction {
121 Instruction::Ret => self.process_ret(false),
122 instruction => self.process_core_instruction(instruction),
123 }
124 }
125
126 fn process_instruction_scope(&mut self, instruction: Instruction<'vm>) -> VMState {
127 match instruction {
128 Instruction::Ret => self.process_ret(true),
129 ins => self.process_core_instruction(ins),
130 }
131 }
132
133 #[inline]
134 fn next_instruction(&self) -> Option<Instruction<'vm>> {
135 let scope = &self.scopes[self.sp];
136 let pc = self.frames.current.borrow().pc;
137 self.frames.current.borrow_mut().pc += 1;
138 scope.instructions.get(pc).cloned()
139 }
140
141 pub fn eval(&mut self) -> Result<Value, VMError> {
143 match self.run() {
144 Value::Error(e) => Err(e),
145 v => Ok(v),
146 }
147 }
148
149 pub fn eval_within(&mut self, duration: Duration) -> Result<Value, VMError> {
150 match self.run_within(duration) {
151 Value::Error(e) => Err(e),
152 v => Ok(v),
153 }
154 }
155
156 pub fn add_bindings(&mut self, bindings: HashMap<&'vm str, (StackValue, bool)>) {
157 let mut current = self.frames.current.borrow_mut();
158 for (k, (v, mutable)) in bindings {
159 let v = if mutable {
160 Variable::Mut(v)
161 } else {
162 Variable::Let(v)
163 };
164 current.variables.insert(k, v);
165 }
166 }
167
168 pub fn run(&mut self) -> Value {
170 self.start_processes();
171
172 let mut run = || loop {
173 match self.step() {
174 None => {}
175 Some(v) => return v,
176 }
177 };
178
179 let res = run();
180 self.close_processes(res)
181 }
182
183 #[inline]
184 fn step(&mut self) -> Option<Value> {
185 let instruction = match self.next_instruction() {
186 None => return self.stack.pop().map(|e| e.resolve(self).borrow().clone()),
188 Some(s) => s,
189 };
190
191 match self.process_instruction(instruction) {
192 VMState::Ran(v) => {
193 return Some(
194 VMError::RuntimeError(format!("Unexpected ran state: {}", v.borrow())).into(),
195 )
196 }
197 VMState::Running => {}
198 VMState::Done(v) => return Some(v.borrow().clone()),
199 };
200 None
201 }
202
203 fn start_processes(&mut self) {
204 self.processes = self
205 .scopes
206 .iter()
207 .filter(|s| matches!(s.lifecycle, Some(Lifecycle::On(_))))
208 .map(|s| Process::spawn(s.clone(), self.options, self.modules.clone(), None))
209 .collect();
210 }
211
212 fn close_processes(&mut self, result: Value) -> Value {
213 let mut errors: Vec<VMError> = vec![];
214 for p in self.processes.drain(..) {
215 match p.close() {
216 Ok(_) => {}
217 Err(r) => {
218 errors.push(r);
219 }
220 }
221 }
222
223 if errors.is_empty() {
224 result
225 } else {
226 let len = errors.len() - 1;
227 let messages =
228 errors
229 .iter()
230 .enumerate()
231 .fold(String::new(), |mut res, (index, next)| {
232 res.push_str(next.to_string().as_str());
233 if index != len {
234 res.push_str(", ");
235 }
236 res
237 });
238 VMError::RuntimeError(format!("Process Failures: {messages}")).into()
239 }
240 }
241
242 pub fn run_within(&mut self, duration: Duration) -> Value {
243 #[cfg(not(feature = "js"))]
244 let now = std::time::Instant::now();
245 #[cfg(feature = "js")]
246 let now = web_time::Instant::now();
247 loop {
248 let elapsed = now.elapsed();
249 if elapsed > duration {
250 return VMError::TimeoutError(format!(
251 "Exceeded runtime {duration:?} - {:?}",
252 elapsed
253 ))
254 .into();
255 }
256
257 match self.step() {
258 None => {}
259 Some(v) => return v,
260 }
261 }
262 }
263
264 pub fn test(&mut self) -> TestResults {
265 let test_scopes: Vec<_> = self
267 .scopes
268 .iter()
269 .enumerate()
270 .filter_map(|(index, s)| match &s.lifecycle {
271 None => None,
272 Some(Lifecycle::Test(_)) => {
273 let Instruction::Ret =
274 s.instructions.last().expect("No instructions for scope")
275 else {
276 unreachable!("Invalid Scope")
277 };
278 Some((index, s.named))
279 }
280 Some(_) => None,
281 })
282 .collect();
283
284 let mut passed = 0;
285 let mut failed = 0;
286 #[cfg(not(feature = "js"))]
287 let start = std::time::Instant::now();
288 #[cfg(feature = "js")]
289 let start = web_time::Instant::now();
290 let mut failure_messages = Vec::new();
291 for (s, named) in test_scopes {
292 out!("test {named} ... ");
293 self.sp = s;
294 self.frames.current = RefCell::new(CallFrame {
295 scope_id: s,
296 ..Default::default()
297 });
298 let v = self.eval();
299 match v {
300 Err(e) => {
301 outln!("\x1b[31mFAILED\x1b[0m");
302 failed += 1;
303 failure_messages.push((named.to_string(), e));
304 }
305 Ok(_) => {
306 outln!("\x1b[32mok\x1b[0m");
307 passed += 1;
308 }
309 };
310 }
311
312 TestResults {
313 passed,
314 failed,
315 failure_messages,
316 duration: start.elapsed(),
317 }
318 }
319
320 pub fn run_scope(&mut self) -> VMState {
321 loop {
322 let instruction = match self.next_instruction() {
323 None => return VMState::Done(Value::None.into()),
325 Some(s) => s,
326 };
327
328 match self.process_instruction_scope(instruction) {
329 VMState::Running => {}
330 s => return s,
331 };
332 }
333 }
334
335 pub fn reset(&mut self) {
337 self.sp = 0;
338 self.stack.clear();
339 self.frames.reset()
340 }
341
342 pub fn snapshot(&self) -> Result<Vec<u8>, VMError> {
344 let mut bytes = Vec::new();
345 bytes.push(self.options.as_byte());
346 bytes.extend((self.sp as u64).to_le_bytes());
347
348 Ok(bytes)
354 }
355
356 pub fn load_snapshot(&mut self, bytes: Vec<u8>) -> Result<(), VMError> {
358 let mut bytes = bytes.into_iter();
359 self.options = VMOptions::from_byte(bytes.next().unwrap());
360 let mut sp = [0; 8];
361 for (i, b) in bytes.take(8).enumerate() {
362 sp[i] = b;
363 }
364 self.sp = u64::from_le_bytes(sp) as usize;
365 Ok(())
371 }
372}
373
374#[cfg(test)]
375pub mod vm_tests {
376 use crate::builder::RigzBuilder;
377 use crate::vm::VM;
378 use crate::{VMBuilder, Value};
379 use wasm_bindgen_test::*;
380
381 #[wasm_bindgen_test(unsupported = test)]
382 fn snapshot() {
383 let mut builder = VMBuilder::new();
384 builder.add_load_instruction(Value::Bool(true).into());
385 let vm = builder.build();
386 let bytes = vm.snapshot().expect("snapshot failed");
387 let mut vm2 = VM::default();
388 vm2.load_snapshot(bytes).expect("load failed");
389 assert_eq!(vm2.options, vm.options);
390 assert_eq!(vm2.sp, vm.sp);
391 }
393}