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