rune/runtime/
vm_call.rs

1use ::rust_alloc::sync::Arc;
2
3use crate::alloc::prelude::*;
4use crate::runtime::vm_execution::VmExecutionState;
5use crate::runtime::{
6    Call, Future, Generator, Output, RuntimeContext, Stack, Stream, Unit, Value, Vm, VmErrorKind,
7    VmExecution, VmResult,
8};
9
10/// An instruction to push a virtual machine to the execution.
11#[derive(Debug)]
12#[must_use = "The construction of a vm call leaves the virtual machine stack in an intermediate state, VmCall::into_execution must be called to fix it"]
13pub(crate) struct VmCall {
14    /// The calling convention to use for the call.
15    call: Call,
16    /// Is set if the context differs for the call for the current virtual machine.
17    context: Option<Arc<RuntimeContext>>,
18    /// Is set if the unit differs for the call for the current virtual machine.
19    unit: Option<Arc<Unit>>,
20    /// The output to store the result of the call into.
21    out: Output,
22}
23
24impl VmCall {
25    pub(crate) fn new(
26        call: Call,
27        context: Option<Arc<RuntimeContext>>,
28        unit: Option<Arc<Unit>>,
29        out: Output,
30    ) -> Self {
31        Self {
32            call,
33            context,
34            unit,
35            out,
36        }
37    }
38
39    /// Encode the push itno an execution.
40    #[tracing::instrument(skip_all)]
41    pub(crate) fn into_execution<T>(self, execution: &mut VmExecution<T>) -> VmResult<()>
42    where
43        T: AsRef<Vm> + AsMut<Vm>,
44    {
45        let out = self.out;
46
47        let value = match self.call {
48            Call::Async => {
49                let vm = vm_try!(self.build_vm(execution));
50                let mut execution = vm.into_execution();
51                vm_try!(Value::try_from(vm_try!(Future::new(async move {
52                    execution.async_complete().await
53                }))))
54            }
55            Call::Immediate => {
56                vm_try!(execution.push_state(VmExecutionState {
57                    context: self.context,
58                    unit: self.unit,
59                }));
60
61                return VmResult::Ok(());
62            }
63            Call::Stream => {
64                let vm = vm_try!(self.build_vm(execution));
65                vm_try!(Value::try_from(Stream::new(vm)))
66            }
67            Call::Generator => {
68                let vm = vm_try!(self.build_vm(execution));
69                vm_try!(Value::try_from(Generator::new(vm)))
70            }
71        };
72
73        vm_try!(out.store(execution.vm_mut().stack_mut(), value));
74        VmResult::Ok(())
75    }
76
77    #[tracing::instrument(skip_all)]
78    fn build_vm<T>(self, execution: &mut VmExecution<T>) -> VmResult<Vm>
79    where
80        T: AsRef<Vm> + AsMut<Vm>,
81    {
82        let vm = execution.vm_mut();
83
84        let new_stack = vm_try!(vm.stack_mut().drain().try_collect::<Stack>());
85
86        let Some(ip) = vm.pop_call_frame_from_call() else {
87            return VmResult::err(VmErrorKind::MissingCallFrame);
88        };
89
90        let context = self.context.unwrap_or_else(|| vm.context().clone());
91        let unit = self.unit.unwrap_or_else(|| vm.unit().clone());
92
93        let mut vm = Vm::with_stack(context, unit, new_stack);
94        vm.set_ip(ip);
95        VmResult::Ok(vm)
96    }
97}