ic_kit_sys/
ic0.rs

1#[cfg(not(target_family = "wasm"))]
2thread_local!(static HANDLER: std::cell::RefCell<Option<Box<dyn Ic0CallHandler>>> = std::cell::RefCell::new(None));
3
4/// Register a handler to be used for handling the canister call in non-wasm environments.
5///
6/// # Panics
7///
8/// If called from within a canister.
9#[cfg(not(target_family = "wasm"))]
10pub fn register_handler<H: Ic0CallHandler + 'static>(handler: H) {
11    HANDLER.with(|c| {
12        let _ = c.borrow_mut().insert(Box::new(handler));
13    });
14}
15
16macro_rules! _ic0_module_ret {
17    ( ( $_: ident : $t: ty ) ) => {
18        $t
19    };
20    ( ( $_i1: ident : $t1: ty , $_i2: ident : $t2: ty) ) => {
21        ($t1, $t2)
22    };
23    ( ( $t: ty ) ) => {
24        $t
25    };
26    ( $t: ty ) => {
27        $t
28    };
29}
30
31macro_rules! ic0_module {
32    ( $(     ic0. $name: ident : ( $( $argname: ident : $argtype: ty ),* ) -> $rettype: tt ; )+ ) => {
33        #[allow(improper_ctypes)]
34        #[cfg(target_family = "wasm")]
35        #[link(wasm_import_module = "ic0")]
36        extern "C" {
37            $(pub fn $name($( $argname: $argtype, )*) -> _ic0_module_ret!($rettype) ;)*
38        }
39
40        /// An object that implements mock handlers for ic0 WASM API calls.
41        #[cfg(not(target_family = "wasm"))]
42        pub trait Ic0CallHandler {
43            $(
44            fn $name(&mut self, $($argname: $argtype,)*) -> _ic0_module_ret!($rettype);
45            )*
46        }
47
48        /// The runtime module provides the tools to have the canister in one thread and communicate
49        /// with another handler on another thread.
50        #[cfg(not(target_family = "wasm"))]
51        pub mod runtime {
52            use futures::executor::block_on;
53            use super::Ic0CallHandler;
54
55            /// A response from the runtime to the canister.
56            #[derive(Debug)]
57            pub enum Response {
58                None,
59                Isize(isize),
60                I32(i32),
61                I64(i64),
62                Trap(String),
63            }
64
65            impl From<()> for Response {
66                #[inline(always)]
67                fn from(_: ()) -> Self {
68                    Response::None
69                }
70            }
71
72            impl Into<()> for Response {
73                #[inline(always)]
74                fn into(self) -> () {
75                    match self {
76                        Response::None => (),
77                        Response::Trap(m) => panic!("Canister trapped: {}", m),
78                        _ => panic!("unexpected type cast."),
79                    }
80                }
81            }
82
83            impl From<isize> for Response {
84                #[inline(always)]
85                fn from(n: isize) -> Self {
86                    Response::Isize(n)
87                }
88            }
89
90            impl Into<isize> for Response {
91                #[inline(always)]
92                fn into(self) -> isize {
93                    match self {
94                        Response::Isize(n) => n,
95                        Response::Trap(m) => panic!("Canister trapped: {}", m),
96                        _ => panic!("unexpected type cast."),
97                    }
98                }
99            }
100
101            impl From<i32> for Response {
102                #[inline(always)]
103                fn from(n: i32) -> Self {
104                    Response::I32(n)
105                }
106            }
107
108            impl Into<i32> for Response {
109                #[inline(always)]
110                fn into(self) -> i32 {
111                    match self {
112                        Response::I32(n) => n,
113                        Response::Trap(m) => panic!("Canister trapped: {}", m),
114                        _ => panic!("unexpected type cast."),
115                    }
116                }
117            }
118
119            impl From<i64> for Response {
120                #[inline(always)]
121                fn from(n: i64) -> Self {
122                    Response::I64(n)
123                }
124            }
125
126            impl Into<i64> for Response {
127                #[inline(always)]
128                fn into(self) -> i64 {
129                    match self {
130                        Response::I64(n) => n,
131                        Response::Trap(m) => panic!("Canister trapped: {}", m),
132                        _ => panic!("unexpected type cast."),
133                    }
134                }
135            }
136
137            impl From<String> for Response {
138                #[inline(always)]
139                fn from(m: String) -> Self {
140                    Response::Trap(m)
141                }
142            }
143
144            /// The Ic0CallHandler on the main thread.
145            pub trait Ic0CallHandlerProxy {
146                $(
147                fn $name(&mut self, $($argname: $argtype,)*) -> Result<_ic0_module_ret!($rettype), String>;
148                )*
149            }
150
151            /// A request from the canister to the handler.
152            #[derive(Debug)]
153            #[allow(non_camel_case_types)]
154            pub enum Request {
155                $(
156                $name {
157                    $($argname: $argtype,)*
158                },
159                )*
160            }
161
162            impl Request {
163                /// Forward a request to a proxy
164                #[inline(always)]
165                pub fn proxy<H: Ic0CallHandlerProxy>(self, handler: &mut H) -> Response {
166                    match self {
167                        $(
168                        Request::$name { $($argname,)* } => handler.$name($($argname,)*)
169                            .map(Response::from)
170                            .unwrap_or_else(Response::from),
171                        )*
172                    }
173                }
174            }
175
176            /// A [`Ic0CallHandler`] that uses tokio mpsc channels to proxy system api calls
177            /// to another handler in another thread.
178            pub struct RuntimeHandle {
179                rx: tokio::sync::mpsc::Receiver<Response>,
180                tx: tokio::sync::mpsc::Sender<Request>,
181            }
182
183            impl RuntimeHandle {
184                pub fn new(
185                    rx: tokio::sync::mpsc::Receiver<Response>,
186                    tx: tokio::sync::mpsc::Sender<Request>,
187                ) -> Self {
188                    Self {
189                        rx,
190                        tx
191                    }
192                }
193            }
194
195            impl Ic0CallHandler for RuntimeHandle {
196                $(
197                fn $name(&mut self, $($argname: $argtype,)*) -> _ic0_module_ret!($rettype) {
198                    block_on(async {
199                        self.tx
200                            .send(Request::$name {$($argname,)*})
201                            .await
202                            .expect("ic-kit-runtime: Failed to send message from canister thread.");
203                        self.rx.recv().await.expect("Channel closed").into()
204                    })
205                }
206                )*
207            }
208        }
209
210        $(
211        #[cfg(not(target_family = "wasm"))]
212        pub unsafe fn $name($( $argname: $argtype, )*) -> _ic0_module_ret!($rettype) {
213            HANDLER.with(|handler| {
214                std::cell::RefMut::map(handler.borrow_mut(), |h| {
215                    h.as_mut().expect("No handler set for current thread.")
216                })
217                .$name($( $argname, )*)
218            })
219        }
220        )*
221    };
222}
223
224// Copy-paste the spec section of the API here.
225// https://github.com/dfinity/interface-spec/blob/master/spec/ic0.txt
226// But change any i32 which is an address space to an isize, so we can work with this even when
227// on 64bit non-wasm runtimes.
228//
229// The comment after each function lists from where these functions may be invoked:
230// I: from canister_init or canister_post_upgrade
231// G: from canister_pre_upgrade
232// U: from canister_update …
233// Q: from canister_query …
234// Ry: from a reply callback
235// Rt: from a reject callback
236// C: from a cleanup callback
237// s: the (start) module initialization function
238// F: from canister_inspect_message
239// H: from canister_heartbeat
240// * = I G U Q Ry Rt C F H (NB: Not (start))
241ic0_module! {
242    ic0.msg_arg_data_size : () -> isize;                                               // I U Q Ry F
243    ic0.msg_arg_data_copy : (dst : isize, offset : isize, size : isize) -> ();         // I U Q Ry F
244    ic0.msg_caller_size : () -> isize;                                                 // I G U Q F
245    ic0.msg_caller_copy : (dst : isize, offset: isize, size : isize) -> ();            // I G U Q F
246    ic0.msg_reject_code : () -> i32;                                                   // Ry Rt
247    ic0.msg_reject_msg_size : () -> isize;                                             // Rt
248    ic0.msg_reject_msg_copy : (dst : isize, offset : isize, size : isize) -> ();       // Rt
249
250    ic0.msg_reply_data_append : (src : isize, size : isize) -> ();                     // U Q Ry Rt
251    ic0.msg_reply : () -> ();                                                          // U Q Ry Rt
252    ic0.msg_reject : (src : isize, size : isize) -> ();                                // U Q Ry Rt
253
254    ic0.msg_cycles_available : () -> i64;                                              // U Rt Ry
255    ic0.msg_cycles_available128 : (dst : isize) -> ();                                 // U Rt Ry
256    ic0.msg_cycles_refunded : () -> i64;                                               // Rt Ry
257    ic0.msg_cycles_refunded128 : (dst : isize) -> ();                                  // Rt Ry
258    ic0.msg_cycles_accept : (max_amount : i64) -> (amount : i64);                      // U Rt Ry
259    ic0.msg_cycles_accept128 : (max_amount_high : i64, max_amount_low: i64, dst : isize)
260                           -> ();                                                      // U Rt Ry
261
262    ic0.canister_self_size : () -> isize;                                              // *
263    ic0.canister_self_copy : (dst : isize, offset : isize, size : isize) -> ();        // *
264    ic0.canister_cycle_balance : () -> i64;                                            // *
265    ic0.canister_cycle_balance128 : (dst : isize) -> ();                               // *
266    ic0.canister_status : () -> i32;                                                   // *
267
268    ic0.msg_method_name_size : () -> isize;                                            // F
269    ic0.msg_method_name_copy : (dst : isize, offset : isize, size : isize) -> ();      // F
270    ic0.accept_message : () -> ();                                                     // F
271
272    ic0.call_new :                                                                     // U Ry Rt H
273      ( callee_src  : isize,
274        callee_size : isize,
275        name_src : isize,
276        name_size : isize,
277        reply_fun : isize,
278        reply_env : isize,
279        reject_fun : isize,
280        reject_env : isize
281      ) -> ();
282    ic0.call_on_cleanup : (fun : isize, env : isize) -> ();                            // U Ry Rt H
283    ic0.call_data_append : (src : isize, size : isize) -> ();                          // U Ry Rt H
284    ic0.call_cycles_add : (amount : i64) -> ();                                        // U Ry Rt H
285    ic0.call_cycles_add128 : (amount_high : i64, amount_low: i64) -> ();               // U Ry Rt H
286    ic0.call_perform : () -> ( err_code : i32 );                                       // U Ry Rt H
287
288    ic0.stable_size : () -> (page_count : i32);                                        // *
289    ic0.stable_grow : (new_pages : i32) -> (old_page_count : i32);                     // *
290    ic0.stable_write : (offset : i32, src : isize, size : isize) -> ();                // *
291    ic0.stable_read : (dst : isize, offset : i32, size : isize) -> ();                 // *
292    ic0.stable64_size : () -> (page_count : i64);                                      // *
293    ic0.stable64_grow : (new_pages : i64) -> (old_page_count : i64);                   // *
294    ic0.stable64_write : (offset : i64, src : i64, size : i64) -> ();                  // *
295    ic0.stable64_read : (dst : i64, offset : i64, size : i64) -> ();                   // *
296
297    ic0.certified_data_set : (src: isize, size: isize) -> ();                          // I G U Ry Rt H
298    ic0.data_certificate_present : () -> i32;                                          // *
299    ic0.data_certificate_size : () -> isize;                                           // *
300    ic0.data_certificate_copy : (dst: isize, offset: isize, size: isize) -> ();        // *
301
302    ic0.time : () -> (timestamp : i64);                                                // *
303    ic0.performance_counter : (counter_type : i32) -> (counter : i64);                 // * s
304
305    ic0.debug_print : (src : isize, size : isize) -> ();                               // * s
306    ic0.trap : (src : isize, size : isize) -> ();                                      // * s
307}