1use std::rc::Rc;
2use std::time::Instant;
3
4use crate::chunk::{Chunk, ChunkRef};
5use crate::value::{ModuleFunctionRegistry, VmError, VmValue};
6
7use super::{CallFrame, LocalSlot, Vm};
8
9impl Vm {
10 pub async fn execute(&mut self, chunk: &Chunk) -> Result<VmValue, VmError> {
12 let span_id = crate::tracing::span_start(crate::tracing::SpanKind::Pipeline, "main".into());
13 let result = self.run_chunk(chunk).await;
14 crate::tracing::span_end(span_id);
15 result
16 }
17
18 pub(crate) fn handle_error(&mut self, error: VmError) -> Result<Option<VmValue>, VmError> {
20 let thrown_value = match &error {
21 VmError::Thrown(v) => v.clone(),
22 other => VmValue::String(Rc::from(other.to_string())),
23 };
24
25 if let Some(handler) = self.exception_handlers.pop() {
26 if !handler.error_type.is_empty() {
27 let matches = match &thrown_value {
29 VmValue::EnumVariant { enum_name, .. } => {
30 enum_name.as_ref() == handler.error_type
31 }
32 _ => false,
33 };
34 if !matches {
35 return self.handle_error(error);
36 }
37 }
38
39 self.release_sync_guards_after_unwind(handler.frame_depth, handler.env_scope_depth);
40
41 while self.frames.len() > handler.frame_depth {
42 if let Some(frame) = self.frames.pop() {
43 if let Some(ref dir) = frame.saved_source_dir {
44 crate::stdlib::set_thread_source_dir(dir);
45 }
46 self.iterators.truncate(frame.saved_iterator_depth);
47 self.env = frame.saved_env;
48 }
49 }
50
51 while self
53 .deadlines
54 .last()
55 .is_some_and(|d| d.1 > handler.frame_depth)
56 {
57 self.deadlines.pop();
58 }
59
60 self.env.truncate_scopes(handler.env_scope_depth);
61
62 self.stack.truncate(handler.stack_depth);
63 self.stack.push(thrown_value);
64
65 if let Some(frame) = self.frames.last_mut() {
66 frame.ip = handler.catch_ip;
67 }
68
69 Ok(None)
70 } else {
71 Err(error)
72 }
73 }
74
75 pub(crate) async fn run_chunk(&mut self, chunk: &Chunk) -> Result<VmValue, VmError> {
76 self.run_chunk_entry(chunk, 0, None, None, None, None).await
77 }
78
79 pub(crate) async fn run_chunk_entry(
80 &mut self,
81 chunk: &Chunk,
82 argc: usize,
83 saved_source_dir: Option<std::path::PathBuf>,
84 module_functions: Option<ModuleFunctionRegistry>,
85 module_state: Option<crate::value::ModuleState>,
86 local_slots: Option<Vec<LocalSlot>>,
87 ) -> Result<VmValue, VmError> {
88 self.run_chunk_ref(
89 Rc::new(chunk.clone()),
90 argc,
91 saved_source_dir,
92 module_functions,
93 module_state,
94 local_slots,
95 )
96 .await
97 }
98
99 pub(crate) async fn run_chunk_ref(
100 &mut self,
101 chunk: ChunkRef,
102 argc: usize,
103 saved_source_dir: Option<std::path::PathBuf>,
104 module_functions: Option<ModuleFunctionRegistry>,
105 module_state: Option<crate::value::ModuleState>,
106 local_slots: Option<Vec<LocalSlot>>,
107 ) -> Result<VmValue, VmError> {
108 let initial_env = self.env.clone();
109 let local_slots = local_slots.unwrap_or_else(|| Self::fresh_local_slots(&chunk));
110 let initial_local_slots = local_slots.clone();
111 self.frames.push(CallFrame {
112 chunk,
113 ip: 0,
114 stack_base: self.stack.len(),
115 saved_env: self.env.clone(),
116 initial_env: Some(initial_env),
117 initial_local_slots: Some(initial_local_slots),
118 saved_iterator_depth: self.iterators.len(),
119 fn_name: String::new(),
120 argc,
121 saved_source_dir,
122 module_functions,
123 module_state,
124 local_slots,
125 local_scope_base: self.env.scope_depth().saturating_sub(1),
126 local_scope_depth: 0,
127 });
128
129 loop {
130 if let Some(&(deadline, _)) = self.deadlines.last() {
131 if Instant::now() > deadline {
132 self.deadlines.pop();
133 let err = VmError::Thrown(VmValue::String(Rc::from("Deadline exceeded")));
134 match self.handle_error(err) {
135 Ok(None) => continue,
136 Ok(Some(val)) => return Ok(val),
137 Err(e) => return Err(e),
138 }
139 }
140 }
141
142 let frame = match self.frames.last_mut() {
143 Some(f) => f,
144 None => return Ok(self.stack.pop().unwrap_or(VmValue::Nil)),
145 };
146
147 if frame.ip >= frame.chunk.code.len() {
148 let val = self.stack.pop().unwrap_or(VmValue::Nil);
149 self.release_sync_guards_for_frame(self.frames.len());
150 let popped_frame = self.frames.pop().unwrap();
151 if let Some(ref dir) = popped_frame.saved_source_dir {
152 crate::stdlib::set_thread_source_dir(dir);
153 }
154
155 if self.frames.is_empty() {
156 return Ok(val);
157 } else {
158 self.iterators.truncate(popped_frame.saved_iterator_depth);
159 self.env = popped_frame.saved_env;
160 self.stack.truncate(popped_frame.stack_base);
161 self.stack.push(val);
162 continue;
163 }
164 }
165
166 let op = frame.chunk.code[frame.ip];
167 frame.ip += 1;
168
169 match self.execute_op(op).await {
170 Ok(Some(val)) => return Ok(val),
171 Ok(None) => continue,
172 Err(VmError::Return(val)) => {
173 if let Some(popped_frame) = self.frames.pop() {
174 self.release_sync_guards_for_frame(self.frames.len() + 1);
175 if let Some(ref dir) = popped_frame.saved_source_dir {
176 crate::stdlib::set_thread_source_dir(dir);
177 }
178 let current_depth = self.frames.len();
179 self.exception_handlers
180 .retain(|h| h.frame_depth <= current_depth);
181
182 if self.frames.is_empty() {
183 return Ok(val);
184 }
185 self.iterators.truncate(popped_frame.saved_iterator_depth);
186 self.env = popped_frame.saved_env;
187 self.stack.truncate(popped_frame.stack_base);
188 self.stack.push(val);
189 } else {
190 return Ok(val);
191 }
192 }
193 Err(e) => {
194 if self.error_stack_trace.is_empty() {
196 self.error_stack_trace = self.capture_stack_trace();
197 }
198 match self.handle_error(e) {
199 Ok(None) => {
200 self.error_stack_trace.clear();
201 continue;
202 }
203 Ok(Some(val)) => return Ok(val),
204 Err(e) => return Err(self.enrich_error_with_line(e)),
205 }
206 }
207 }
208 }
209 }
210
211 pub(crate) async fn execute_one_cycle(&mut self) -> Result<Option<(VmValue, bool)>, VmError> {
212 if let Some(&(deadline, _)) = self.deadlines.last() {
213 if Instant::now() > deadline {
214 self.deadlines.pop();
215 let err = VmError::Thrown(VmValue::String(Rc::from("Deadline exceeded")));
216 match self.handle_error(err) {
217 Ok(None) => return Ok(None),
218 Ok(Some(val)) => return Ok(Some((val, false))),
219 Err(e) => return Err(e),
220 }
221 }
222 }
223
224 let frame = match self.frames.last_mut() {
225 Some(f) => f,
226 None => {
227 let val = self.stack.pop().unwrap_or(VmValue::Nil);
228 return Ok(Some((val, false)));
229 }
230 };
231
232 if frame.ip >= frame.chunk.code.len() {
233 let val = self.stack.pop().unwrap_or(VmValue::Nil);
234 self.release_sync_guards_for_frame(self.frames.len());
235 let popped_frame = self.frames.pop().unwrap();
236 if self.frames.is_empty() {
237 return Ok(Some((val, false)));
238 } else {
239 self.iterators.truncate(popped_frame.saved_iterator_depth);
240 self.env = popped_frame.saved_env;
241 self.stack.truncate(popped_frame.stack_base);
242 self.stack.push(val);
243 return Ok(None);
244 }
245 }
246
247 let op = frame.chunk.code[frame.ip];
248 frame.ip += 1;
249
250 match self.execute_op(op).await {
251 Ok(Some(val)) => Ok(Some((val, false))),
252 Ok(None) => Ok(None),
253 Err(VmError::Return(val)) => {
254 if let Some(popped_frame) = self.frames.pop() {
255 self.release_sync_guards_for_frame(self.frames.len() + 1);
256 if let Some(ref dir) = popped_frame.saved_source_dir {
257 crate::stdlib::set_thread_source_dir(dir);
258 }
259 let current_depth = self.frames.len();
260 self.exception_handlers
261 .retain(|h| h.frame_depth <= current_depth);
262 if self.frames.is_empty() {
263 return Ok(Some((val, false)));
264 }
265 self.iterators.truncate(popped_frame.saved_iterator_depth);
266 self.env = popped_frame.saved_env;
267 self.stack.truncate(popped_frame.stack_base);
268 self.stack.push(val);
269 Ok(None)
270 } else {
271 Ok(Some((val, false)))
272 }
273 }
274 Err(e) => {
275 if self.error_stack_trace.is_empty() {
276 self.error_stack_trace = self.capture_stack_trace();
277 }
278 match self.handle_error(e) {
279 Ok(None) => {
280 self.error_stack_trace.clear();
281 Ok(None)
282 }
283 Ok(Some(val)) => Ok(Some((val, false))),
284 Err(e) => Err(self.enrich_error_with_line(e)),
285 }
286 }
287 }
288 }
289
290 pub(crate) fn capture_stack_trace(&self) -> Vec<(String, usize, usize, Option<String>)> {
292 self.frames
293 .iter()
294 .map(|f| {
295 let idx = if f.ip > 0 { f.ip - 1 } else { 0 };
296 let line = f.chunk.lines.get(idx).copied().unwrap_or(0) as usize;
297 let col = f.chunk.columns.get(idx).copied().unwrap_or(0) as usize;
298 (f.fn_name.clone(), line, col, f.chunk.source_file.clone())
299 })
300 .collect()
301 }
302
303 pub(crate) fn enrich_error_with_line(&self, error: VmError) -> VmError {
307 let line = self
309 .error_stack_trace
310 .last()
311 .map(|(_, l, _, _)| *l)
312 .unwrap_or_else(|| self.current_line());
313 if line == 0 {
314 return error;
315 }
316 let suffix = format!(" (line {line})");
317 match error {
318 VmError::Runtime(msg) => VmError::Runtime(format!("{msg}{suffix}")),
319 VmError::TypeError(msg) => VmError::TypeError(format!("{msg}{suffix}")),
320 VmError::DivisionByZero => VmError::Runtime(format!("Division by zero{suffix}")),
321 VmError::UndefinedVariable(name) => {
322 VmError::Runtime(format!("Undefined variable: {name}{suffix}"))
323 }
324 VmError::UndefinedBuiltin(name) => {
325 VmError::Runtime(format!("Undefined builtin: {name}{suffix}"))
326 }
327 VmError::ImmutableAssignment(name) => VmError::Runtime(format!(
328 "Cannot assign to immutable binding: {name}{suffix}"
329 )),
330 VmError::StackOverflow => {
331 VmError::Runtime(format!("Stack overflow: too many nested calls{suffix}"))
332 }
333 other => other,
339 }
340 }
341}