runestick/
protocol_caller.rs

1use crate::{GuardedArgs, Hash, Protocol, Stack, UnitFn, Value, Vm, VmError, VmErrorKind};
2
3/// Trait used for integrating an instance function call.
4pub(crate) trait ProtocolCaller {
5    /// Call the given protocol function.
6    fn call_protocol_fn<A>(
7        self,
8        protocol: Protocol,
9        target: Value,
10        args: A,
11    ) -> Result<Value, VmError>
12    where
13        A: GuardedArgs;
14}
15
16/// Use the global environment caller.
17///
18/// This allocates its own stack and virtual machine for the call.
19pub(crate) struct EnvProtocolCaller;
20
21impl ProtocolCaller for EnvProtocolCaller {
22    fn call_protocol_fn<A>(
23        self,
24        protocol: Protocol,
25        target: Value,
26        args: A,
27    ) -> Result<Value, VmError>
28    where
29        A: GuardedArgs,
30    {
31        return crate::env::with(|context, unit| {
32            let count = args.count() + 1;
33            let hash = Hash::instance_function(target.type_hash()?, protocol.hash);
34
35            if let Some(UnitFn::Offset {
36                offset,
37                args: expected,
38                call,
39            }) = unit.lookup(hash)
40            {
41                check_args(count, expected)?;
42
43                let mut stack = Stack::with_capacity(count);
44                stack.push(target);
45
46                // Safety: We hold onto the guard until the vm has completed.
47                let _guard = unsafe { args.unsafe_into_stack(&mut stack)? };
48
49                let mut vm = Vm::new_with_stack(context.clone(), unit.clone(), stack);
50                vm.set_ip(offset);
51                return call.call_with_vm(vm);
52            }
53
54            let handler = match context.lookup(hash) {
55                Some(handler) => handler,
56                None => return Err(VmError::from(VmErrorKind::MissingFunction { hash })),
57            };
58
59            let mut stack = Stack::with_capacity(count);
60            stack.push(target);
61
62            // Safety: We hold onto the guard until the vm has completed.
63            let _guard = unsafe { args.unsafe_into_stack(&mut stack)? };
64
65            handler(&mut stack, count)?;
66            Ok(stack.pop()?)
67        });
68
69        /// Check that arguments matches expected or raise the appropriate error.
70        fn check_args(args: usize, expected: usize) -> Result<(), VmError> {
71            if args != expected {
72                return Err(VmError::from(VmErrorKind::BadArgumentCount {
73                    actual: args,
74                    expected,
75                }));
76            }
77
78            Ok(())
79        }
80    }
81}
82
83impl ProtocolCaller for &mut Vm {
84    fn call_protocol_fn<A>(
85        self,
86        protocol: Protocol,
87        target: Value,
88        args: A,
89    ) -> Result<Value, VmError>
90    where
91        A: GuardedArgs,
92    {
93        if !self.call_instance_fn(target, protocol, args)? {
94            return Err(VmError::from(VmErrorKind::MissingFunction {
95                hash: protocol.hash,
96            }));
97        }
98
99        Ok(self.stack.pop()?)
100    }
101}