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, Continuation, 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 continuations: Vec<Continuation>,
21 stdout: String,
22 limits: ResourceLimits,
23 external_functions: Vec<String>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ZapcodeSnapshot {
30 data: Vec<u8>,
31}
32
33impl ZapcodeSnapshot {
34 pub(crate) fn capture(vm: &Vm) -> Result<Self> {
36 let builtin_names: HashSet<&str> = Vm::BUILTIN_GLOBAL_NAMES.iter().copied().collect();
38 let user_globals: Vec<(String, Value)> = vm
39 .globals
40 .iter()
41 .filter(|(k, _)| !builtin_names.contains(k.as_str()))
42 .map(|(k, v)| (k.clone(), v.clone()))
43 .collect();
44
45 let snapshot = VmSnapshot {
46 program: vm.program.clone(),
47 stack: vm.stack.clone(),
48 frames: vm.frames.clone(),
49 globals: user_globals,
50 try_stack: vm.try_stack.clone(),
51 continuations: vm.continuations.clone(),
52 stdout: vm.stdout.clone(),
53 limits: vm.limits.clone(),
54 external_functions: vm.external_functions.iter().cloned().collect(),
55 };
56
57 let data = postcard::to_allocvec(&snapshot)
58 .map_err(|e| ZapcodeError::SnapshotError(format!("capture failed: {}", e)))?;
59
60 Ok(Self { data })
61 }
62
63 pub fn dump(&self) -> Result<Vec<u8>> {
65 postcard::to_allocvec(self)
66 .map_err(|e| ZapcodeError::SnapshotError(format!("dump failed: {}", e)))
67 }
68
69 pub fn load(bytes: &[u8]) -> Result<Self> {
71 postcard::from_bytes(bytes)
72 .map_err(|e| ZapcodeError::SnapshotError(format!("load failed: {}", e)))
73 }
74
75 pub fn resume(self, return_value: Value) -> Result<VmState> {
78 let vm_snap: VmSnapshot = postcard::from_bytes(&self.data)
79 .map_err(|e| ZapcodeError::SnapshotError(format!("resume decode failed: {}", e)))?;
80
81 let user_globals: HashMap<String, Value> = vm_snap.globals.into_iter().collect();
82 let ext_set: HashSet<String> = vm_snap.external_functions.into_iter().collect();
83
84 let mut vm = Vm::from_snapshot(
85 vm_snap.program,
86 vm_snap.stack,
87 vm_snap.frames,
88 user_globals,
89 vm_snap.try_stack,
90 vm_snap.continuations,
91 vm_snap.stdout,
92 vm_snap.limits,
93 ext_set,
94 );
95
96 vm.stack.push(return_value);
99
100 vm.resume_execution()
101 }
102}