rune/runtime/
protocol_caller.rsuse crate::runtime::vm::{CallResult, CallResultOnly, Isolated};
use crate::runtime::{
DynArgs, Protocol, Stack, UnitFn, Value, Vm, VmError, VmErrorKind, VmExecution, VmResult,
};
use crate::Hash;
pub(crate) trait ProtocolCaller: 'static {
fn call_protocol_fn(
&mut self,
protocol: &'static Protocol,
target: Value,
args: &mut dyn DynArgs,
) -> VmResult<Value> {
match vm_try!(self.try_call_protocol_fn(protocol, target, args)) {
CallResultOnly::Ok(value) => VmResult::Ok(value),
CallResultOnly::Unsupported(value) => {
VmResult::err(VmErrorKind::MissingProtocolFunction {
protocol,
instance: value.type_info(),
})
}
}
}
fn try_call_protocol_fn(
&mut self,
protocol: &'static Protocol,
target: Value,
args: &mut dyn DynArgs,
) -> VmResult<CallResultOnly<Value>>;
}
pub(crate) struct EnvProtocolCaller;
impl ProtocolCaller for EnvProtocolCaller {
fn try_call_protocol_fn(
&mut self,
protocol: &Protocol,
target: Value,
args: &mut dyn DynArgs,
) -> VmResult<CallResultOnly<Value>> {
fn check_args(args: usize, expected: usize) -> Result<(), VmError> {
if args != expected {
return Err(VmError::from(VmErrorKind::BadArgumentCount {
actual: args,
expected,
}));
}
Ok(())
}
crate::runtime::env::shared(|context, unit| {
let count = args.count() + 1;
let hash = Hash::associated_function(target.type_hash(), protocol.hash);
if let Some(UnitFn::Offset {
offset,
args: expected,
call,
..
}) = unit.function(&hash)
{
vm_try!(check_args(count, *expected));
let mut stack = vm_try!(Stack::with_capacity(count));
vm_try!(stack.push(target));
vm_try!(args.push_to_stack(&mut stack));
let mut vm = Vm::with_stack(context.clone(), unit.clone(), stack);
vm.set_ip(*offset);
return VmResult::Ok(CallResultOnly::Ok(vm_try!(call.call_with_vm(vm))));
}
if let Some(handler) = context.function(&hash) {
let mut stack = vm_try!(Stack::with_capacity(count));
let addr = stack.addr();
vm_try!(stack.push(target));
vm_try!(args.push_to_stack(&mut stack));
vm_try!(handler(&mut stack, addr, count, addr.output()));
let value = stack.at(addr).clone();
return VmResult::Ok(CallResultOnly::Ok(value));
}
VmResult::Ok(CallResultOnly::Unsupported(target))
})
}
}
impl ProtocolCaller for Vm {
fn try_call_protocol_fn(
&mut self,
protocol: &'static Protocol,
target: Value,
args: &mut dyn DynArgs,
) -> VmResult<CallResultOnly<Value>> {
let addr = self.stack().addr();
vm_try!(self.stack_mut().push(()));
match vm_try!(self.call_instance_fn(
Isolated::Isolated,
target,
protocol,
args,
addr.output()
)) {
CallResult::Unsupported(value) => VmResult::Ok(CallResultOnly::Unsupported(value)),
CallResult::Ok(()) => {
let value = self.stack().at(addr).clone();
self.stack_mut().truncate(addr);
VmResult::Ok(CallResultOnly::Ok(value))
}
CallResult::Frame => {
let mut execution = VmExecution::new(self);
let value = vm_try!(execution.complete());
execution.vm_mut().stack_mut().truncate(addr);
VmResult::Ok(CallResultOnly::Ok(value))
}
}
}
}