multiversx_sc_scenario/executor/debug/
contract_debug_instance.rs1use std::rc::Rc;
2
3use multiversx_chain_vm::host::{
4 context::{TxContextRef, TxFunctionName, TxPanic},
5 runtime::RuntimeInstanceCall,
6};
7use multiversx_chain_vm_executor::{ExecutorError, Instance, InstanceCallResult};
8use multiversx_sc::chain_core::types::ReturnCode;
9
10use super::{
11 ContractContainer, ContractContainerRef, ContractDebugStack, StaticVarData, catch_tx_panic,
12};
13
14const FUNC_CONTEXT_PUSH: &str = "<ContractDebugInstance-PushContext>";
17const FUNC_CONTEXT_POP: &str = "<ContractDebugInstance-PopContext>";
18const DEBUG_GAS_LIMIT: u64 = 0;
19
20#[derive(Clone, Debug)]
21pub struct ContractDebugInstance {
22 pub tx_context_ref: TxContextRef,
23 pub contract_container_ref: ContractContainerRef,
24 pub static_var_ref: Rc<StaticVarData>,
25}
26
27impl ContractDebugInstance {
28 pub fn new(tx_context_ref: TxContextRef, contract_container: ContractContainerRef) -> Self {
29 ContractDebugInstance {
30 tx_context_ref,
31 contract_container_ref: contract_container,
32 static_var_ref: Default::default(),
33 }
34 }
35
36 pub fn dummy() -> Self {
38 ContractDebugInstance {
39 tx_context_ref: TxContextRef::dummy(),
40 contract_container_ref: ContractContainerRef::new(ContractContainer::dummy()),
41 static_var_ref: Default::default(),
42 }
43 }
44
45 pub(super) fn wrap_lambda_call<F>(
46 panic_message_flag: bool,
47 instance_call: RuntimeInstanceCall,
48 f: F,
49 ) where
50 F: FnOnce(),
51 {
52 assert!(
53 instance_call.instance.has_function(FUNC_CONTEXT_PUSH),
54 "lambda call is not running on top of a DebugSCInstance instance"
55 );
56
57 let _ = instance_call
58 .instance
59 .call(FUNC_CONTEXT_PUSH, DEBUG_GAS_LIMIT);
60
61 let result = catch_tx_panic(panic_message_flag, || {
62 f();
63 Ok(())
64 });
65
66 if let Err(tx_panic) = result {
67 ContractDebugStack::static_peek()
68 .tx_context_ref
69 .replace_tx_result_with_error(tx_panic);
70 }
71
72 let _ = instance_call
73 .instance
74 .call(FUNC_CONTEXT_POP, DEBUG_GAS_LIMIT);
75 }
76
77 fn call_endpoint(&self, func_name: &str) {
78 let tx_func_name = TxFunctionName::from(func_name);
79
80 ContractDebugStack::static_push(self.clone());
81
82 let result = catch_tx_panic(self.contract_container_ref.0.panic_message, || {
83 let call_successful = self.contract_container_ref.0.call(&tx_func_name);
84 if call_successful {
85 Ok(())
86 } else {
87 Err(TxPanic::new(
88 ReturnCode::FunctionNotFound,
89 "invalid function (not found)",
90 ))
91 }
92 });
93
94 if let Err(tx_panic) = result {
95 self.tx_context_ref
96 .clone()
97 .replace_tx_result_with_error(tx_panic);
98 }
99
100 ContractDebugStack::static_pop();
101 }
102}
103
104impl Instance for ContractDebugInstance {
105 fn call(&mut self, func_name: &str, _points_limit: u64) -> InstanceCallResult {
106 match func_name {
107 FUNC_CONTEXT_PUSH => {
108 ContractDebugStack::static_push(self.clone());
109 }
110 FUNC_CONTEXT_POP => {
111 ContractDebugStack::static_pop();
112 }
113 _ => self.call_endpoint(func_name),
114 }
115 InstanceCallResult::Ok
116 }
117
118 fn check_signatures(&self) -> bool {
119 true
120 }
121
122 fn has_function(&self, func_name: &str) -> bool {
123 match func_name {
124 FUNC_CONTEXT_PUSH => true,
125 FUNC_CONTEXT_POP => true,
126 _ => self.contract_container_ref.has_function(func_name),
127 }
128 }
129
130 fn get_exported_function_names(&self) -> Vec<String> {
131 panic!("ContractDebugInstance get_exported_function_names not yet supported")
132 }
133
134 fn get_points_used(&mut self) -> Result<u64, ExecutorError> {
135 Ok(0)
136 }
137
138 fn reset(&self) -> Result<(), ExecutorError> {
139 panic!("ContractDebugInstance reset not supported")
140 }
141
142 fn cache(&self) -> Result<Vec<u8>, ExecutorError> {
143 panic!("ContractDebugInstance cache not supported")
144 }
145}