138 lines
5.0 KiB
Rust
138 lines
5.0 KiB
Rust
use fix_error::Error;
|
|
use gc_arena::{Gc, Mutation, RefLock};
|
|
|
|
use crate::{BytecodeReader, CallFrame, Closure, Env, StepResult, ThunkState, VmContextExt};
|
|
|
|
impl<'gc> crate::Vm<'gc> {
|
|
#[inline(always)]
|
|
pub(crate) fn op_call(
|
|
&mut self,
|
|
ctx: &mut impl crate::VmContext,
|
|
reader: &mut BytecodeReader<'_>,
|
|
mc: &Mutation<'gc>,
|
|
) -> StepResult<'gc> {
|
|
if let Some(step) = self.try_force_resolved(0, reader.inst_start_pc(), mc) {
|
|
return step;
|
|
}
|
|
if self.call_depth > 10000 {
|
|
return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
|
}
|
|
self.call_depth += 1;
|
|
let func = self.pop_stack();
|
|
let arg = reader.read_operand_data(ctx).resolve(mc, self);
|
|
if let Some(closure) = func.as_gc::<Closure>() {
|
|
let ip = closure.ip;
|
|
let n_locals = closure.n_locals;
|
|
let env = closure.env;
|
|
if let Some(ref _pattern) = closure.pattern {
|
|
todo!("pattern call")
|
|
} else {
|
|
let new_env = Gc::new(mc, RefLock::new(Env::with_arg(arg, n_locals, env)));
|
|
self.call_stack.push(CallFrame {
|
|
pc: reader.pc(),
|
|
stack_depth: 0,
|
|
thunk: None,
|
|
env: self.env,
|
|
with_env: self.with_env,
|
|
});
|
|
reader.set_pc(ip as usize);
|
|
self.env = new_env;
|
|
}
|
|
} else {
|
|
todo!("call other types: {func:?}")
|
|
}
|
|
StepResult::Continue
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub(crate) fn op_return(
|
|
&mut self,
|
|
ctx: &mut impl crate::VmContext,
|
|
reader: &mut BytecodeReader<'_>,
|
|
mc: &Mutation<'gc>,
|
|
) -> StepResult<'gc> {
|
|
self.handle_return(reader, ctx, mc)
|
|
}
|
|
|
|
pub(crate) fn handle_return<C: crate::VmContext>(
|
|
&mut self,
|
|
reader: &mut BytecodeReader<'_>,
|
|
ctx: &C,
|
|
mc: &Mutation<'gc>,
|
|
) -> StepResult<'gc> {
|
|
let ret_inst_pc = reader.pc() - 1;
|
|
let Some(CallFrame {
|
|
pc: ret_pc,
|
|
stack_depth,
|
|
thunk,
|
|
env,
|
|
with_env,
|
|
}) = self.call_stack.pop()
|
|
else {
|
|
let val = self.pop_stack();
|
|
return self.finish_ok(ctx.convert_value(val));
|
|
};
|
|
reader.set_pc(ret_pc);
|
|
if let Some(outer_thunk) = thunk {
|
|
let val = self.pop_stack();
|
|
match val.restrict() {
|
|
Ok(val) => {
|
|
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
|
if reader.bytecode().get(ret_pc).copied() == Some(fix_codegen::Op::Return as u8)
|
|
{
|
|
self.push_stack(val.relax());
|
|
}
|
|
}
|
|
Err(inner_thunk) => {
|
|
let mut state = inner_thunk.borrow_mut(mc);
|
|
match *state {
|
|
ThunkState::Pending {
|
|
ip: inner_ip,
|
|
env: inner_env,
|
|
with_env: inner_with_env,
|
|
} => {
|
|
self.call_stack.push(CallFrame {
|
|
pc: ret_pc,
|
|
stack_depth,
|
|
thunk: Some(outer_thunk),
|
|
env,
|
|
with_env,
|
|
});
|
|
self.call_stack.push(CallFrame {
|
|
pc: ret_inst_pc,
|
|
stack_depth: 0,
|
|
thunk: Some(inner_thunk),
|
|
env: inner_env,
|
|
with_env: inner_with_env,
|
|
});
|
|
*state = ThunkState::Blackhole;
|
|
reader.set_pc(inner_ip);
|
|
self.env = inner_env;
|
|
self.with_env = inner_with_env;
|
|
return StepResult::Continue;
|
|
}
|
|
ThunkState::Evaluated(val) => {
|
|
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
|
if reader.bytecode().get(ret_pc).copied()
|
|
== Some(fix_codegen::Op::Return as u8)
|
|
{
|
|
self.push_stack(val.relax());
|
|
}
|
|
}
|
|
ThunkState::Apply { func: _, arg: _ } => todo!("force Apply thunk"),
|
|
ThunkState::Blackhole => {
|
|
return self
|
|
.finish_err(Error::eval_error("infinite recursion encountered"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
self.call_depth -= 1;
|
|
}
|
|
self.env = env;
|
|
self.with_env = with_env;
|
|
StepResult::Continue
|
|
}
|
|
}
|