rust2go_common/
r2g.rs

1// Copyright 2024 ihciah. All Rights Reserved.
2
3use std::collections::HashMap;
4
5use proc_macro2::{Span, TokenStream};
6use quote::{format_ident, quote};
7use syn::{
8    Error, FnArg, Ident, ItemTrait, Meta, Pat, Path, Result, ReturnType, Token, TraitItem, Type,
9};
10
11use crate::common::{Param, ParamType};
12
13pub struct R2GTraitRepr {
14    name: Ident,
15    fns: Vec<R2GFnRepr>,
16}
17
18impl TryFrom<&ItemTrait> for R2GTraitRepr {
19    type Error = Error;
20
21    fn try_from(trat: &ItemTrait) -> Result<Self> {
22        let trait_name = trat.ident.clone();
23        let mut fns = Vec::new();
24
25        let mut mem_cnt = 0;
26        for item in trat.items.iter() {
27            let TraitItem::Fn(fn_item) = item else {
28                sbail!("only fn items are supported");
29            };
30            let fn_name = fn_item.sig.ident.clone();
31            let mut params = Vec::new();
32            for param in fn_item.sig.inputs.iter() {
33                let FnArg::Typed(param) = param else {
34                    sbail!("only typed fn args are supported")
35                };
36                // param name
37                let Pat::Ident(param_name) = param.pat.as_ref() else {
38                    sbail!("only ident fn args are supported");
39                };
40                // param type
41                let param_type = ParamType::try_from(param.ty.as_ref())?;
42                params.push(Param {
43                    name: param_name.ident.clone(),
44                    ty: param_type,
45                });
46            }
47            let mut is_async = fn_item.sig.asyncness.is_some();
48            let ret = match &fn_item.sig.output {
49                ReturnType::Default => None,
50                ReturnType::Type(_, t) => match t.as_ref() {
51                    Type::Path(_) => {
52                        let param_type = ParamType::try_from(t.as_ref())?;
53                        Some(param_type)
54                    }
55                    // Check if it's a future.
56                    Type::ImplTrait(i) => {
57                        let t_str = i
58                            .bounds
59                            .iter()
60                            .find_map(|b| match b {
61                                syn::TypeParamBound::Trait(t) => {
62                                    let last_seg = t.path.segments.last().unwrap();
63                                    if last_seg.ident != "Future" {
64                                        return None;
65                                    }
66                                    // extract the Output type of the future.
67                                    let arg = match &last_seg.arguments {
68                                        syn::PathArguments::AngleBracketed(a)
69                                            if a.args.len() == 1 =>
70                                        {
71                                            a.args.first().unwrap()
72                                        }
73                                        _ => return None,
74                                    };
75                                    match arg {
76                                        syn::GenericArgument::AssocType(t)
77                                            if t.ident == "Output" =>
78                                        {
79                                            // extract the type of the Output.
80                                            let ret = Some(ParamType::try_from(&t.ty).unwrap());
81                                            if is_async {
82                                                panic!("async cannot be used with impl Future");
83                                            }
84                                            is_async = true;
85                                            ret
86                                        }
87                                        _ => None,
88                                    }
89                                }
90                                _ => None,
91                            })
92                            .ok_or_else(|| serr!("only future types are supported"))?;
93                        Some(t_str)
94                    }
95                    _ => sbail!("only path type or impl trait returns are supported"),
96                },
97            };
98            if is_async && ret.is_none() {
99                sbail!("async function must have a return value")
100            }
101
102            // on async mode, parse attributes to check it's drop safe setting.
103            let mut drop_safe_ret_params = false;
104            let mut ret_send = false;
105
106            let mut is_safe = true;
107            let has_reference = params.iter().any(|param| param.ty.is_reference);
108
109            if is_async {
110                let drop_safe = fn_item
111                .attrs
112                .iter()
113                .any(|attr|
114                    matches!(&attr.meta, Meta::Path(p) if p.get_ident() == Some(&format_ident!("drop_safe")))
115                );
116                drop_safe_ret_params = fn_item
117                .attrs
118                .iter()
119                .any(|attr|
120                    matches!(&attr.meta, Meta::Path(p) if p.get_ident() == Some(&format_ident!("drop_safe_ret")))
121                );
122                ret_send = fn_item
123                .attrs
124                .iter()
125                .any(|attr|
126                    matches!(&attr.meta, Meta::Path(p) if p.get_ident() == Some(&format_ident!("send")))
127                );
128
129                if !drop_safe && !drop_safe_ret_params {
130                    is_safe = false;
131                }
132                if (drop_safe || drop_safe_ret_params) && has_reference {
133                    sbail!("drop_safe function cannot have reference parameters")
134                }
135            }
136
137            let go_ptr = fn_item
138                .attrs
139                .iter()
140                .all(|attr|
141                    matches!(&attr.meta, Meta::Path(p) if p.get_ident() != Some(&format_ident!("go_pass_struct")))
142                );
143
144            let using_mem = fn_item
145                .attrs
146                .iter()
147                .any(|attr|
148                    matches!(&attr.meta, Meta::Path(p) if p.get_ident() == Some(&format_ident!("mem")) || p.get_ident() == Some(&format_ident!("shm")))
149                );
150            let cgo_cb = fn_item
151                .attrs
152                .iter()
153                .any(|attr|
154                    matches!(&attr.meta, Meta::Path(p) if p.get_ident() == Some(&format_ident!("cgo_callback")) || p.get_ident() == Some(&format_ident!("cgo")))
155                );
156            if using_mem && !is_async {
157                if ret.is_some() {
158                    sbail!("function based on shm must be async or without return value")
159                } else {
160                    is_safe = false;
161                }
162            }
163            let mem_call_id = if using_mem {
164                let id = mem_cnt;
165                mem_cnt += 1;
166                Some(id)
167            } else {
168                None
169            };
170
171            fns.push(R2GFnRepr {
172                name: fn_name,
173                is_async,
174                params,
175                ret,
176                is_safe,
177                drop_safe_ret_params,
178                ret_send,
179                ret_static: !has_reference,
180                cgo_cb,
181                go_ptr,
182                mem_call_id,
183            });
184        }
185        Ok(R2GTraitRepr {
186            name: trait_name,
187            fns,
188        })
189    }
190}
191
192pub struct R2GFnRepr {
193    name: Ident,
194    is_async: bool,
195    params: Vec<Param>,
196    ret: Option<ParamType>,
197    is_safe: bool,
198    drop_safe_ret_params: bool,
199    ret_send: bool,
200    ret_static: bool,
201    go_ptr: bool,
202    cgo_cb: bool,
203    mem_call_id: Option<usize>,
204}
205
206impl R2GTraitRepr {
207    pub fn fns(&self) -> &[R2GFnRepr] {
208        &self.fns
209    }
210
211    // Generate golang exports.
212    pub fn generate_go_exports(&self, levels: &HashMap<Ident, u8>) -> String {
213        let name = self.name.to_string();
214        let mut out: String = self
215            .fns
216            .iter()
217            .map(|f| f.to_go_export(&name, levels))
218            .collect();
219        let shm_cnt = self.fns.iter().filter(|f| f.mem_call_id.is_some()).count();
220        if shm_cnt != 0 {
221            let mem_ffi_handles = (0..shm_cnt)
222                .map(|id| format!("ringHandle{name}{id}"))
223                .collect::<Vec<String>>();
224            out.push_str(&format!("//export RingsInit{name}\nfunc RingsInit{name}(crr, crw C.QueueMeta) {{\nringsInit(crr, crw, []func(unsafe.Pointer, *ants.MultiPool, func(interface{{}}, []byte, uint)){{{}}})\n}}\n", mem_ffi_handles.join(",")));
225        }
226        out
227    }
228
229    // Generate golang interface.
230    pub fn generate_go_interface(&self) -> String {
231        // var DemoCallImpl DemoCall
232        // type DemoCall interface {
233        //     demo_oneway(req DemoUser)
234        //     demo_check(req DemoComplicatedRequest) DemoResponse
235        //     demo_check_async(req DemoComplicatedRequest) DemoResponse
236        // }
237        let name = self.name.to_string();
238        let fns = self.fns.iter().map(|f| f.to_go_interface_method());
239
240        let mut out = String::new();
241        out.push_str(&format!("var {name}Impl {name}\n"));
242        out.push_str(&format!("type {name} interface {{\n"));
243        for f in fns {
244            out.push_str(&f);
245            out.push('\n');
246        }
247        out.push_str("}\n");
248        out
249    }
250
251    // Generate rust impl, callbacks and binding mod include.
252    pub fn generate_rs(
253        &self,
254        binding_path: Option<&Path>,
255        queue_size: Option<usize>,
256    ) -> Result<TokenStream> {
257        const DEFAULT_BINDING_MOD: &str = "binding";
258        let path_prefix = match binding_path {
259            Some(p) => quote! {#p::},
260            None => {
261                let binding_mod = format_ident!("{DEFAULT_BINDING_MOD}");
262                quote! {#binding_mod::}
263            }
264        };
265        let (mut fn_trait_impls, mut fn_callbacks) = (
266            Vec::with_capacity(self.fns.len()),
267            Vec::with_capacity(self.fns.len()),
268        );
269        for f in self.fns.iter() {
270            fn_trait_impls.push(f.to_rs_impl(&self.name, &path_prefix)?);
271            fn_callbacks.push(f.to_rs_callback(&path_prefix)?);
272        }
273
274        let trait_name = &self.name;
275        let impl_struct_name = format_ident!("{}Impl", trait_name);
276
277        let mem_init_ffi = format_ident!("RingsInit{}", trait_name);
278        let mut shm_init = None;
279        let mut shm_init_extc = None;
280        let mem_cnt = self.fns.iter().filter(|f| f.mem_call_id.is_some()).count();
281        let queue_size = queue_size.unwrap_or(4096);
282        if mem_cnt != 0 {
283            let mem_ffi_handles = (0..mem_cnt).map(|id| format_ident!("mem_ffi_handle{}", id));
284            shm_init = Some(quote! {
285                ::std::thread_local! {
286                    static WS: (::rust2go_mem_ffi::WriteQueue<::rust2go_mem_ffi::Payload>, ::rust2go_mem_ffi::SharedSlab) = {
287                        unsafe {::rust2go_mem_ffi::init_mem_ffi(#mem_init_ffi as *const (), #queue_size, [#(#impl_struct_name::#mem_ffi_handles),*])}
288                    };
289                }
290            });
291            shm_init_extc = Some(quote! {
292                extern "C" {
293                    fn #mem_init_ffi(rr: ::rust2go_mem_ffi::QueueMeta, rw: ::rust2go_mem_ffi::QueueMeta);
294                }
295            })
296        }
297
298        Ok(quote! {
299            #shm_init_extc
300            pub struct #impl_struct_name;
301            impl #trait_name for #impl_struct_name {
302                #(#fn_trait_impls)*
303            }
304            impl #impl_struct_name {
305                #shm_init
306                #(#fn_callbacks)*
307            }
308        })
309    }
310}
311
312impl R2GFnRepr {
313    pub const fn name(&self) -> &Ident {
314        &self.name
315    }
316
317    pub const fn is_async(&self) -> bool {
318        self.is_async
319    }
320
321    pub const fn drop_safe_ret_params(&self) -> bool {
322        self.drop_safe_ret_params
323    }
324
325    pub const fn is_safe(&self) -> bool {
326        self.is_safe
327    }
328
329    pub fn params(&self) -> &[Param] {
330        &self.params
331    }
332
333    pub fn ret(&self) -> Option<&ParamType> {
334        self.ret.as_ref()
335    }
336
337    pub const fn ret_send(&self) -> bool {
338        self.ret_send
339    }
340
341    pub const fn ret_static(&self) -> bool {
342        self.ret_static
343    }
344
345    pub const fn mem_call_id(&self) -> Option<usize> {
346        self.mem_call_id
347    }
348
349    pub const fn cgo_callback(&self) -> bool {
350        self.cgo_cb
351    }
352
353    fn to_go_export(&self, trait_name: &str, levels: &HashMap<Ident, u8>) -> String {
354        let ref_mark = BoolMark::new(self.go_ptr, "&");
355        if let Some(mem_call_id) = self.mem_call_id {
356            let fn_sig = format!("func ringHandle{trait_name}{mem_call_id}(ptr unsafe.Pointer, pool *ants.MultiPool, post_func func(interface{{}}, []byte, uint)) {{\n");
357            let Some(ret) = &self.ret else {
358                return format!("{fn_sig}post_func(nil, nil, 0)\n}}\n");
359            };
360
361            let mut fn_body = String::new();
362            let params_len = self.params().len();
363            for (idx, p) in self.params().iter().enumerate() {
364                fn_body.push_str(&format!(
365                    "{name}:=*(*C.{ref_type})(ptr)\n",
366                    name = p.name,
367                    ref_type = p.ty.to_c(false)
368                ));
369                if idx + 1 != params_len {
370                    fn_body.push_str(&format!(
371                        "ptr=unsafe.Pointer(uintptr(ptr)+unsafe.Sizeof({name}))\n",
372                        name = p.name
373                    ));
374                }
375                fn_body.push_str(&format!(
376                    "{name}_:={cvt}({name})\n",
377                    name = p.name,
378                    cvt = p.ty.c_to_go_field_converter(levels).0
379                ));
380            }
381            fn_body.push_str("pool.Submit(func() {\n");
382            fn_body.push_str(&format!(
383                "resp := {trait_name}Impl.{fn_name}({ref_mark}{params})\n",
384                fn_name = self.name,
385                params = self
386                    .params
387                    .iter()
388                    .map(|p| format!("{}_", p.name))
389                    .collect::<Vec<_>>()
390                    .join(", ")
391            ));
392            fn_body.push_str(&format!(
393                "resp_ref_size := uint(unsafe.Sizeof(C.{}{{}}))\n",
394                ret.to_c(false)
395            ));
396            let (g2c_cnt, g2c_cvt) = (
397                ret.go_to_c_field_counter(levels).0,
398                ret.go_to_c_field_converter(levels).0,
399            );
400            fn_body.push_str(&format!("resp_ref, buffer := cvt_ref_cap({g2c_cnt}, {g2c_cvt}, resp_ref_size)(&resp)\noffset := uint(len(buffer))\nbuffer = append(buffer, unsafe.Slice((*byte)(unsafe.Pointer(&resp_ref)), resp_ref_size)...)\n"));
401            fn_body.push_str("post_func(resp, buffer, offset)\n})\n");
402            let fn_ending = "}\n";
403            return format!("{fn_sig}{fn_body}{fn_ending}");
404        }
405
406        let mut out = String::new();
407        let fn_name = format!("C{}_{}", trait_name, self.name);
408        out.push_str(&format!("//export {fn_name}\nfunc {fn_name}("));
409        self.params
410            .iter()
411            .for_each(|p| out.push_str(&format!("{} C.{}, ", p.name, p.ty.to_c(false))));
412
413        let mut new_names = Vec::new();
414        let mut new_cvt = String::new();
415        for p in self.params.iter() {
416            let new_name = format_ident!("_new_{}", p.name);
417            let cvt = p.ty.c_to_go_field_converter(levels).0;
418            new_cvt.push_str(&format!("{new_name} := {cvt}({})\n", p.name));
419            new_names.push(format!("{ref_mark}{}", new_name));
420        }
421        match (self.is_async, &self.ret) {
422            (true, None) => panic!("async function must have a return value"),
423            (false, None) => {
424                // //export CDemoCall_demo_oneway
425                // func CDemoCall_demo_oneway(req C.DemoUserRef) {
426                //     DemoCallImpl.demo_oneway(newDemoUser(req))
427                // }
428                out.push_str(") {\n");
429                out.push_str(&new_cvt);
430                out.push_str(&format!(
431                    "    {trait_name}Impl.{fn_name}({params})\n",
432                    fn_name = self.name,
433                    params = new_names.join(", ")
434                ));
435                out.push_str("}\n");
436            }
437            (false, Some(ret)) => {
438                // //export CDemoCall_demo_check
439                // func CDemoCall_demo_check(req C.DemoComplicatedRequestRef, slot *C.void, cb *C.void) {
440                //     resp := DemoCallImpl.demo_check(newDemoComplicatedRequest(req))
441                //     resp_ref, buffer := cvt_ref(cntDemoResponse, refDemoResponse)(&resp)
442                //     C.DemoCall_demo_check_cb(unsafe.Pointer(cb), &resp_ref, unsafe.Pointer(slot))
443                //     runtime.KeepAlive(resp_ref)
444                //     runtime.KeepAlive(resp)
445                //     runtime.KeepAlive(buffer)
446                // }
447                out.push_str("slot *C.void, cb *C.void) {\n");
448                out.push_str(&new_cvt);
449                out.push_str(&format!(
450                    "resp := {trait_name}Impl.{fn_name}({params})\n",
451                    fn_name = self.name,
452                    params = new_names.join(", ")
453                ));
454                let (g2c_cnt, g2c_cvt) = (
455                    ret.go_to_c_field_counter(levels).0,
456                    ret.go_to_c_field_converter(levels).0,
457                );
458                out.push_str(&format!(
459                    "resp_ref, buffer := cvt_ref({g2c_cnt}, {g2c_cvt})(&resp)\n"
460                ));
461                if self.cgo_cb {
462                    out.push_str("cgocall.CallFuncG0P2(unsafe.Pointer(cb), unsafe.Pointer(&resp_ref), unsafe.Pointer(slot))\n");
463                } else {
464                    out.push_str("asmcall.CallFuncG0P2(unsafe.Pointer(cb), unsafe.Pointer(&resp_ref), unsafe.Pointer(slot))\n");
465                }
466                out.push_str("runtime.KeepAlive(resp_ref)\nruntime.KeepAlive(resp)\nruntime.KeepAlive(buffer)\n");
467                out.push_str("}\n");
468            }
469            (true, Some(ret)) => {
470                // //export CDemoCall_demo_check_async
471                // func CDemoCall_demo_check_async(req C.DemoComplicatedRequestRef, slot *C.void, cb *C.void) {
472                //     _new_req := newDemoComplicatedRequest(req)
473                //     go func() {
474                //         resp := DemoCallImpl.demo_check_async(_new_req)
475                //         resp_ref, buffer := cvt_ref(cntDemoResponse, refDemoResponse)(&resp)
476                //         C.DemoCall_demo_check_async_cb(unsafe.Pointer(cb), &resp_ref, unsafe.Pointer(slot))
477                //         runtime.KeepAlive(resp)
478                //         runtime.KeepAlive(resp)
479                //         runtime.KeepAlive(buffer)
480                //     }()
481                // }
482                out.push_str("slot *C.void, cb *C.void) {\n");
483                out.push_str(&new_cvt);
484                out.push_str("    go func() {\n");
485                out.push_str(&format!(
486                    "resp := {trait_name}Impl.{fn_name}({params})\n",
487                    fn_name = self.name,
488                    params = new_names.join(", ")
489                ));
490                let (g2c_cnt, g2c_cvt) = (
491                    ret.go_to_c_field_counter(levels).0,
492                    ret.go_to_c_field_converter(levels).0,
493                );
494                out.push_str(&format!(
495                    "resp_ref, buffer := cvt_ref({g2c_cnt}, {g2c_cvt})(&resp)\n"
496                ));
497                if self.cgo_cb {
498                    out.push_str("cgocall.CallFuncG0P2(unsafe.Pointer(cb), unsafe.Pointer(&resp_ref), unsafe.Pointer(slot))\n");
499                } else {
500                    out.push_str("asmcall.CallFuncG0P2(unsafe.Pointer(cb), unsafe.Pointer(&resp_ref), unsafe.Pointer(slot))\n");
501                }
502                out.push_str("runtime.KeepAlive(resp_ref)\nruntime.KeepAlive(resp)\nruntime.KeepAlive(buffer)\n");
503                out.push_str("}()\n}\n");
504            }
505        }
506        out
507    }
508
509    fn to_go_interface_method(&self) -> String {
510        // demo_oneway(req DemoUser)
511        // demo_check(req DemoComplicatedRequest) DemoResponse
512        let star_mark = BoolMark::new(self.go_ptr, "*");
513        format!(
514            "{}({}) {}",
515            self.name,
516            self.params
517                .iter()
518                .map(|p| format!("{} {star_mark}{}", p.name, p.ty.to_go()))
519                .collect::<Vec<_>>()
520                .join(", "),
521            self.ret.as_ref().map(|p| p.to_go()).unwrap_or_default()
522        )
523    }
524
525    fn to_rs_impl(&self, trait_name: &Ident, path_prefix: &TokenStream) -> Result<TokenStream> {
526        let mut out = TokenStream::default();
527
528        let func_name = &self.name;
529        let callback_name = format_ident!("{func_name}_cb");
530        let func_param_names: Vec<_> = self.params.iter().map(|p| &p.name).collect();
531        let func_param_types: Vec<_> = self.params.iter().map(|p| &p.ty).collect();
532        let unsafe_marker = (!self.is_safe).then(syn::token::Unsafe::default);
533        out.extend(quote! {
534            #unsafe_marker fn #func_name(#(#func_param_names: #func_param_types),*)
535        });
536
537        let ref_marks = self.params.iter().map(|p| {
538            if p.ty.is_reference {
539                None
540            } else {
541                Some(Token![&](Span::call_site()))
542            }
543        });
544        let c_func_name = format_ident!("C{trait_name}_{func_name}");
545        match (self.is_async, &self.ret) {
546            (true, None) => sbail!("async function must have a return value"),
547            (false, None) => {
548                if let Some(mem_call_id) = self.mem_call_id {
549                    // fn demo_oneway(req: &DemoUser) {
550                    //     const CALL_ID: u32 = 0;
551                    //     let (buf, ptr) = ::rust2go::ToRef::calc_ref(&::rust2go::CopyStruct((&req,)));
552                    //     Self::WS.with(|(wq, slab)| {
553                    //         let slab = unsafe { &mut *slab.get() };
554                    //         let sid = slab.insert(::rust2go_mem_ffi::TaskDesc {
555                    //             buf,
556                    //             params_ptr: 0,
557                    //             slot_ptr: 0,
558                    //         });
559                    //         wq.push(::rust2go_mem_ffi::Payload::new_call(
560                    //             CALL_ID,
561                    //             sid,
562                    //             ptr as usize,
563                    //         ));
564                    //     });
565                    // }
566                    let mem_call_id = mem_call_id as u32;
567                    out.extend(quote! {
568                        {
569                            const CALL_ID: u32 = #mem_call_id;
570                            let (buf, ptr) = ::rust2go::ToRef::calc_ref(&::rust2go::CopyStruct((#(&#func_param_names,)*)));
571                            Self::WS.with(|(wq, sb)| {
572                                let sid = ::rust2go_mem_ffi::push_slab(sb, ::rust2go_mem_ffi::TaskDesc {
573                                    buf,
574                                    params_ptr: 0,
575                                    slot_ptr: 0,
576                                });
577                                wq.push(::rust2go_mem_ffi::Payload::new_call(
578                                    CALL_ID,
579                                    sid,
580                                    ptr as usize,
581                                ));
582                            });
583                        }
584                    });
585                } else {
586                    // fn demo_check(r: user::DemoRequest) {
587                    //     let (_buf, r) = ::rust2go::ToRef::calc_ref(&r);
588                    //     unsafe {binding::CDemoCall_demo_check(::std::mem::transmute(r))}
589                    // }
590                    out.extend(quote! {
591                        {
592                            #(
593                                let (_buf, #func_param_names) = ::rust2go::ToRef::calc_ref(#ref_marks #func_param_names);
594                            )*
595                            #[allow(clippy::useless_transmute)]
596                            unsafe {#path_prefix #c_func_name(#(::std::mem::transmute(#func_param_names)),*)}
597                        }
598                    });
599                }
600            }
601            (false, Some(ret)) => {
602                if self.mem_call_id.is_some() {
603                    sbail!("sync function with return value cannot be shm call")
604                }
605                // fn demo_check(r: user::DemoRequest) -> user::DemoResponse {
606                //     let mut slot = None;
607                //     let (_buf, r) = ::rust2go::ToRef::calc_ref(&r);
608                //     unsafe { binding::CDemoCall_demo_check(
609                //         ::std::mem::transmute(r),
610                //         &slot as *const _ as *const () as *mut _,
611                //         Self::demo_check_cb as *const () as *mut _,
612                //     )}
613                //     slot.take().unwrap()
614                // }
615
616                out.extend(quote!{
617                    -> #ret {
618                        let mut slot = None;
619                        #(
620                            let (_buf, #func_param_names) = ::rust2go::ToRef::calc_ref(#ref_marks #func_param_names);
621                        )*
622                        #[allow(clippy::useless_transmute)]
623                        unsafe { #path_prefix #c_func_name(#(::std::mem::transmute(#func_param_names),)* &slot as *const _ as *const () as *mut _, Self::#callback_name as *const () as *mut _) };
624                        slot.take().unwrap()
625                    }
626                });
627            }
628            (true, Some(ret)) => {
629                if let Some(mem_call_id) = self.mem_call_id {
630                    // const CALL_ID: u32 = 1;
631
632                    // let (buf, ptr) = ::rust2go::ToRef::calc_ref(&::rust2go::CopyStruct((&req,)));
633                    // let slot = ::std::rc::Rc::new(::std::cell::UnsafeCell::new(::rust2go::SlotInner::<
634                    //     DemoResponse,
635                    // >::new()));
636                    // let slot_ptr = ::std::rc::Rc::into_raw(slot.clone()) as usize;
637
638                    // Self::WS.with(|(wq, sb)| {
639                    //     let slab = unsafe { &mut *sb.get() };
640                    //     let sid = slab.insert(::rust2go_mem_ffi::TaskDesc {
641                    //         buf,
642                    //         params_ptr: Box::leak(Box::new((req,))) as *const _ as usize,
643                    //         slot_ptr,
644                    //     });
645                    //     let payload = ::rust2go_mem_ffi::Payload::new_call(CALL_ID, sid, ptr as usize);
646                    //     println!("[Rust] Send payload: {payload:?}");
647                    //     wq.push(payload)
648                    // });
649                    // ::rust2go::LocalFut { slot }
650                    let mem_call_id = mem_call_id as u32;
651                    let fut_output = if self.drop_safe_ret_params {
652                        quote! { (#ret, (#(#func_param_types,)*)) }
653                    } else {
654                        quote! { #ret }
655                    };
656                    out.extend(quote! {
657                        -> impl ::std::future::Future<Output = #fut_output> {
658                            const CALL_ID: u32 = #mem_call_id;
659
660                            let (buf, ptr) = ::rust2go::ToRef::calc_ref(&::rust2go::CopyStruct((#(&#func_param_names,)*)));
661                            let slot = ::rust2go_mem_ffi::new_shared_mut(::rust2go_mem_ffi::SlotInner::<#fut_output>::new());
662                            let slot_ptr = ::rust2go_mem_ffi::Shared::into_raw(slot.clone()) as usize;
663                            Self::WS.with(|(wq, sb)| {
664                                let sid = ::rust2go_mem_ffi::push_slab(sb, ::rust2go_mem_ffi::TaskDesc {
665                                    buf,
666                                    params_ptr: Box::into_raw(Box::new((#(#func_param_names,)*))) as usize,
667                                    slot_ptr,
668                                });
669                                let payload = ::rust2go_mem_ffi::Payload::new_call(CALL_ID, sid, ptr as usize);
670                                wq.push(payload)
671                            });
672                            ::rust2go_mem_ffi::LocalFut { slot }
673                        }
674                    });
675                } else {
676                    // fn demo_check_async(
677                    //     req: user::DemoRequest,
678                    // ) -> impl std::future::Future<Output = user::DemoResponse> {
679                    //     ::rust2go::ResponseFuture::Init(
680                    //         |r_ref: <(user::DemoRequest,) as ToRef>::Ref, slot: *const (), cb: *const ()| {
681                    //             unsafe {
682                    //                 binding::CDemoCall_demo_check_async(
683                    //                     ::std::mem::transmute(r_ref.0),
684                    //                     slot as *const _ as *mut _,
685                    //                     cb as *const _ as *mut _,
686                    //                 )
687                    //             };
688                    //         },
689                    //         (req,),
690                    //         Self::demo_check_async_cb as *const (),
691                    //     )
692                    // }
693                    let len = self.params.len();
694                    let tuple_ids = (0..len).map(syn::Index::from);
695                    let new_fn = match self.drop_safe_ret_params {
696                        false => quote! {::rust2go::ResponseFuture::new_without_req},
697                        true => quote! {::rust2go::ResponseFuture::new},
698                    };
699                    let ret = match self.drop_safe_ret_params {
700                        false => quote! { #ret },
701                        true => quote! { (#ret, (#(#func_param_types,)*)) },
702                    };
703                    out.extend(quote! {
704                        -> impl ::std::future::Future<Output = #ret> {
705                            #new_fn(
706                                |r_ref: <(#(#func_param_types,)*) as ::rust2go::ToRef>::Ref, slot: *const (), cb: *const ()| {
707                                    #[allow(clippy::useless_transmute)]
708                                    unsafe {
709                                        #path_prefix #c_func_name(
710                                            #(::std::mem::transmute(r_ref.#tuple_ids),)*
711                                            slot as *const _ as *mut _,
712                                            cb as *const _ as *mut _,
713                                        )
714                                    };
715                                },
716                                (#(#func_param_names,)*),
717                                Self::#callback_name as *const (),
718                            )
719                        }
720                    });
721                }
722            }
723        }
724        Ok(out)
725    }
726
727    fn to_rs_callback(&self, path_prefix: &TokenStream) -> Result<TokenStream> {
728        if let Some(mem_call_id) = self.mem_call_id {
729            let fn_name = format_ident!("mem_ffi_handle{}", mem_call_id);
730            let drop = if self.ret.is_some() {
731                quote! { true }
732            } else {
733                quote! { false }
734            };
735
736            let mut body = None;
737            if let Some(ret) = self.ret.as_ref() {
738                let resp_ref_ty = ret.to_rust_ref(None);
739                let reqs_ty = self.params().iter().map(|p| &p.ty);
740                let set_result = if self.drop_safe_ret_params {
741                    quote! {
742                        ::rust2go_mem_ffi::set_result_for_shared_mut_slot(&slot, (value, *_params));
743                    }
744                } else {
745                    quote! {
746                        ::rust2go_mem_ffi::set_result_for_shared_mut_slot(&slot, value);
747                    }
748                };
749                body = Some(quote! {
750                    let value_ref = unsafe { &*(response_ptr as *const #resp_ref_ty) };
751                    let value: #ret = ::rust2go::FromRef::from_ref(value_ref);
752
753                    let _params = unsafe { Box::from_raw(desc.params_ptr as *mut (#(#reqs_ty,)*)) };
754
755                    let slot = unsafe { ::rust2go_mem_ffi::shared_mut_from_raw(desc.slot_ptr) };
756                    #set_result
757                });
758            }
759
760            return Ok(quote! {
761                #[allow(unused_variables)]
762                fn #fn_name(response_ptr: usize, desc: ::rust2go_mem_ffi::TaskDesc) -> bool {
763                    #body
764                    #drop
765                }
766            });
767        }
768
769        let fn_name = format_ident!("{}_cb", self.name);
770
771        match (self.is_async, &self.ret) {
772            (true, None) => sbail!("async function must have a return value"),
773            (false, None) => {
774                // There's no need to generate callback for sync function without callback.
775                Ok(TokenStream::default())
776            }
777            (false, Some(ret)) => {
778                // #[no_mangle]
779                // unsafe extern "C" fn demo_check_cb(resp: *const binding::DemoResponseRef, slot: *const ()) {
780                //     *(slot as *mut Option<DemoResponse>) = Some(::rust2go::FromRef::from_ref(::std::mem::transmute(resp)));
781                // }
782                let resp_ref_ty = ret.to_rust_ref(Some(path_prefix));
783                Ok(quote! {
784                    #[allow(clippy::useless_transmute, clippy::transmute_ptr_to_ref)]
785                    #[no_mangle]
786                    unsafe extern "C" fn #fn_name(resp: *const #resp_ref_ty, slot: *const ()) {
787                        *(slot as *mut Option<#ret>) = Some(::rust2go::FromRef::from_ref(::std::mem::transmute(resp)));
788                    }
789                })
790            }
791            (true, Some(ret)) => {
792                // #[no_mangle]
793                // unsafe extern "C" fn demo_check_async_cb(
794                //     resp: *const binding::DemoResponseRef,
795                //     slot: *const (),
796                // ) {
797                //     ::rust2go::SlotWriter::<DemoResponse>::from_ptr(slot).write(::rust2go::FromRef::from_ref(::std::mem::transmute(resp)));
798                // }
799                let resp_ref_ty = ret.to_rust_ref(Some(path_prefix));
800                let func_param_types = self.params.iter().map(|p| &p.ty);
801                Ok(quote! {
802                    #[allow(clippy::useless_transmute, clippy::transmute_ptr_to_ref)]
803                    #[no_mangle]
804                    unsafe extern "C" fn #fn_name(resp: *const #resp_ref_ty, slot: *const ()) {
805                        ::rust2go::SlotWriter::<#ret, ((#(#func_param_types,)*), Vec<u8>)>::from_ptr(slot).write(::rust2go::FromRef::from_ref(::std::mem::transmute(resp)));
806                    }
807                })
808            }
809        }
810    }
811}
812
813struct BoolMark {
814    mark: bool,
815    fmt: &'static str,
816}
817impl BoolMark {
818    fn new(mark: bool, fmt: &'static str) -> Self {
819        Self { mark, fmt }
820    }
821}
822impl std::fmt::Display for BoolMark {
823    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
824        if self.mark {
825            return write!(f, "{}", self.fmt);
826        }
827        Ok(())
828    }
829}