1use shape_value::{Upvalue, VMError, ValueWord};
4
5use super::{CallFrame, ExecutionResult, VirtualMachine, task_scheduler};
6
7impl VirtualMachine {
8 pub fn execute_function_by_name(
13 &mut self,
14 name: &str,
15 args: Vec<ValueWord>,
16 ctx: Option<&mut shape_runtime::context::ExecutionContext>,
17 ) -> Result<ValueWord, VMError> {
18 let func_id = self
19 .program
20 .functions
21 .iter()
22 .position(|f| f.name == name)
23 .ok_or_else(|| VMError::RuntimeError(format!("Function '{}' not found", name)))?;
24
25 if !self.program.module_binding_names.is_empty() && !self.module_init_done {
28 self.reset();
29 self.execute(None)?;
30 self.module_init_done = true;
31 }
32
33 self.reset_stack();
36 self.ip = self.program.instructions.len();
37 self.call_function_with_nb_args(func_id as u16, &args)?;
38 self.execute(ctx)
39 }
40
41 pub fn execute_function_by_id(
46 &mut self,
47 func_id: u16,
48 args: Vec<ValueWord>,
49 ctx: Option<&mut shape_runtime::context::ExecutionContext>,
50 ) -> Result<ValueWord, VMError> {
51 self.reset();
52 self.ip = self.program.instructions.len();
53 self.call_function_with_nb_args(func_id, &args)?;
54 self.execute(ctx)
55 }
56
57 pub fn execute_closure(
62 &mut self,
63 function_id: u16,
64 upvalues: Vec<Upvalue>,
65 args: Vec<ValueWord>,
66 ctx: Option<&mut shape_runtime::context::ExecutionContext>,
67 ) -> Result<ValueWord, VMError> {
68 self.reset();
69 self.ip = self.program.instructions.len();
70 self.call_closure_with_nb_args(function_id, upvalues, &args)?;
71 self.execute(ctx)
72 }
73
74 pub fn execute_function_fast(
80 &mut self,
81 func_id: u16,
82 ctx: Option<&mut shape_runtime::context::ExecutionContext>,
83 ) -> Result<ValueWord, VMError> {
84 self.reset_minimal();
86 self.ip = self.program.instructions.len();
87 self.call_function_with_nb_args(func_id, &[])?;
88 self.execute_fast(ctx)
89 }
90
91 pub fn execute_function_with_named_args(
94 &mut self,
95 func_id: u16,
96 named_args: &[(String, ValueWord)],
97 ctx: Option<&mut shape_runtime::context::ExecutionContext>,
98 ) -> Result<ValueWord, VMError> {
99 let function = self
100 .program
101 .functions
102 .get(func_id as usize)
103 .ok_or(VMError::InvalidCall)?;
104
105 let mut args = vec![ValueWord::none(); function.arity as usize];
107 for (name, value) in named_args {
108 if let Some(idx) = function.param_names.iter().position(|p| p == name) {
109 if idx < args.len() {
110 args[idx] = value.clone();
111 }
112 }
113 }
114
115 self.reset_minimal();
116 self.ip = self.program.instructions.len();
117 self.call_function_with_nb_args(func_id, &args)?;
118 self.execute_fast(ctx)
119 }
120
121 pub fn resume(
126 &mut self,
127 value: ValueWord,
128 ctx: Option<&mut shape_runtime::context::ExecutionContext>,
129 ) -> Result<ExecutionResult, VMError> {
130 self.push_vw(value)?;
131 self.execute_with_suspend(ctx)
132 }
133
134 pub fn execute_with_async(
142 &mut self,
143 mut ctx: Option<&mut shape_runtime::context::ExecutionContext>,
144 ) -> Result<ValueWord, VMError> {
145 loop {
146 match self.execute_with_suspend(ctx.as_deref_mut())? {
147 ExecutionResult::Completed(value) => return Ok(value),
148 ExecutionResult::Suspended { future_id, .. } => {
149 let result = self.resolve_spawned_task(future_id)?;
151 self.push_vw(result)?;
153 }
155 }
156 }
157 }
158
159 fn resolve_spawned_task(&mut self, task_id: u64) -> Result<ValueWord, VMError> {
166 if let Some(task_scheduler::TaskStatus::Completed(val)) =
168 self.task_scheduler.get_result(task_id)
169 {
170 return Ok(val.clone());
171 }
172 if let Some(task_scheduler::TaskStatus::Cancelled) = self.task_scheduler.get_result(task_id)
173 {
174 return Err(VMError::RuntimeError(format!(
175 "Task {} was cancelled",
176 task_id
177 )));
178 }
179
180 let callable_nb = self.task_scheduler.take_callable(task_id).ok_or_else(|| {
182 VMError::RuntimeError(format!("No callable registered for task {}", task_id))
183 })?;
184
185 use shape_value::NanTag;
189
190 let result_nb = match callable_nb.tag() {
191 NanTag::Function => {
192 let func_id = callable_nb.as_function().ok_or(VMError::InvalidCall)?;
193 let saved_ip = self.ip;
194 let saved_sp = self.sp;
195
196 self.ip = self.program.instructions.len();
197 self.call_function_with_nb_args(func_id, &[])?;
198 let res = self.execute_fast(None);
199
200 self.ip = saved_ip;
201 for i in saved_sp..self.sp {
203 self.stack[i] = ValueWord::none();
204 }
205 self.sp = saved_sp;
206
207 res?
208 }
209 NanTag::Heap => {
210 if let Some((function_id, upvalues)) = callable_nb.as_closure() {
211 let upvalues = upvalues.to_vec();
212 let saved_ip = self.ip;
213 let saved_sp = self.sp;
214
215 self.ip = self.program.instructions.len();
216 self.call_closure_with_nb_args(function_id, upvalues, &[])?;
217 let res = self.execute_fast(None);
218
219 self.ip = saved_ip;
220 for i in saved_sp..self.sp {
221 self.stack[i] = ValueWord::none();
222 }
223 self.sp = saved_sp;
224
225 res?
226 } else {
227 callable_nb
229 }
230 }
231 _ => callable_nb,
233 };
234
235 self.task_scheduler.complete(task_id, result_nb.clone());
237
238 Ok(result_nb)
239 }
240
241 pub(crate) fn call_function_with_nb_args(
243 &mut self,
244 func_id: u16,
245 args: &[ValueWord],
246 ) -> Result<(), VMError> {
247 let function = self
248 .program
249 .functions
250 .get(func_id as usize)
251 .ok_or(VMError::InvalidCall)?;
252
253 if self.call_stack.len() >= self.config.max_call_depth {
254 return Err(VMError::StackOverflow);
255 }
256
257 let locals_count = function.locals_count as usize;
258 let param_count = function.arity as usize;
259 let entry_point = function.entry_point;
260
261 let bp = self.sp;
262 let needed = bp + locals_count;
263 if needed > self.stack.len() {
264 self.stack.resize_with(needed * 2 + 1, ValueWord::none);
265 }
266
267 for i in 0..param_count {
268 if i < locals_count {
269 self.stack[bp + i] = args.get(i).cloned().unwrap_or_else(ValueWord::none);
270 }
271 }
272
273 self.sp = needed;
274
275 let blob_hash = self.blob_hash_for_function(func_id);
276 let frame = CallFrame {
277 return_ip: self.ip,
278 base_pointer: bp,
279 locals_count,
280 function_id: Some(func_id),
281 upvalues: None,
282 blob_hash,
283 };
284 self.call_stack.push(frame);
285 self.ip = entry_point;
286 Ok(())
287 }
288
289 pub(crate) fn call_closure_with_nb_args(
291 &mut self,
292 func_id: u16,
293 upvalues: Vec<Upvalue>,
294 args: &[ValueWord],
295 ) -> Result<(), VMError> {
296 let function = self
297 .program
298 .functions
299 .get(func_id as usize)
300 .ok_or(VMError::InvalidCall)?;
301
302 if self.call_stack.len() >= self.config.max_call_depth {
303 return Err(VMError::StackOverflow);
304 }
305
306 let locals_count = function.locals_count as usize;
307 let captures_count = function.captures_count as usize;
308 let arity = function.arity as usize;
309 let entry_point = function.entry_point;
310
311 let bp = self.sp;
312 let needed = bp + locals_count;
313 if needed > self.stack.len() {
314 self.stack.resize_with(needed * 2 + 1, ValueWord::none);
315 }
316
317 for (i, upvalue) in upvalues.iter().enumerate() {
319 if i < locals_count {
320 self.stack[bp + i] = upvalue.get();
321 }
322 }
323
324 for (i, arg) in args.iter().enumerate() {
326 let local_idx = captures_count + i;
327 if local_idx < locals_count {
328 self.stack[bp + local_idx] = arg.clone();
329 }
330 }
331
332 for i in (captures_count + args.len())..arity.min(locals_count) {
334 self.stack[bp + i] = ValueWord::none();
335 }
336
337 self.sp = needed;
338
339 let blob_hash = self.blob_hash_for_function(func_id);
340 self.call_stack.push(CallFrame {
341 return_ip: self.ip,
342 base_pointer: bp,
343 locals_count,
344 function_id: Some(func_id),
345 upvalues: Some(upvalues),
346 blob_hash,
347 });
348
349 self.ip = entry_point;
350 Ok(())
351 }
352
353 pub(in crate::executor) fn call_value_immediate_nb(
357 &mut self,
358 callee: &ValueWord,
359 args: &[ValueWord],
360 ctx: Option<&mut shape_runtime::context::ExecutionContext>,
361 ) -> Result<ValueWord, VMError> {
362 use shape_value::NanTag;
363 let target_depth = self.call_stack.len();
364
365 match callee.tag() {
366 NanTag::Function => {
367 let func_id = callee.as_function().ok_or(VMError::InvalidCall)?;
368 self.call_function_with_nb_args(func_id, args)?;
369 }
370 NanTag::ModuleFunction => {
371 let func_id = callee.as_module_function().ok_or(VMError::InvalidCall)?;
372 let module_fn = self.module_fn_table.get(func_id).cloned().ok_or_else(|| {
373 VMError::RuntimeError(format!(
374 "Module function ID {} not found in registry",
375 func_id
376 ))
377 })?;
378 let args_vec: Vec<ValueWord> = args.to_vec();
379 let result_nb = self.invoke_module_fn(&module_fn, &args_vec)?;
380 return Ok(result_nb);
381 }
382 NanTag::Heap => match callee.as_heap_ref() {
383 Some(shape_value::HeapValue::Closure {
384 function_id,
385 upvalues,
386 }) => {
387 self.call_closure_with_nb_args(*function_id, upvalues.clone(), args)?;
388 }
389 Some(shape_value::HeapValue::HostClosure(callable)) => {
390 let args_vec: Vec<ValueWord> = args.to_vec();
391 let result_nb = callable.call(&args_vec).map_err(VMError::RuntimeError)?;
392 return Ok(result_nb);
393 }
394 _ => return Err(VMError::InvalidCall),
395 },
396 _ => return Err(VMError::InvalidCall),
397 }
398
399 self.execute_until_call_depth(target_depth, ctx)?;
400 self.pop_vw()
401 }
402
403 pub(crate) fn call_function_from_stack(
410 &mut self,
411 func_id: u16,
412 arg_count: usize,
413 ) -> Result<(), VMError> {
414 let function = self
415 .program
416 .functions
417 .get(func_id as usize)
418 .ok_or(VMError::InvalidCall)?;
419
420 if self.call_stack.len() >= self.config.max_call_depth {
421 return Err(VMError::StackOverflow);
422 }
423
424 let locals_count = function.locals_count as usize;
425 let entry_point = function.entry_point;
426 let arity = function.arity as usize;
427
428 let bp = self.sp.saturating_sub(arg_count);
432
433 let needed = bp + locals_count;
435 if needed > self.stack.len() {
436 self.stack.resize_with(needed * 2 + 1, ValueWord::none);
437 }
438
439 let copy_count = arg_count.min(arity).min(locals_count);
442 for i in copy_count..locals_count {
443 self.stack[bp + i] = ValueWord::none();
444 }
445
446 self.sp = needed;
448
449 let blob_hash = self.blob_hash_for_function(func_id);
450 self.call_stack.push(CallFrame {
451 return_ip: self.ip,
452 base_pointer: bp,
453 locals_count,
454 function_id: Some(func_id),
455 upvalues: None,
456 blob_hash,
457 });
458 self.ip = entry_point;
459 Ok(())
460 }
461}