85 lines
2.7 KiB
Rust
85 lines
2.7 KiB
Rust
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::<i32>()
|
|
.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::<AttrSet>()
|
|
.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(())
|
|
}
|