ab_contracts_common/
env.rs1use crate::ContractError;
2use crate::method::{ExternalArgs, MethodFingerprint};
3use ab_core_primitives::address::Address;
4use ab_core_primitives::shard::ShardIndex;
5use ab_io_type::trivial_type::TrivialType;
6use core::ffi::c_void;
7use core::marker::PhantomData;
8use core::ptr::NonNull;
9
10#[derive(Debug, Copy, Clone, Eq, PartialEq, TrivialType)]
21#[repr(u8)]
22pub enum MethodContext {
23 Keep,
25 Reset,
27 Replace,
29}
30
31#[derive(Debug)]
33#[repr(C)]
34#[must_use]
35pub struct PreparedMethod<'a> {
36 pub contract: Address,
38 pub fingerprint: MethodFingerprint,
40 pub external_args: NonNull<c_void>,
43 pub method_context: MethodContext,
45 pub phantom: PhantomData<&'a ()>,
47}
48
49#[cfg(feature = "guest")]
50unsafe extern "C" {
51 fn __ab_host_call_import(method: &PreparedMethod<'_>) -> crate::ExitCode;
53}
54
55#[cfg(feature = "guest")]
59#[unsafe(no_mangle)]
60#[inline(never)]
61#[doc(hidden)]
62pub extern "C" fn __ab_host_call(method: &PreparedMethod<'_>) -> crate::ExitCode {
63 unsafe { __ab_host_call_import(method) }
65}
66
67#[derive(Debug, Copy, Clone, TrivialType)]
69#[repr(C)]
70pub struct EnvState {
71 pub shard_index: ShardIndex,
73 pub padding_0: [u8; 4],
75 pub own_address: Address,
77 pub context: Address,
79 pub caller: Address,
81}
82
83#[cfg(feature = "executor")]
85pub trait ExecutorContext: core::fmt::Debug {
86 fn call(
88 &self,
89 previous_env_state: &EnvState,
90 prepared_method: &mut PreparedMethod<'_>,
91 ) -> Result<(), ContractError>;
92}
93
94#[cfg(all(feature = "executor", feature = "guest", not(any(doc, unix, windows))))]
95compile_error!(
96 "`executor` and `guest` features are mutually exclusive due to it affecting `Env` layout"
97);
98
99#[derive(Debug)]
104#[repr(C)]
105pub struct Env<'a> {
106 state: EnvState,
107 #[cfg(feature = "executor")]
108 executor_context: &'a mut dyn ExecutorContext,
109 #[cfg(not(feature = "executor"))]
110 phantom_data: PhantomData<&'a ()>,
111}
112
113#[cfg_attr(
117 not(feature = "executor"),
118 expect(
119 clippy::elidable_lifetime_names,
120 reason = "False-positive, see https://github.com/rust-lang/rust-clippy/issues/17638"
121 )
122)]
123impl<'a> Env<'a> {
124 #[cfg(feature = "executor")]
126 #[inline(always)]
127 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
128 pub fn with_executor_context(
129 state: EnvState,
130 executor_context: &'a mut dyn ExecutorContext,
131 ) -> Self {
132 Self {
133 state,
134 executor_context,
135 }
136 }
137
138 #[cfg(feature = "executor")]
140 #[inline(always)]
141 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
142 pub fn get_mut_executor_context(&mut self) -> &mut dyn ExecutorContext {
143 self.executor_context
144 }
145
146 #[inline(always)]
148 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
149 pub fn shard_index(&self) -> ShardIndex {
150 self.state.shard_index
151 }
152
153 #[inline(always)]
155 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
156 pub fn own_address(&self) -> Address {
157 self.state.own_address
158 }
159
160 #[inline(always)]
162 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
163 pub fn context<'b>(self: &'b &'b mut Self) -> Address {
164 self.state.context
165 }
166
167 #[inline(always)]
169 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
170 pub fn caller<'b>(self: &'b &'b mut Self) -> Address {
171 self.state.caller
172 }
173
174 #[inline(always)]
178 pub fn call<Args>(
179 &self,
180 contract: Address,
181 args: &mut Args,
182 method_context: MethodContext,
183 ) -> Result<(), ContractError>
184 where
185 Args: ExternalArgs,
186 {
187 let prepared_method = Self::prepare_method_call(contract, args, method_context);
188 self.call_prepared(prepared_method)
189 }
190
191 #[inline(always)]
195 #[cfg_attr(feature = "no-panic", no_panic::no_panic)]
196 pub fn prepare_method_call<Args>(
197 contract: Address,
198 args: &mut Args,
199 method_context: MethodContext,
200 ) -> PreparedMethod<'_>
201 where
202 Args: ExternalArgs,
203 {
204 PreparedMethod {
205 contract,
206 fingerprint: Args::FINGERPRINT,
207 external_args: NonNull::from_mut(args).cast::<c_void>(),
208 method_context,
209 phantom: PhantomData,
210 }
211 }
212
213 #[inline]
218 pub fn call_prepared(&self, method: PreparedMethod<'_>) -> Result<(), ContractError> {
219 cfg_select! {
220 feature = "executor" => {
221 let mut method = method;
222 self.executor_context.call(&self.state, &mut method)
223 }
224 feature = "guest" => __ab_host_call(&method).into(),
225 _ => {
226 let _: PreparedMethod<'_> = method;
227 Err(ContractError::InternalError)
228 }
229 }
230 }
231}