rune/runtime/
protocol_caller.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::runtime::vm::{CallResult, CallResultOnly, Isolated};
use crate::runtime::{
    DynArgs, Protocol, Stack, UnitFn, Value, Vm, VmError, VmErrorKind, VmExecution, VmResult,
};
use crate::Hash;

/// Trait used for integrating an instance function call.
pub(crate) trait ProtocolCaller: 'static {
    /// Call the given protocol function.
    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(),
                })
            }
        }
    }

    /// Call the given protocol function.
    fn try_call_protocol_fn(
        &mut self,
        protocol: &'static Protocol,
        target: Value,
        args: &mut dyn DynArgs,
    ) -> VmResult<CallResultOnly<Value>>;
}

/// Use the global environment caller.
///
/// This allocates its own stack and virtual machine for the call.
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>> {
        /// Check that arguments matches expected or raise the appropriate error.
        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))
            }
        }
    }
}