1use crate::deterministic::DeterministicContext;
4use crate::frame::UniversalFrame;
5use crate::version::{check_ues_format, UES_FORMAT_VERSION};
6use indexmap::IndexMap;
7use parallax_core::{ExceptionInfo, ExecutionId, RuntimeKind};
8use parallax_pcir::PcirProgram;
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
13pub struct ControlState {
14 #[serde(default, skip_serializing_if = "Option::is_none")]
16 pub safepoint_label: Option<String>,
17 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub safepoint_kind: Option<String>,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub instruction_position: Option<String>,
23 #[serde(default)]
25 pub suspended: bool,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub resume_byte_offset: Option<usize>,
29}
30
31#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
33pub struct HeapState {
34 #[serde(default)]
36 pub bindings: serde_json::Value,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub pir_document: Option<serde_json::Value>,
40}
41
42#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
44pub struct ModuleState {
45 pub name: String,
47 pub runtime: RuntimeKind,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub fingerprint: Option<String>,
52}
53
54#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
56pub struct AsyncState {
57 pub status: String,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub detail: Option<String>,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub tasks: Option<serde_json::Value>,
65}
66
67#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
69pub struct CapabilityState {
70 #[serde(default)]
72 pub granted: Vec<String>,
73 #[serde(default)]
75 pub notes: Vec<String>,
76}
77
78#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
80pub struct ExternalResource {
81 pub kind: String,
83 pub descriptor: String,
85 #[serde(default)]
87 pub required: bool,
88}
89
90#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
92pub struct MigrationMetadata {
93 #[serde(default, skip_serializing_if = "Option::is_none")]
95 pub capture_mode: Option<String>,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub continuum_status: Option<String>,
99 #[serde(default)]
101 pub notes: Vec<String>,
102 #[serde(default)]
104 pub extra: IndexMap<String, serde_json::Value>,
105}
106
107#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
109pub struct UniversalExecutionState {
110 pub format_version: u32,
112 pub execution_id: ExecutionId,
114 pub source_runtime: RuntimeKind,
116 pub source_program: Option<String>,
118 pub control_state: ControlState,
120 pub call_stack: Vec<UniversalFrame>,
122 pub heap: HeapState,
124 #[serde(default)]
126 pub globals: IndexMap<String, serde_json::Value>,
127 #[serde(default)]
129 pub modules: Vec<ModuleState>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub exception_state: Option<ExceptionInfo>,
133 #[serde(default)]
135 pub async_state: AsyncState,
136 #[serde(default)]
138 pub capability_state: CapabilityState,
139 #[serde(default)]
141 pub external_resources: Vec<ExternalResource>,
142 #[serde(default)]
144 pub deterministic_context: DeterministicContext,
145 #[serde(default)]
147 pub migration_metadata: MigrationMetadata,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
150 pub pcir: Option<PcirProgram>,
151 #[serde(default, skip_serializing_if = "Option::is_none")]
153 pub resume_source: Option<String>,
154 #[serde(default)]
156 pub extensions: IndexMap<String, serde_json::Value>,
157}
158
159impl UniversalExecutionState {
160 pub fn empty(runtime: RuntimeKind) -> Self {
162 Self {
163 format_version: UES_FORMAT_VERSION,
164 execution_id: ExecutionId::new(),
165 source_runtime: runtime,
166 source_program: None,
167 control_state: ControlState {
168 suspended: true,
169 ..ControlState::default()
170 },
171 call_stack: Vec::new(),
172 heap: HeapState::default(),
173 globals: IndexMap::new(),
174 modules: Vec::new(),
175 exception_state: None,
176 async_state: AsyncState {
177 status: "none".into(),
178 detail: None,
179 tasks: None,
180 },
181 capability_state: CapabilityState::default(),
182 external_resources: Vec::new(),
183 deterministic_context: DeterministicContext::unsupported_engine(),
184 migration_metadata: MigrationMetadata {
185 continuum_status: Some("experimental".into()),
186 ..MigrationMetadata::default()
187 },
188 pcir: None,
189 resume_source: None,
190 extensions: IndexMap::new(),
191 }
192 }
193
194 pub fn checkpoint_shell(
196 runtime: RuntimeKind,
197 program: impl Into<String>,
198 label: impl Into<String>,
199 ) -> Self {
200 let program = program.into();
201 let label = label.into();
202 let mut ues = Self::empty(runtime.clone());
203 ues.source_program = Some(program.clone());
204 ues.control_state = ControlState {
205 safepoint_label: Some(label.clone()),
206 safepoint_kind: Some("explicit_checkpoint".into()),
207 instruction_position: Some(format!("safepoint:{label}")),
208 suspended: true,
209 resume_byte_offset: None,
210 };
211 ues.call_stack.push(UniversalFrame::checkpoint(
212 runtime.clone(),
213 "__parallax_main",
214 program.clone(),
215 label.clone(),
216 IndexMap::new(),
217 ));
218 ues.modules.push(ModuleState {
219 name: program,
220 runtime,
221 fingerprint: None,
222 });
223 ues.pcir = Some(PcirProgram::checkpoint_stub(&label));
224 ues.migration_metadata = MigrationMetadata {
225 capture_mode: Some("explicit_checkpoint".into()),
226 continuum_status: Some("experimental".into()),
227 notes: vec![
228 "Captured at explicit parallax.checkpoint safepoint".into(),
229 "Arbitrary live stack migration is NOT claimed".into(),
230 ],
231 extra: IndexMap::new(),
232 };
233 ues
234 }
235
236 pub fn validate(&self) -> parallax_core::Result<()> {
238 check_ues_format(self.format_version)?;
239 if let Some(pcir) = &self.pcir {
240 pcir.validate()?;
241 }
242 Ok(())
243 }
244}