MCPcopy Create free account
hub / github.com/dylan-sutton-chavez/edge-python / exec_call

Method exec_call

compiler/src/modules/vm/handlers/function.rs:184–279  ·  view source on GitHub ↗

`Call` orchestrator. Only user `Func` callees build a fresh `fn_slots` and run the body inline; every other callee kind short-circuits in `try_dispatch_non_func_callable`. */

(&mut self, operand: u16, chunk: &SSAChunk, slots: &mut [Val])

Source from the content-addressed store, hash-verified

182
183 /* `Call` orchestrator. Only user `Func` callees build a fresh `fn_slots` and run the body inline; every other callee kind short-circuits in `try_dispatch_non_func_callable`. */
184 pub(crate) fn exec_call(&mut self, operand: u16, chunk: &SSAChunk, slots: &mut [Val]) -> Result<(), VmErr> {
185 let (positional, kw_flat, _num_pos, num_kw) = self.parse_call_args(operand)?;
186
187 if self.depth >= self.max_calls { return Err(cold_depth()); }
188 // Charge each call so wide recursion is op-budget bounded.
189 self.charge_step()?;
190
191 let callee = self.pop()?;
192 if !callee.is_heap() { return Err(cold_type("object is not callable")); }
193
194 if self.try_dispatch_non_func_callable(callee, &positional, &kw_flat, num_kw, chunk, slots)? {
195 return Ok(());
196 }
197
198 // Snapshot defaults/captures once, both are tiny (<10), and cloning beats the 3+ heap re-reads later phases would do. Back-prop still uses `get_mut` since it writes.
199 let (fi, defaults, captures) = match self.heap.get(callee) {
200 HeapObj::Func(i, d, c) => (*i, d.clone(), c.clone()),
201 _ => return Err(cold_type("object is not callable")),
202 };
203
204 // Pure-call memoisation. Disabled under impure outer frames (stale-view risk) or kwargs (cache key only spans positionals).
205 let outer_impure = self.observed_impure.last().copied().unwrap_or(false);
206 if num_kw == 0 && !outer_impure
207 && let Some(cached) = self.templates.lookup(fi, &positional, &self.heap) {
208 self.push(cached);
209 return Ok(());
210 }
211
212 self.depth += 1;
213 let (_params, body, _, _) = self.functions[fi];
214 let mut fn_slots = self.slot_templates[fi].clone();
215
216 self.bind_function_args(fi, &defaults, &captures, &positional, &kw_flat, &mut fn_slots)?;
217
218 if self.needs_caller_slots[fi] {
219 self.apply_caller_slot_propagation(fi, &captures, chunk, slots, &mut fn_slots);
220 }
221
222 self.bind_self_reference(fi, callee, &mut fn_slots);
223
224 // Generator/coroutine: return a suspended Coroutine instead of running. Both flags are O(1).
225 let is_async_fn = self.is_async.get(fi).copied().unwrap_or(false);
226 if is_async_fn || body.is_generator {
227 let coro = self.heap.alloc(HeapObj::Coroutine(0, fn_slots, Vec::new(), BodyRef::Fn(fi), Vec::new(), Vec::new(), Vec::new()))?;
228 self.push(coro);
229 self.depth -= 1;
230 return Ok(());
231 }
232
233 // Snapshot caller-visible depths so we can split the helper's stack/iter/exception contributions out if it suspends mid-body via a yielding builtin.
234 let stack_base = self.stack.len();
235 let iter_base = self.iter_stack.len();
236 let exc_base = self.exception_stack.len();
237 let yields_before = self.yields.len();
238 let (callee_impure, exec_result) = self.run_body_with_frame(fi, body, chunk, &mut fn_slots, slots);
239 self.depth -= 1;
240
241 self.back_propagate_nonlocals(fi, body, callee, chunk, slots, &fn_slots);

Callers 13

exec_instMethod · 0.80
exec_call_methodMethod · 0.80
exec_store_attrMethod · 0.80
try_call_dunderMethod · 0.80
handle_functionMethod · 0.80
handle_load_attrMethod · 0.80
call_minmaxMethod · 0.80
sort_by_keyMethod · 0.80
call_iterMethod · 0.80
call_mapMethod · 0.80
call_filterMethod · 0.80

Calls 15

cold_depthFunction · 0.85
cold_typeFunction · 0.85
parse_call_argsMethod · 0.80
charge_stepMethod · 0.80
is_heapMethod · 0.80
lookupMethod · 0.80
pushMethod · 0.80
bind_function_argsMethod · 0.80
bind_self_referenceMethod · 0.80
run_body_with_frameMethod · 0.80

Tested by

no test coverage detected