use fix_error::Error; use gc_arena::{Gc, Mutation, RefLock}; use crate::{BytecodeReader, CallFrame, Closure, Env, StepResult, ThunkState, VmContextExt}; impl<'gc> crate::Vm<'gc> { #[inline(always)] pub(crate) fn op_call( &mut self, ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> StepResult<'gc> { if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) { return step; } if self.call_depth > 10000 { return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded")); } self.call_depth += 1; let func = self.pop_stack(); let arg = reader.read_operand_data(ctx).resolve(mc, self); 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: reader.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 { todo!("call other types: {func:?}") } StepResult::Continue } #[inline(always)] pub(crate) fn op_return( &mut self, ctx: &mut impl crate::VmContext, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> StepResult<'gc> { self.handle_return(reader, ctx, mc) } pub(crate) fn handle_return( &mut self, reader: &mut BytecodeReader<'_>, ctx: &C, mc: &Mutation<'gc>, ) -> StepResult<'gc> { let ret_inst_pc = reader.pc() - 1; let Some(CallFrame { pc: ret_pc, stack_depth, thunk, env, with_env, }) = self.call_stack.pop() else { let val = self.pop_stack(); return self.finish_ok(ctx.convert_value(val)); }; reader.set_pc(ret_pc); if let Some(outer_thunk) = thunk { let val = self.pop_stack(); match val.restrict() { Ok(val) => { *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); if reader.bytecode().get(ret_pc).copied() == Some(fix_codegen::Op::Return as u8) { self.push_stack(val.relax()); } } Err(inner_thunk) => { let mut state = inner_thunk.borrow_mut(mc); match *state { ThunkState::Pending { ip: inner_ip, env: inner_env, with_env: inner_with_env, } => { self.call_stack.push(CallFrame { pc: ret_pc, stack_depth, thunk: Some(outer_thunk), env, with_env, }); self.call_stack.push(CallFrame { pc: ret_inst_pc, stack_depth: 0, thunk: Some(inner_thunk), env: inner_env, with_env: inner_with_env, }); *state = ThunkState::Blackhole; reader.set_pc(inner_ip); self.env = inner_env; self.with_env = inner_with_env; return StepResult::Continue; } ThunkState::Evaluated(val) => { *outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val); if reader.bytecode().get(ret_pc).copied() == Some(fix_codegen::Op::Return as u8) { self.push_stack(val.relax()); } } ThunkState::Apply { func: _, arg: _ } => todo!("force Apply thunk"), ThunkState::Blackhole => { return self .finish_err(Error::eval_error("infinite recursion encountered")); } } } } } else { self.call_depth -= 1; } self.env = env; self.with_env = with_env; StepResult::Continue } }