153 lines
5.1 KiB
Rust
153 lines
5.1 KiB
Rust
use fix_builtins::PrimOpPhase;
|
|
use fix_error::Error;
|
|
use gc_arena::{Gc, Mutation, RefLock};
|
|
|
|
use crate::value::*;
|
|
use crate::{
|
|
BytecodeReader, CallFrame, Closure, Env, ForceMode, Step, ThunkState, VmRuntimeCtx,
|
|
VmRuntimeCtxExt,
|
|
};
|
|
|
|
impl<'gc> crate::Vm<'gc> {
|
|
#[inline(always)]
|
|
pub(crate) fn call(
|
|
&mut self,
|
|
reader: &mut BytecodeReader<'_>,
|
|
mc: &Mutation<'gc>,
|
|
arg: Value<'gc>,
|
|
resume_pc: usize,
|
|
) -> Step {
|
|
let func = self.try_force::<StrictValue>(reader, mc)?;
|
|
if self.call_depth > 10000 {
|
|
return self.finish_err(Error::eval_error("stack overflow; max-call-depth exceeded"));
|
|
}
|
|
self.call_depth += 1;
|
|
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: resume_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 if let Some(primop) = func.as_inline::<PrimOp>() {
|
|
if primop.arity == 1 {
|
|
self.push(arg);
|
|
self.call_stack.push(CallFrame {
|
|
pc: resume_pc,
|
|
stack_depth: 0,
|
|
thunk: None,
|
|
env: self.env,
|
|
with_env: self.with_env,
|
|
});
|
|
reader.set_pc(primop.dispatch_ip as usize)
|
|
} else {
|
|
let app = PrimOpApp {
|
|
primop,
|
|
arity: primop.arity - 1,
|
|
args: [arg, Value::default(), Value::default()],
|
|
};
|
|
self.push(Value::new_gc(Gc::new(mc, app)));
|
|
}
|
|
} else if let Some(app) = func.as_gc::<PrimOpApp>() {
|
|
if app.arity == 1 {
|
|
for i in 0..app.primop.arity - 1 {
|
|
self.push(app.args[i as usize]);
|
|
}
|
|
self.push(arg);
|
|
self.call_stack.push(CallFrame {
|
|
pc: resume_pc,
|
|
stack_depth: 0,
|
|
thunk: None,
|
|
env: self.env,
|
|
with_env: self.with_env,
|
|
});
|
|
reader.set_pc(app.primop.dispatch_ip as usize)
|
|
} else {
|
|
let new_app = PrimOpApp {
|
|
arity: app.arity - 1,
|
|
..*app
|
|
};
|
|
self.push(Value::new_gc(Gc::new(mc, new_app)))
|
|
}
|
|
} else {
|
|
todo!("call other types: {func:?}")
|
|
}
|
|
Step::Continue(())
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub(crate) fn op_call(
|
|
&mut self,
|
|
ctx: &impl VmRuntimeCtx,
|
|
reader: &mut BytecodeReader<'_>,
|
|
mc: &Mutation<'gc>,
|
|
) -> Step {
|
|
let arg = reader.read_operand_data(ctx).resolve(mc, self);
|
|
let pc = reader.pc();
|
|
self.call(reader, mc, arg, pc)
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub(crate) fn op_return(
|
|
&mut self,
|
|
ctx: &mut impl VmRuntimeCtx,
|
|
reader: &mut BytecodeReader<'_>,
|
|
mc: &Mutation<'gc>,
|
|
) -> Step {
|
|
let val = self.try_force::<StrictValue>(reader, mc)?;
|
|
let Some(CallFrame {
|
|
pc: ret_pc,
|
|
stack_depth,
|
|
thunk,
|
|
env,
|
|
with_env,
|
|
}) = self.call_stack.pop()
|
|
else {
|
|
match self.force_mode {
|
|
ForceMode::AsIs => return self.finish_ok(ctx.convert_value(val.relax())),
|
|
ForceMode::Shallow => {
|
|
self.push(val.relax());
|
|
reader.set_pc(PrimOpPhase::ForceResultShallow.ip() as usize);
|
|
return Step::Continue(());
|
|
}
|
|
ForceMode::Deep => {
|
|
self.push(val.relax());
|
|
self.push(val.relax());
|
|
self.call_stack.push(CallFrame {
|
|
pc: PrimOpPhase::ForceResultDeepFinish.ip() as usize,
|
|
stack_depth: 0,
|
|
thunk: None,
|
|
env: self.env,
|
|
with_env: self.with_env,
|
|
});
|
|
self.call_depth += 1;
|
|
reader.set_pc(PrimOpPhase::DeepSeq.ip() as usize);
|
|
return Step::Continue(());
|
|
}
|
|
}
|
|
};
|
|
reader.set_pc(ret_pc);
|
|
if let Some(outer_thunk) = thunk {
|
|
*outer_thunk.borrow_mut(mc) = ThunkState::Evaluated(val);
|
|
self.replace(stack_depth, val.relax());
|
|
} else {
|
|
self.call_depth -= 1;
|
|
self.push(val.relax())
|
|
}
|
|
self.env = env;
|
|
self.with_env = with_env;
|
|
Step::Continue(())
|
|
}
|
|
}
|