1use std::collections::{HashMap, HashSet};
2
3use serde::{Deserialize, Serialize};
4
5use crate::compiler::CompiledProgram;
6use crate::error::{Result, ZapcodeError};
7use crate::sandbox::ResourceLimits;
8use crate::value::Value;
9use crate::vm::{CallFrame, TryInfo, Vm, VmState};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13struct VmSnapshot {
14 program: CompiledProgram,
15 stack: Vec<Value>,
16 frames: Vec<CallFrame>,
17 globals: Vec<(String, Value)>,
19 try_stack: Vec<TryInfo>,
20 stdout: String,
21 limits: ResourceLimits,
22 external_functions: Vec<String>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ZapcodeSnapshot {
29 data: Vec<u8>,
30}
31
32impl ZapcodeSnapshot {
33 pub(crate) fn capture(vm: &Vm) -> Result<Self> {
35 let builtin_names: HashSet<&str> = Vm::BUILTIN_GLOBAL_NAMES.iter().copied().collect();
37 let user_globals: Vec<(String, Value)> = vm
38 .globals
39 .iter()
40 .filter(|(k, _)| !builtin_names.contains(k.as_str()))
41 .map(|(k, v)| (k.clone(), v.clone()))
42 .collect();
43
44 let snapshot = VmSnapshot {
45 program: vm.program.clone(),
46 stack: vm.stack.clone(),
47 frames: vm.frames.clone(),
48 globals: user_globals,
49 try_stack: vm.try_stack.clone(),
50 stdout: vm.stdout.clone(),
51 limits: vm.limits.clone(),
52 external_functions: vm.external_functions.iter().cloned().collect(),
53 };
54
55 let data = postcard::to_allocvec(&snapshot)
56 .map_err(|e| ZapcodeError::SnapshotError(format!("capture failed: {}", e)))?;
57
58 Ok(Self { data })
59 }
60
61 pub fn dump(&self) -> Result<Vec<u8>> {
63 postcard::to_allocvec(self)
64 .map_err(|e| ZapcodeError::SnapshotError(format!("dump failed: {}", e)))
65 }
66
67 pub fn load(bytes: &[u8]) -> Result<Self> {
69 postcard::from_bytes(bytes)
70 .map_err(|e| ZapcodeError::SnapshotError(format!("load failed: {}", e)))
71 }
72
73 pub fn resume(self, return_value: Value) -> Result<VmState> {
76 let vm_snap: VmSnapshot = postcard::from_bytes(&self.data)
77 .map_err(|e| ZapcodeError::SnapshotError(format!("resume decode failed: {}", e)))?;
78
79 let user_globals: HashMap<String, Value> = vm_snap.globals.into_iter().collect();
80 let ext_set: HashSet<String> = vm_snap.external_functions.into_iter().collect();
81
82 let mut vm = Vm::from_snapshot(
83 vm_snap.program,
84 vm_snap.stack,
85 vm_snap.frames,
86 user_globals,
87 vm_snap.try_stack,
88 vm_snap.stdout,
89 vm_snap.limits,
90 ext_set,
91 );
92
93 vm.stack.push(return_value);
96
97 vm.resume_execution()
98 }
99}