fix-vm: use Machine trait exclusively
This commit is contained in:
+81
-199
@@ -137,45 +137,20 @@ impl<'gc> Vm<'gc> {
|
||||
functor_sym: ctx.intern_string("__functor"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn finish_ok(&mut self, val: fix_lang::Value) -> Step {
|
||||
self.result = Some(Ok(val));
|
||||
Step::Break(Break::Done)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn finish_err(&mut self, err: Box<Error>) -> Step {
|
||||
self.result = Some(Err(err));
|
||||
Step::Break(Break::Done)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step {
|
||||
self.result = Some(Err(Error::eval_error(format!(
|
||||
"expected {expected}, got {got}"
|
||||
))));
|
||||
Step::Break(Break::Done)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn finish_vm_err(&mut self, err: VmError) -> Step {
|
||||
self.finish_err(err.into_error())
|
||||
}
|
||||
|
||||
impl<'gc> Machine<'gc> for Vm<'gc> {
|
||||
#[inline(always)]
|
||||
fn push(&mut self, val: Value<'gc>) {
|
||||
self.stack.push(val);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn pop(&mut self) -> Value<'gc> {
|
||||
self.stack.pop().expect("stack underflow")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn peek(&self, depth: usize) -> Value<'gc> {
|
||||
*self
|
||||
.stack
|
||||
@@ -184,7 +159,6 @@ impl<'gc> Vm<'gc> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn peek_forced(&self, depth: usize) -> StrictValue<'gc> {
|
||||
self.stack
|
||||
.get(self.stack.len() - depth - 1)
|
||||
@@ -193,6 +167,15 @@ impl<'gc> Vm<'gc> {
|
||||
.expect("forced")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn pop_forced(&mut self) -> StrictValue<'gc> {
|
||||
self.stack
|
||||
.pop()
|
||||
.expect("stack underflow")
|
||||
.restrict()
|
||||
.expect("forced")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn replace(&mut self, depth: usize, val: Value<'gc>) {
|
||||
let len = self.stack.len();
|
||||
@@ -203,72 +186,13 @@ impl<'gc> Vm<'gc> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
fn pop_forced(&mut self) -> StrictValue<'gc> {
|
||||
self.stack
|
||||
.pop()
|
||||
.expect("stack underflow")
|
||||
.restrict()
|
||||
.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)]
|
||||
fn force_and_retry<T: Forced<'gc>>(
|
||||
&mut self,
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> std::ops::ControlFlow<Break, T> {
|
||||
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)]
|
||||
fn force_and_retry_pc<T: Forced<'gc>>(
|
||||
&mut self,
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
resume_pc: usize,
|
||||
) -> std::ops::ControlFlow<Break, T> {
|
||||
T::force_and_check(self, reader, mc, 0, resume_pc)?;
|
||||
std::ops::ControlFlow::Continue(T::pop_converted(self))
|
||||
fn drop_n(&mut self, depth: usize) {
|
||||
self.stack.truncate(self.stack.len() - depth);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[allow(unused)]
|
||||
fn force_slot(
|
||||
&mut self,
|
||||
depth: usize,
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Step {
|
||||
self.force_slot_to_pc(depth, reader, mc, reader.inst_start_pc())
|
||||
fn stack_len(&self) -> usize {
|
||||
self.stack.len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -313,54 +237,6 @@ impl<'gc> Vm<'gc> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Machine<'gc> for Vm<'gc> {
|
||||
#[inline(always)]
|
||||
fn push(&mut self, val: Value<'gc>) {
|
||||
self.push(val);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn pop(&mut self) -> Value<'gc> {
|
||||
self.pop()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn peek(&self, depth: usize) -> Value<'gc> {
|
||||
Vm::peek(self, depth)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn peek_forced(&self, depth: usize) -> StrictValue<'gc> {
|
||||
Vm::peek_forced(self, depth)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn pop_forced(&mut self) -> StrictValue<'gc> {
|
||||
self.pop_forced()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn replace(&mut self, depth: usize, val: Value<'gc>) {
|
||||
self.replace(depth, val);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn stack_len(&self) -> usize {
|
||||
self.stack.len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn force_slot_to_pc(
|
||||
&mut self,
|
||||
depth: usize,
|
||||
reader: &mut BytecodeReader<'_>,
|
||||
mc: &Mutation<'gc>,
|
||||
resume_pc: usize,
|
||||
) -> Step {
|
||||
self.force_slot_to_pc(depth, reader, mc, resume_pc)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn call(
|
||||
@@ -370,7 +246,7 @@ impl<'gc> Machine<'gc> for Vm<'gc> {
|
||||
arg: Value<'gc>,
|
||||
resume_pc: usize,
|
||||
) -> Step {
|
||||
self.call(reader, mc, arg, resume_pc)
|
||||
instructions::call(self, reader, mc, arg, resume_pc)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -410,17 +286,22 @@ impl<'gc> Machine<'gc> for Vm<'gc> {
|
||||
|
||||
#[inline(always)]
|
||||
fn finish_ok(&mut self, val: fix_lang::Value) -> Step {
|
||||
self.finish_ok(val)
|
||||
self.result = Some(Ok(val));
|
||||
Step::Break(Break::Done)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn finish_err(&mut self, err: Box<Error>) -> Step {
|
||||
self.finish_err(err)
|
||||
self.result = Some(Err(err));
|
||||
Step::Break(Break::Done)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn finish_type_err(&mut self, expected: NixType, got: NixType) -> Step {
|
||||
self.finish_type_err(expected, got)
|
||||
self.result = Some(Err(Error::eval_error(format!(
|
||||
"expected {expected}, got {got}"
|
||||
))));
|
||||
Step::Break(Break::Done)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -590,6 +471,7 @@ impl<'gc> Vm<'gc> {
|
||||
mc: &Mutation<'gc>,
|
||||
) -> Action {
|
||||
use fix_bytecode::Op::*;
|
||||
use instructions::*;
|
||||
|
||||
let mut reader = BytecodeReader::new(bytecode, pc);
|
||||
let mut fuel = Self::DEFAULT_FUEL_AMOUNT;
|
||||
@@ -603,75 +485,75 @@ impl<'gc> Vm<'gc> {
|
||||
let op = reader.read_op();
|
||||
|
||||
let result = match op {
|
||||
PushSmi => self.op_push_smi(&mut reader),
|
||||
PushBigInt => self.op_push_bigint(&mut reader, mc),
|
||||
PushFloat => self.op_push_float(&mut reader),
|
||||
PushString => self.op_push_string(&mut reader),
|
||||
PushNull => self.op_push_null(),
|
||||
PushTrue => self.op_push_true(),
|
||||
PushFalse => self.op_push_false(),
|
||||
PushSmi => op_push_smi(self, &mut reader),
|
||||
PushBigInt => op_push_bigint(self, &mut reader, mc),
|
||||
PushFloat => op_push_float(self, &mut reader),
|
||||
PushString => op_push_string(self, &mut reader),
|
||||
PushNull => op_push_null(self),
|
||||
PushTrue => op_push_true(self),
|
||||
PushFalse => op_push_false(self),
|
||||
|
||||
LoadLocal => self.op_load_local(&mut reader),
|
||||
LoadOuter => self.op_load_outer(&mut reader),
|
||||
StoreLocal => self.op_store_local(&mut reader, mc),
|
||||
AllocLocals => self.op_alloc_locals(&mut reader, mc),
|
||||
LoadLocal => op_load_local(self, &mut reader),
|
||||
LoadOuter => op_load_outer(self, &mut reader),
|
||||
StoreLocal => op_store_local(self, &mut reader, mc),
|
||||
AllocLocals => op_alloc_locals(self, &mut reader, mc),
|
||||
|
||||
MakeThunk => self.op_make_thunk(&mut reader, mc),
|
||||
MakeClosure => self.op_make_closure(&mut reader, mc),
|
||||
MakePatternClosure => self.op_make_pattern_closure(&mut reader, mc),
|
||||
MakeThunk => op_make_thunk(self, &mut reader, mc),
|
||||
MakeClosure => op_make_closure(self, &mut reader, mc),
|
||||
MakePatternClosure => op_make_pattern_closure(self, &mut reader, mc),
|
||||
|
||||
Call => self.op_call(ctx, &mut reader, mc),
|
||||
DispatchPrimOp => self.op_dispatch_primop(ctx, &mut reader, mc),
|
||||
Return => self.op_return(ctx, &mut reader, mc),
|
||||
Call => op_call(self, ctx, &mut reader, mc),
|
||||
DispatchPrimOp => op_dispatch_primop(self, ctx, &mut reader, mc),
|
||||
Return => op_return(self, ctx, &mut reader, mc),
|
||||
|
||||
MakeAttrs => self.op_make_attrs(ctx, &mut reader, mc),
|
||||
MakeEmptyAttrs => self.op_make_empty_attrs(),
|
||||
SelectStatic => self.op_select_static(ctx, &mut reader, mc),
|
||||
SelectDynamic => self.op_select_dynamic(ctx, &mut reader, mc),
|
||||
HasAttrPathStatic => self.op_has_attr_path_static(ctx, &mut reader, mc),
|
||||
HasAttrPathDynamic => self.op_has_attr_path_dynamic(ctx, &mut reader, mc),
|
||||
HasAttrStatic => self.op_has_attr_static(&mut reader, mc),
|
||||
HasAttrDynamic => self.op_has_attr_dynamic(ctx, &mut reader, mc),
|
||||
HasAttrResolve => self.op_has_attr_resolve(),
|
||||
JumpIfSelectFailed => self.op_jump_if_select_failed(&mut reader),
|
||||
JumpIfSelectSucceeded => self.op_jump_if_select_succeeded(&mut reader),
|
||||
MakeAttrs => op_make_attrs(self, ctx, &mut reader, mc),
|
||||
MakeEmptyAttrs => op_make_empty_attrs(self),
|
||||
SelectStatic => op_select_static(self, ctx, &mut reader, mc),
|
||||
SelectDynamic => op_select_dynamic(self, ctx, &mut reader, mc),
|
||||
HasAttrPathStatic => op_has_attr_path_static(self, ctx, &mut reader, mc),
|
||||
HasAttrPathDynamic => op_has_attr_path_dynamic(self, ctx, &mut reader, mc),
|
||||
HasAttrStatic => op_has_attr_static(self, &mut reader, mc),
|
||||
HasAttrDynamic => op_has_attr_dynamic(self, ctx, &mut reader, mc),
|
||||
HasAttrResolve => op_has_attr_resolve(self),
|
||||
JumpIfSelectFailed => op_jump_if_select_failed(self, &mut reader),
|
||||
JumpIfSelectSucceeded => op_jump_if_select_succeeded(self, &mut reader),
|
||||
|
||||
MakeList => self.op_make_list(ctx, &mut reader, mc),
|
||||
MakeEmptyList => self.op_make_empty_list(),
|
||||
MakeList => op_make_list(self, ctx, &mut reader, mc),
|
||||
MakeEmptyList => op_make_empty_list(self),
|
||||
|
||||
OpAdd => self.op_add(ctx, &mut reader, mc),
|
||||
OpSub => self.op_sub(&mut reader, mc),
|
||||
OpMul => self.op_mul(&mut reader, mc),
|
||||
OpDiv => self.op_div(&mut reader, mc),
|
||||
OpEq => self.op_eq(ctx, &mut reader, mc),
|
||||
OpNeq => self.op_neq(ctx, &mut reader, mc),
|
||||
OpLt => self.op_lt(ctx, &mut reader, mc),
|
||||
OpGt => self.op_gt(ctx, &mut reader, mc),
|
||||
OpLeq => self.op_leq(ctx, &mut reader, mc),
|
||||
OpGeq => self.op_geq(ctx, &mut reader, mc),
|
||||
OpConcat => self.op_concat(&mut reader, mc),
|
||||
OpUpdate => self.op_update(&mut reader, mc),
|
||||
OpAdd => op_add(self, ctx, &mut reader, mc),
|
||||
OpSub => op_sub(self, &mut reader, mc),
|
||||
OpMul => op_mul(self, &mut reader, mc),
|
||||
OpDiv => op_div(self, &mut reader, mc),
|
||||
OpEq => op_eq(self, ctx, &mut reader, mc),
|
||||
OpNeq => op_neq(self, ctx, &mut reader, mc),
|
||||
OpLt => op_lt(self, ctx, &mut reader, mc),
|
||||
OpGt => op_gt(self, ctx, &mut reader, mc),
|
||||
OpLeq => op_leq(self, ctx, &mut reader, mc),
|
||||
OpGeq => op_geq(self, ctx, &mut reader, mc),
|
||||
OpConcat => op_concat(self, &mut reader, mc),
|
||||
OpUpdate => op_update(self, &mut reader, mc),
|
||||
|
||||
OpNeg => self.op_neg(&mut reader, mc),
|
||||
OpNot => self.op_not(&mut reader, mc),
|
||||
OpNeg => op_neg(self, &mut reader, mc),
|
||||
OpNot => op_not(self, &mut reader, mc),
|
||||
|
||||
JumpIfFalse => self.op_jump_if_false(&mut reader, mc),
|
||||
JumpIfTrue => self.op_jump_if_true(&mut reader, mc),
|
||||
Jump => self.op_jump(&mut reader),
|
||||
JumpIfFalse => op_jump_if_false(self, &mut reader, mc),
|
||||
JumpIfTrue => op_jump_if_true(self, &mut reader, mc),
|
||||
Jump => op_jump(self, &mut reader),
|
||||
|
||||
ConcatStrings => self.op_concat_strings(ctx, &mut reader, mc),
|
||||
CoerceToString => self.op_coerce_to_string(&mut reader, mc),
|
||||
ResolvePath => self.op_resolve_path(ctx, &mut reader, mc),
|
||||
ConcatStrings => op_concat_strings(self, ctx, &mut reader, mc),
|
||||
CoerceToString => op_coerce_to_string(self, &mut reader, mc),
|
||||
ResolvePath => op_resolve_path(self, ctx, &mut reader, mc),
|
||||
|
||||
Assert => self.op_assert(ctx, &mut reader, mc),
|
||||
Assert => op_assert(self, ctx, &mut reader, mc),
|
||||
|
||||
LookupWith => self.op_lookup_with(ctx, &mut reader, mc),
|
||||
LookupWith => op_lookup_with(self, ctx, &mut reader, mc),
|
||||
|
||||
LoadBuiltins => self.op_load_builtins(),
|
||||
LoadBuiltin => self.op_load_builtin(&mut reader),
|
||||
LoadBuiltins => op_load_builtins(self),
|
||||
LoadBuiltin => op_load_builtin(self, &mut reader),
|
||||
|
||||
LoadReplBinding => self.op_load_repl_binding(&mut reader),
|
||||
LoadScopedBinding => self.op_load_scoped_binding(ctx, &mut reader, mc),
|
||||
LoadReplBinding => op_load_repl_binding(self, &mut reader),
|
||||
LoadScopedBinding => op_load_scoped_binding(self, ctx, &mut reader, mc),
|
||||
|
||||
Illegal => unreachable!(),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user