document trying mechanism
This commit is contained in:
@@ -13,7 +13,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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)) {
|
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}")));
|
let ns = Gc::new(mc, crate::NixString::new(format!("{ls}{rs}")));
|
||||||
self.push(Value::new_gc(ns));
|
self.push(Value::new_gc(ns));
|
||||||
@@ -47,7 +47,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
int_op: fn(i64, i64) -> i64,
|
int_op: fn(i64, i64) -> i64,
|
||||||
float_op: fn(f64, f64) -> f64,
|
float_op: fn(f64, f64) -> f64,
|
||||||
) -> Step {
|
) -> 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);
|
let res = numeric_binop(lhs, rhs, mc, int_op, float_op);
|
||||||
match res {
|
match res {
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
@@ -60,7 +60,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_div(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
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)) {
|
match (get_num(lhs), get_num(rhs)) {
|
||||||
(_, Some(NixNum::Int(0))) | (_, Some(NixNum::Float(0.))) => {
|
(_, Some(NixNum::Int(0))) | (_, Some(NixNum::Float(0.))) => {
|
||||||
return self.finish_vm_err(VmError::Uncatchable(fix_error::Error::eval_error(
|
return self.finish_vm_err(VmError::Uncatchable(fix_error::Error::eval_error(
|
||||||
@@ -86,7 +86,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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) {
|
let eq = match self.values_equal(ctx, lhs, rhs) {
|
||||||
Ok(eq) => eq,
|
Ok(eq) => eq,
|
||||||
Err(e) => return self.finish_vm_err(e),
|
Err(e) => return self.finish_vm_err(e),
|
||||||
@@ -102,7 +102,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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) {
|
let eq = match self.values_equal(ctx, lhs, rhs) {
|
||||||
Ok(eq) => eq,
|
Ok(eq) => eq,
|
||||||
Err(e) => return self.finish_vm_err(e),
|
Err(e) => return self.finish_vm_err(e),
|
||||||
@@ -158,7 +158,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
pred: fn(Ordering) -> bool,
|
pred: fn(Ordering) -> bool,
|
||||||
) -> Step {
|
) -> 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) {
|
match self.compare_values_inner(ctx, pred, lhs, rhs) {
|
||||||
Ok(()) => Step::Continue(()),
|
Ok(()) => Step::Continue(()),
|
||||||
Err(e) => self.finish_vm_err(e),
|
Err(e) => self.finish_vm_err(e),
|
||||||
@@ -171,7 +171,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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();
|
let mut items = smallvec::SmallVec::new();
|
||||||
items.extend_from_slice(&l.inner.borrow());
|
items.extend_from_slice(&l.inner.borrow());
|
||||||
items.extend_from_slice(&r.inner.borrow());
|
items.extend_from_slice(&r.inner.borrow());
|
||||||
@@ -190,14 +190,14 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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)));
|
self.push(Value::new_gc(l.merge(&r, mc)));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_neg(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
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 {
|
match rhs {
|
||||||
NixNum::Int(int) => self.push(Value::make_int(-int, mc)),
|
NixNum::Int(int) => self.push(Value::make_int(-int, mc)),
|
||||||
NixNum::Float(float) => self.push(Value::new_float(-float)),
|
NixNum::Float(float) => self.push(Value::new_float(-float)),
|
||||||
@@ -207,7 +207,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn op_not(&mut self, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>) -> Step {
|
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));
|
self.push(Value::new_inline(!rhs));
|
||||||
Step::Continue(())
|
Step::Continue(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let ret = self.try_force::<bool>(reader, mc)?;
|
let ret = self.force_and_retry::<bool>(reader, mc)?;
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
let idx = self.peek(1).as_inline::<i32>().unwrap();
|
let idx = self.peek(1).as_inline::<i32>().unwrap();
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
@@ -368,7 +368,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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()))
|
self.finish_ok(ctx.convert_value(val.relax()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +378,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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 {
|
let Closure {
|
||||||
ip,
|
ip,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
arg: Value<'gc>,
|
arg: Value<'gc>,
|
||||||
resume_pc: usize,
|
resume_pc: usize,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let func = self.try_force::<StrictValue>(reader, mc)?;
|
let func = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
if self.call_depth > 10000 {
|
if self.call_depth > 10000 {
|
||||||
return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let val = self.try_force::<StrictValue>(reader, mc)?;
|
let val = self.force_and_retry::<StrictValue>(reader, mc)?;
|
||||||
let Some(CallFrame {
|
let Some(CallFrame {
|
||||||
pc: ret_pc,
|
pc: ret_pc,
|
||||||
stack_depth,
|
stack_depth,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let key = reader.read_string_id();
|
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) {
|
match attrset.lookup(key) {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
@@ -95,7 +95,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
) -> Step {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
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) {
|
let key_sid = match ctx.get_string_id(key_val) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
@@ -191,7 +191,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
let _span_id = reader.read_u32();
|
let _span_id = reader.read_u32();
|
||||||
let key = reader.read_string_id();
|
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
|
match current
|
||||||
.as_gc::<AttrSet>()
|
.as_gc::<AttrSet>()
|
||||||
@@ -214,7 +214,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
) -> Step {
|
) -> Step {
|
||||||
let _span_id = reader.read_u32();
|
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) {
|
let key_sid = match ctx.get_string_id(key_val) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
@@ -254,7 +254,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let key = reader.read_string_id();
|
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(
|
self.push(Value::new_inline(
|
||||||
current
|
current
|
||||||
@@ -275,7 +275,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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) {
|
let key_sid = match ctx.get_string_id(dyn_key) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
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) {
|
if cond.as_inline::<bool>() == Some(false) {
|
||||||
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
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>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> Step {
|
||||||
let offset = reader.read_i32();
|
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) {
|
if cond.as_inline::<bool>() == Some(true) {
|
||||||
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
reader.set_pc(((reader.pc() as isize) + (offset as isize)) as usize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &gc_arena::Mutation<'gc>,
|
mc: &gc_arena::Mutation<'gc>,
|
||||||
) -> Step {
|
) -> 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>() {
|
if val.is::<StringId>() || val.is::<NixString>() {
|
||||||
self.push(val.relax());
|
self.push(val.relax());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
self.push(env);
|
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 {
|
let Some(val) = env.lookup(name) else {
|
||||||
reader.set_pc(reader.inst_start_pc());
|
reader.set_pc(reader.inst_start_pc());
|
||||||
self.with_env = prev;
|
self.with_env = prev;
|
||||||
|
|||||||
+31
-3
@@ -420,17 +420,45 @@ impl<'gc> Vm<'gc> {
|
|||||||
.expect("forced")
|
.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)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_force<T: Forced<'gc>>(
|
pub(crate) fn force_and_retry<T: Forced<'gc>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
) -> std::ops::ControlFlow<Break, T> {
|
) -> 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)]
|
#[inline(always)]
|
||||||
pub(crate) fn try_force_to_pc<T: Forced<'gc>>(
|
pub(crate) fn force_and_retry_pc<T: Forced<'gc>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
mc: &Mutation<'gc>,
|
mc: &Mutation<'gc>,
|
||||||
|
|||||||
Reference in New Issue
Block a user