Files
nix-js/fix-vm/src/instructions/with_scope.rs
T

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(())
}