use fix_error::Error; use fix_lang::Symbol; use fix_runtime::{resolve_operand, *}; use smallvec::SmallVec; use crate::{Break, BytecodeReader, CallFrame, Step, VmRuntimeCtx}; #[inline(always)] #[expect( clippy::indexing_slicing, clippy::cast_sign_loss, reason = "counter is a non-negative with-scope index in 0..n, staying within the namespaces vec of length n" )] pub(crate) fn op_lookup_with<'gc, M: Machine<'gc>>( m: &mut M, ctx: &mut impl VmRuntimeCtx, reader: &mut BytecodeReader<'_>, mc: &gc_arena::Mutation<'gc>, ) -> Step { let counter = m .peek_forced(0) .downcast::() .expect("stack slot must be an integer"); let name = reader.read_string_id(); let n = reader.read_u8(); let mut namespaces = SmallVec::<[_; 2]>::new(); for _ in 0..n { namespaces.push(resolve_operand(&reader.read_operand_data(), mc, ctx, m)); } let resume_pc = reader.inst_start_pc(); let namespace = match namespaces[counter as usize].restrict() { Ok(val) => val, Err(thunk) => { let mut state = thunk.borrow_mut(mc); match *state { ThunkState::Pending { ip, env } => { *state = ThunkState::Blackhole; m.push_call_frame(CallFrame { thunk: Some(thunk), pc: resume_pc, env: m.env(), depth: None, }); m.set_env(env); reader.set_pc(ip); return Step::Break(Break::Force); } ThunkState::Evaluated(v) => v, ThunkState::Apply { func, arg } => { m.push_call_frame(CallFrame { thunk: Some(thunk), pc: resume_pc, env: m.env(), depth: None, }); m.push(func); return m.call(reader, mc, arg, resume_pc); } ThunkState::Blackhole => { return m.finish_err(Error::eval_error("infinite recursion encountered")); } } } }; if let Some(val) = namespace .downcast::() .and_then(|attrs| attrs.lookup(name)) { m.replace(0, val); } else if counter + 1 == n as i32 { return m.finish_err(Error::eval_error(format!( "undefined variable '{}'", Symbol::from(ctx.resolve_string(name)) ))); } else { m.replace(0, Value::new(counter + 1)); reader.set_pc(resume_pc); } Step::Continue(()) }