cfx_executor/stack/
executable.rsuse super::{FrameLocal, Resumable};
use crate::{
    builtin::BuiltinExec, context::Context, executive_observer::TracerTrait,
    internal_contract::InternalContractExec,
};
use cfx_statedb::Result as DbResult;
use cfx_types::{AddressSpaceUtil, U256};
use cfx_vm_interpreter::{FinalizationResult, Finalize};
use cfx_vm_types::{
    self as vm, separate_out_db_error, ActionParams, Exec, GasLeft, ReturnData,
    TrapError, TrapResult,
};
pub trait Executable: Send {
    fn execute(
        self: Box<Self>, context: Context,
    ) -> DbResult<ExecutableOutcome>;
}
pub enum ExecutableOutcome {
    Return(vm::Result<FinalizationResult>),
    Invoke(ActionParams, Box<dyn Resumable>),
}
use ExecutableOutcome::*;
pub fn make_executable<'a>(
    frame_local: &FrameLocal<'a>, params: ActionParams,
    tracer: &mut dyn TracerTrait,
) -> Box<dyn 'a + Executable> {
    let is_create = frame_local.create_address.is_some();
    let code_address = params.code_address.with_space(params.space);
    let internal_contract_map = frame_local.machine.internal_contracts();
    if let Some(builtin) = frame_local
        .machine
        .builtin(&code_address, frame_local.env.number)
    {
        trace!("CallBuiltin");
        return Box::new(BuiltinExec { builtin, params });
    }
    if let Some(internal) =
        internal_contract_map.contract(&code_address, &frame_local.spec)
    {
        trace!(
            "CallInternalContract: address={:?} data={:?}",
            code_address,
            params.data
        );
        return Box::new(InternalContractExec { internal, params });
    }
    if is_create || params.code.is_some() {
        trace!("CallCreate");
        tracer.initialize_interp(params.gas.clone());
        let factory = frame_local.machine.vm_factory_ref();
        Box::new(factory.create(params, frame_local.spec, frame_local.depth))
    } else {
        trace!("Transfer");
        Box::new(NoopExec { gas: params.gas })
    }
}
impl Executable for Box<dyn Exec> {
    fn execute(
        self: Box<Self>, mut context: Context,
    ) -> DbResult<ExecutableOutcome> {
        Ok(match self.exec(&mut context) {
            TrapResult::Return(result) => {
                let result = separate_out_db_error(result)?;
                if matches!(result, Ok(GasLeft::Known(_))) {
                    context.insert_create_address_to_substate();
                }
                Return(result.finalize(context))
            }
            TrapResult::SubCallCreate(TrapError::Call(params, resume)) => {
                Invoke(params, Box::new(resume))
            }
            TrapResult::SubCallCreate(TrapError::Create(params, resume)) => {
                Invoke(params, Box::new(resume))
            }
        })
    }
}
pub struct NoopExec {
    pub gas: U256,
}
impl Executable for NoopExec {
    fn execute(self: Box<Self>, _: Context) -> DbResult<ExecutableOutcome> {
        let result = FinalizationResult {
            gas_left: self.gas,
            apply_state: true,
            return_data: ReturnData::empty(),
        };
        Ok(Return(Ok(result)))
    }
}