cfx_executor/stack/
mod.rs

1mod executable;
2mod frame_invoke;
3mod frame_local;
4mod frame_return;
5mod frame_start;
6mod resources;
7mod resumable;
8mod stack_info;
9
10pub use crate::context::Context;
11pub use executable::{Executable, ExecutableOutcome};
12pub use frame_local::FrameLocal;
13pub use frame_return::{FrameResult, FrameReturn};
14pub use frame_start::FreshFrame;
15pub use resources::RuntimeRes;
16pub use resumable::Resumable;
17pub use stack_info::CallStackInfo;
18
19#[cfg(test)]
20pub use resources::runtime_res_test::OwnedRuntimeRes;
21
22use crate::{substate::Substate, unwrap_or_return};
23use cfx_statedb::Result as DbResult;
24
25use frame_invoke::{InvokeInfo, SuspendedFrame};
26
27/// The output of a frame's execution, which guides the behavior of the frame
28/// stack.
29///
30/// It either completes the execution of the frame and returns its result or
31/// prepares the parameters for the invocation of the next frame in the call
32/// stack.
33enum FrameStackAction<'a> {
34    /// The result of the frame execution.
35    Return(FrameResult),
36
37    /// The info for invoking the next frame.
38    Invoke(InvokeInfo<'a>),
39}
40
41/// The function operates in a loop, starting with the execution of the main
42/// frame. Upon each frame's invocation, the caller is pushed onto the call
43/// stack, and the callee is executed. After a frame completes execution, the
44/// function retrieves the result, pops a frame from the stack, and continues
45/// execution with the results from the callee. The loop continues until the
46/// call stack is empty, indicating that the main frame has finished executing.
47pub fn exec_main_frame<'a>(
48    main_frame: FreshFrame<'a>, mut resources: RuntimeRes<'a>,
49) -> DbResult<FrameResult> {
50    let mut frame_stack: Vec<SuspendedFrame> = Vec::new();
51    let mut last_result = main_frame.init_and_exec(&mut resources)?;
52
53    loop {
54        last_result = match last_result {
55            FrameStackAction::Return(result) => {
56                let frame = unwrap_or_return!(frame_stack.pop(), Ok(result));
57                frame.resume(result, &mut resources)?
58            }
59            FrameStackAction::Invoke(InvokeInfo { callee, caller }) => {
60                frame_stack.push(caller);
61                callee.init_and_exec(&mut resources)?
62            }
63        }
64    }
65}
66
67/// Executes an `Executable` within a frame context. Processes and transforms
68/// the output into a `FrameStackAction` that is compatible with the frame stack
69/// logic. This function also maintains the resources (e.g., maintain the state
70/// checkpoints and the callstack metadata) on the frame return.
71#[inline]
72fn run_executable<'a>(
73    executable: Box<dyn 'a + Executable>, mut frame_local: FrameLocal<'a>,
74    resources: &mut RuntimeRes<'a>,
75) -> DbResult<FrameStackAction<'a>> {
76    let vm_context = frame_local.make_vm_context(resources);
77    let output = executable.execute(vm_context)?;
78
79    let exec_result = match output {
80        ExecutableOutcome::Return(result) => FrameStackAction::Return(
81            frame_return::process_return(frame_local, result, resources),
82        ),
83
84        ExecutableOutcome::Invoke(params, resumer) => FrameStackAction::Invoke(
85            frame_invoke::process_invoke(frame_local, params, resumer),
86        ),
87    };
88    Ok(exec_result)
89}
90
91/// A helper function which extract substate from `FrameResult` if applicable
92/// and merges it to the parent function.
93pub fn accrue_substate(substate: &mut Substate, result: &mut FrameResult) {
94    if let Ok(frame_return) = result {
95        if let Some(child_substate) = std::mem::take(&mut frame_return.substate)
96        {
97            substate.accrue(child_substate);
98        }
99    }
100}