rune/runtime/
protocol_caller.rs

1use crate::runtime::vm::{CallResult, CallResultOnly, Isolated};
2use crate::runtime::{
3    DynArgs, Protocol, Stack, UnitFn, Value, Vm, VmError, VmErrorKind, VmExecution, VmResult,
4};
5use crate::Hash;
6
7/// Trait used for integrating an instance function call.
8pub(crate) trait ProtocolCaller: 'static {
9    /// Call the given protocol function.
10    fn call_protocol_fn(
11        &mut self,
12        protocol: &'static Protocol,
13        target: Value,
14        args: &mut dyn DynArgs,
15    ) -> VmResult<Value> {
16        match vm_try!(self.try_call_protocol_fn(protocol, target, args)) {
17            CallResultOnly::Ok(value) => VmResult::Ok(value),
18            CallResultOnly::Unsupported(value) => {
19                VmResult::err(VmErrorKind::MissingProtocolFunction {
20                    protocol,
21                    instance: value.type_info(),
22                })
23            }
24        }
25    }
26
27    /// Call the given protocol function.
28    fn try_call_protocol_fn(
29        &mut self,
30        protocol: &'static Protocol,
31        target: Value,
32        args: &mut dyn DynArgs,
33    ) -> VmResult<CallResultOnly<Value>>;
34}
35
36/// Use the global environment caller.
37///
38/// This allocates its own stack and virtual machine for the call.
39pub(crate) struct EnvProtocolCaller;
40
41impl ProtocolCaller for EnvProtocolCaller {
42    fn try_call_protocol_fn(
43        &mut self,
44        protocol: &Protocol,
45        target: Value,
46        args: &mut dyn DynArgs,
47    ) -> VmResult<CallResultOnly<Value>> {
48        /// Check that arguments matches expected or raise the appropriate error.
49        fn check_args(args: usize, expected: usize) -> Result<(), VmError> {
50            if args != expected {
51                return Err(VmError::from(VmErrorKind::BadArgumentCount {
52                    actual: args,
53                    expected,
54                }));
55            }
56
57            Ok(())
58        }
59
60        crate::runtime::env::shared(|context, unit| {
61            let count = args.count() + 1;
62            let hash = Hash::associated_function(target.type_hash(), protocol.hash);
63
64            if let Some(UnitFn::Offset {
65                offset,
66                args: expected,
67                call,
68                ..
69            }) = unit.function(&hash)
70            {
71                vm_try!(check_args(count, *expected));
72
73                let mut stack = vm_try!(Stack::with_capacity(count));
74                vm_try!(stack.push(target));
75                vm_try!(args.push_to_stack(&mut stack));
76                let mut vm = Vm::with_stack(context.clone(), unit.clone(), stack);
77                vm.set_ip(*offset);
78                return VmResult::Ok(CallResultOnly::Ok(vm_try!(call.call_with_vm(vm))));
79            }
80
81            if let Some(handler) = context.function(&hash) {
82                let mut stack = vm_try!(Stack::with_capacity(count));
83                let addr = stack.addr();
84                vm_try!(stack.push(target));
85                vm_try!(args.push_to_stack(&mut stack));
86                vm_try!(handler(&mut stack, addr, count, addr.output()));
87                let value = stack.at(addr).clone();
88                return VmResult::Ok(CallResultOnly::Ok(value));
89            }
90
91            VmResult::Ok(CallResultOnly::Unsupported(target))
92        })
93    }
94}
95
96impl ProtocolCaller for Vm {
97    fn try_call_protocol_fn(
98        &mut self,
99        protocol: &'static Protocol,
100        target: Value,
101        args: &mut dyn DynArgs,
102    ) -> VmResult<CallResultOnly<Value>> {
103        let addr = self.stack().addr();
104        vm_try!(self.stack_mut().push(()));
105
106        match vm_try!(self.call_instance_fn(
107            Isolated::Isolated,
108            target,
109            protocol,
110            args,
111            addr.output()
112        )) {
113            CallResult::Unsupported(value) => VmResult::Ok(CallResultOnly::Unsupported(value)),
114            CallResult::Ok(()) => {
115                let value = self.stack().at(addr).clone();
116                self.stack_mut().truncate(addr);
117                VmResult::Ok(CallResultOnly::Ok(value))
118            }
119            CallResult::Frame => {
120                let mut execution = VmExecution::new(self);
121                let value = vm_try!(execution.complete());
122                execution.vm_mut().stack_mut().truncate(addr);
123                VmResult::Ok(CallResultOnly::Ok(value))
124            }
125        }
126    }
127}