document trying mechanism
This commit is contained in:
@@ -13,7 +13,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
if let (Some(ls), Some(rs)) = (ctx.get_string(lhs), ctx.get_string(rhs)) {
|
||||
let ns = Gc::new(mc, crate::NixString::new(format!("{ls}{rs}")));
|
||||
self.push(Value::new_gc(ns));
|
||||
@@ -47,7 +47,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
int_op: fn(i64, i64) -> i64,
|
||||
float_op: fn(f64, f64) -> f64,
|
||||
) -> Step {
|
||||
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let res = numeric_binop(lhs, rhs, mc, int_op, float_op);
|
||||
match res {
|
||||
Ok(val) => {
|
||||
@@ -60,7 +60,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn op_div(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
||||
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
match (get_num(lhs), get_num(rhs)) {
|
||||
(_, Some(NixNum::Int(0))) | (_, Some(NixNum::Float(0.))) => {
|
||||
return self.finish_vm_err(VmError::Uncatchable(fix_error::Error::eval_error(
|
||||
@@ -86,7 +86,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let eq = match self.values_equal(ctx, lhs, rhs) {
|
||||
Ok(eq) => eq,
|
||||
Err(e) => return self.finish_vm_err(e),
|
||||
@@ -102,7 +102,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let eq = match self.values_equal(ctx, lhs, rhs) {
|
||||
Ok(eq) => eq,
|
||||
Err(e) => return self.finish_vm_err(e),
|
||||
@@ -158,7 +158,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
mc: &Mutation<'gc>,
|
||||
pred: fn(Ordering) -> bool,
|
||||
) -> Step {
|
||||
let (lhs, rhs) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let (lhs, rhs) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
match self.compare_values_inner(ctx, pred, lhs, rhs) {
|
||||
Ok(()) => Step::Continue(()),
|
||||
Err(e) => self.finish_vm_err(e),
|
||||
@@ -171,7 +171,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let (l, r) = self.try_force::<(Gc<List>, Gc<List>)>(reader, mc)?;
|
||||
let (l, r) = self.force_and_retry::<(Gc<List>, Gc<List>)>(reader, mc)?;
|
||||
let mut items = smallvec::SmallVec::new();
|
||||
items.extend_from_slice(&l.inner.borrow());
|
||||
items.extend_from_slice(&r.inner.borrow());
|
||||
@@ -190,14 +190,14 @@ impl<'gc> crate::Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let (l, r) = self.try_force::<(Gc<AttrSet>, Gc<AttrSet>)>(reader, mc)?;
|
||||
let (l, r) = self.force_and_retry::<(Gc<AttrSet>, Gc<AttrSet>)>(reader, mc)?;
|
||||
self.push(Value::new_gc(l.merge(&r, mc)));
|
||||
Step::Continue(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn op_neg(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
||||
let rhs = self.try_force::<NixNum>(reader, mc)?;
|
||||
let rhs = self.force_and_retry::<NixNum>(reader, mc)?;
|
||||
match rhs {
|
||||
NixNum::Int(int) => self.push(Value::make_int(-int, mc)),
|
||||
NixNum::Float(float) => self.push(Value::new_float(-float)),
|
||||
@@ -207,7 +207,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn op_not(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
||||
let rhs = self.try_force::<bool>(reader, mc)?;
|
||||
let rhs = self.force_and_retry::<bool>(reader, mc)?;
|
||||
self.push(Value::new_inline(!rhs));
|
||||
Step::Continue(())
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ impl<'gc> Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let ret = self.try_force::<bool>(reader, mc)?;
|
||||
let ret = self.force_and_retry::<bool>(reader, mc)?;
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let idx = self.peek(1).as_inline::<i32>().unwrap();
|
||||
#[allow(clippy::unwrap_used)]
|
||||
@@ -368,7 +368,7 @@ impl<'gc> Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let val = self.try_force::<StrictValue>(reader, mc)?;
|
||||
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||
self.finish_ok(ctx.convert_value(val.relax()))
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ impl<'gc> Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let (func, attrset) = self.try_force::<(Gc<Closure>, Gc<AttrSet>)>(reader, mc)?;
|
||||
let (func, attrset) = self.force_and_retry::<(Gc<Closure>, Gc<AttrSet>)>(reader, mc)?;
|
||||
|
||||
let Closure {
|
||||
ip,
|
||||
|
||||
@@ -17,7 +17,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
arg: Value<'gc>,
|
||||
resume_pc: usize,
|
||||
) -> Step {
|
||||
let func = self.try_force::<StrictValue>(reader, mc)?;
|
||||
let func = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||
if self.call_depth > 10000 {
|
||||
return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
||||
}
|
||||
@@ -116,7 +116,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
let val = self.try_force::<StrictValue>(reader, mc)?;
|
||||
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||
let Some(CallFrame {
|
||||
pc: ret_pc,
|
||||
stack_depth,
|
||||
|
||||
@@ -75,7 +75,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
let _span_id = reader.read_u32();
|
||||
let key = reader.read_string_id();
|
||||
|
||||
let attrset = self.try_force::<Gc<AttrSet>>(reader, mc)?;
|
||||
let attrset = self.force_and_retry::<Gc<AttrSet>>(reader, mc)?;
|
||||
|
||||
match attrset.lookup(key) {
|
||||
Some(v) => {
|
||||
@@ -95,7 +95,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
) -> Step {
|
||||
let _span_id = reader.read_u32();
|
||||
|
||||
let (attrset, key_val) = self.try_force::<(Gc<AttrSet>, StrictValue)>(reader, mc)?;
|
||||
let (attrset, key_val) = self.force_and_retry::<(Gc<AttrSet>, StrictValue)>(reader, mc)?;
|
||||
|
||||
let key_sid = match ctx.get_string_id(key_val) {
|
||||
Ok(id) => id,
|
||||
@@ -191,7 +191,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
let _span_id = reader.read_u32();
|
||||
let key = reader.read_string_id();
|
||||
|
||||
let current = self.try_force::<StrictValue>(reader, mc)?;
|
||||
let current = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||
|
||||
match current
|
||||
.as_gc::<AttrSet>()
|
||||
@@ -214,7 +214,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
) -> Step {
|
||||
let _span_id = reader.read_u32();
|
||||
|
||||
let (current, key_val) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let (current, key_val) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
|
||||
let key_sid = match ctx.get_string_id(key_val) {
|
||||
Ok(id) => id,
|
||||
@@ -254,7 +254,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
mc: &gc_arena::Mutation<'gc>,
|
||||
) -> Step {
|
||||
let key = reader.read_string_id();
|
||||
let current = self.try_force::<StrictValue>(reader, mc)?;
|
||||
let current = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||
|
||||
self.push(Value::new_inline(
|
||||
current
|
||||
@@ -275,7 +275,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &gc_arena::Mutation<'gc>,
|
||||
) -> Step {
|
||||
let (current, dyn_key) = self.try_force::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
let (current, dyn_key) = self.force_and_retry::<(StrictValue, StrictValue)>(reader, mc)?;
|
||||
|
||||
let key_sid = match ctx.get_string_id(dyn_key) {
|
||||
Ok(id) => id,
|
||||
|
||||
@@ -9,7 +9,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
mc: &gc_arena::Mutation<'gc>,
|
||||
) -> Step {
|
||||
let offset = reader.read_i32();
|
||||
let cond = self.try_force::<StrictValue>(reader, mc)?;
|
||||
let cond = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||
if cond.as_inline::<bool>() == Some(false) {
|
||||
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
mc: &gc_arena::Mutation<'gc>,
|
||||
) -> Step {
|
||||
let offset = reader.read_i32();
|
||||
let cond = self.try_force::<StrictValue>(reader, mc)?;
|
||||
let cond = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||
if cond.as_inline::<bool>() == Some(true) {
|
||||
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &gc_arena::Mutation<'gc>,
|
||||
) -> Step {
|
||||
let val = self.try_force::<StrictValue>(reader, mc)?;
|
||||
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||
if val.is::<StringId>() || val.is::<NixString>() {
|
||||
self.push(val.relax());
|
||||
} else {
|
||||
|
||||
@@ -66,7 +66,7 @@ impl<'gc> crate::Vm<'gc> {
|
||||
)));
|
||||
};
|
||||
self.push(env);
|
||||
let env = self.try_force::<Gc<AttrSet>>(reader, mc)?;
|
||||
let env = self.force_and_retry::<Gc<AttrSet>>(reader, mc)?;
|
||||
let Some(val) = env.lookup(name) else {
|
||||
reader.set_pc(reader.inst_start_pc());
|
||||
self.with_env = prev;
|
||||
|
||||
+31
-3
@@ -420,17 +420,45 @@ impl<'gc> Vm<'gc> {
|
||||
.expect("forced")
|
||||
}
|
||||
|
||||
/// Force the top `T::WIDTH` stack slots and return them as `T`.
|
||||
///
|
||||
/// If any slot holds a pending thunk, this method pushes a call frame
|
||||
/// whose resume PC is the **start of the current instruction**
|
||||
/// (`reader.inst_start_pc()`), enters the thunk, and returns
|
||||
/// `Break::Force`. When the thunk eventually returns, the VM will
|
||||
/// **re-execute the entire opcode handler from the beginning**.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// * **Do not call this method more than once in a single handler.**
|
||||
/// If you need to force multiple values, use a tuple type such as
|
||||
/// `(StrictValue, StrictValue)` so they are forced and popped in one
|
||||
/// atomic operation. Calling `force_and_retry` twice (or more)
|
||||
/// means the handler will be re-run from the top after each retry;
|
||||
/// any stack modifications between the two calls would be duplicated
|
||||
/// and corrupt the stack layout.
|
||||
///
|
||||
/// * The caller must ensure that the stack layout at the point of
|
||||
/// invocation is **identical** every time the handler is re-entered.
|
||||
/// In practice this means no pushes, pops, or local mutations may
|
||||
/// happen before the call, and the call must be the first thing
|
||||
/// that consumes the instruction's operand values.
|
||||
///
|
||||
/// * The return value must be propagated with `?` so that
|
||||
/// `Break::Force` correctly unwinds to the dispatch loop.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_force<T: Forced<'gc>>(
|
||||
pub(crate) fn force_and_retry<T: Forced<'gc>>(
|
||||
&mut self,
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> std::ops::ControlFlow<Break, T> {
|
||||
self.try_force_to_pc(reader, mc, reader.inst_start_pc())
|
||||
self.force_and_retry_pc(reader, mc, reader.inst_start_pc())
|
||||
}
|
||||
|
||||
/// Same as [`force_and_retry`](Self::force_and_retry) but allows
|
||||
/// specifying a custom resume PC.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_force_to_pc<T: Forced<'gc>>(
|
||||
pub(crate) fn force_and_retry_pc<T: Forced<'gc>>(
|
||||
&mut self,
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
|
||||
Reference in New Issue
Block a user