fix(tvix/eval): ensure OpResolveWith can be traced

The previous implementation of OpResolveWith manually controlled the
loop iteration, which skipped over the disassembler's tracing
instruction.

Instead, the resolution of dynamic variables has been delegated to a
new helper function. This has the additional benefit that the loop
labels are no longer required, making things a bit cleaner.

Change-Id: If22b74c3d49c74bf3a1ec4497cb761a9ee6cf2a4
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6298
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
Vincent Ambo 2022-08-27 03:17:18 +03:00 committed by tazjin
parent 7c2fdefcd8
commit 10b0879c00

View file

@ -126,7 +126,7 @@ impl VM {
#[cfg(feature = "disassembler")]
let mut tracer = Tracer::new();
'dispatch: loop {
loop {
if self.frame().ip == self.chunk().code.len() {
// If this is the end of the top-level function,
// return, otherwise pop the call frame.
@ -330,21 +330,8 @@ impl VM {
OpCode::OpResolveWith => {
let ident = self.pop().to_string()?;
// Attempt to resolve the variable, starting at
// the back of the with_stack.
'with: for idx in self.with_stack.iter().rev() {
let with = self.stack[*idx].as_attrs()?;
match with.select(ident.as_str()) {
None => continue 'with,
Some(val) => {
self.push(val.clone());
continue 'dispatch;
}
}
}
return Err(ErrorKind::UnknownDynamicVariable(ident.to_string()).into());
let value = self.resolve_with(ident.as_str())?;
self.push(value)
}
OpCode::OpAssert => {
@ -450,6 +437,19 @@ impl VM {
self.push(Value::String(out.into()));
Ok(())
}
/// Resolve a dynamic identifier through the with-stack at runtime.
fn resolve_with(&self, ident: &str) -> EvalResult<Value> {
for idx in self.with_stack.iter().rev() {
let with = self.stack[*idx].as_attrs()?;
match with.select(ident) {
None => continue,
Some(val) => return Ok(val.clone()),
}
}
Err(ErrorKind::UnknownDynamicVariable(ident.to_string()).into())
}
}
// TODO: use Rc::unwrap_or_clone once it is stabilised.