use fix_builtins::PrimOpPhase; use fix_error::Error; use gc_arena::{Gc, Mutation, RefLock}; use crate::value::*; use crate::{ BytecodeReader, CallFrame, Closure, Env, ForceMode, Step, ThunkState, VmRuntimeCtx, VmRuntimeCtxExt, }; impl<'gc> crate::Vm<'gc> { #[inline(always)] pub(crate) fn call( &mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, arg: Value<'gc>, resume_pc: usize, ) -> Step { let func = self.try_force::(reader, mc)?; if self.call_depth > 10000 { return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded")); } self.call_depth += 1; if let Some(closure) = func.as_gc::() { let ip = closure.ip; let n_locals = closure.n_locals; let env = closure.env; if let Some(ref _pattern) = closure.pattern { todo!("pattern call") } else { let new_env = Gc::new(mc, RefLock::new(Env::with_arg(arg, n_locals, env))); self.call_stack.push(CallFrame { pc: resume_pc, stack_depth: 0, thunk: None, env: self.env, with_env: self.with_env, }); reader.set_pc(ip as usize); self.env = new_env; } } else if let Some(primop) = func.as_inline::() { if primop.arity == 1 { self.push(arg); self.call_stack.push(CallFrame { pc: resume_pc, stack_depth: 0, thunk: None, env: self.env, with_env: self.with_env, }); reader.set_pc(primop.dispatch_ip as usize) } else { let app = PrimOpApp { primop, arity: primop.arity - 1, args: [arg, Value::default(), Value::default()], }; self.push(Value::new_gc(Gc::new(mc, app))); } } else if let Some(app) = func.as_gc::() { if app.arity == 1 { for i in 0..app.primop.arity - 1 { self.push(app.args[i as usize]); } self.push(arg); self.call_stack.push(CallFrame { pc: resume_pc, stack_depth: 0, thunk: None, env: self.env, with_env: self.with_env, }); reader.set_pc(app.primop.dispatch_ip as usize) } else { let new_app = PrimOpApp { arity: app.arity - 1, ..*app }; self.push(Value::new_gc(Gc::new(mc, new_app))) } } else { todo!("call other types: {func:?}") } Step::Continue(()) } #[inline(always)] pub(crate) fn op_call( &mut self, ctx: &impl VmRuntimeCtx, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { let arg = reader.read_operand_data(ctx).resolve(mc, self); let pc = reader.pc(); self.call(reader, mc, arg, pc) } #[inline(always)] pub(crate) fn op_return( &mut self, ctx: &mut impl VmRuntimeCtx, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { let val = self.try_force::(reader, mc)?; let Some(CallFrame { pc: ret_pc, stack_depth, thunk, env, with_env, }) = self.call_stack.pop() else { match self.force_mode { ForceMode::AsIs => return self.finish_ok(ctx.convert_value(val.relax())), ForceMode::Shallow => { self.push(val.relax()); reader.set_pc(PrimOpPhase::ForceResultShallow.ip() as usize); return Step::Continue(()); } ForceMode::Deep => { self.push(val.relax()); self.push(val.relax()); self.call_stack.push(CallFrame { pc: PrimOpPhase::ForceResultDeepFinish.ip() as usize, stack_depth: 0, thunk: None, env: self.env, with_env: self.with_env, }); self.call_depth += 1; reader.set_pc(PrimOpPhase::DeepSeq.ip() as usize); return Step::Continue(()); } } }; reader.set_pc(ret_pc); if let Some(outer_thunk) = thunk { *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); self.replace(stack_depth, val.relax()); } else { self.call_depth -= 1; self.push(val.relax()) } self.env = env; self.with_env = with_env; Step::Continue(()) } }