rusty_bind_parser/
extern_functions_utils.rs

1//
2// Wildland Project
3//
4// Copyright © 2022 Golem Foundation,
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License version 3 as published by
8// the Free Software Foundation.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18use proc_macro2::{Ident, Span, TokenStream};
19use quote::quote;
20use syn::punctuated::Punctuated;
21use syn::{parse_quote, Block, FnArg, ItemFn, Token, Type};
22
23use crate::binding_types::*;
24use crate::EXPORTED_SYMBOLS_PREFIX;
25
26pub fn prepare_extern_function_signature(function: &Function) -> Punctuated<FnArg, Token![,]> {
27    let args = function.arguments.iter().map(|arg| {
28        let Arg { arg_name, typ } = arg;
29        let typ = typ.original_type_name.clone();
30        let arg_name = if arg_name == "self" {
31            Ident::new("_self", Span::call_site())
32        } else {
33            Ident::new(arg_name, Span::call_site())
34        };
35        let arg: FnArg = if (arg.typ.rust_type == RustWrapperType::Primitive
36            || arg.typ.rust_type == RustWrapperType::FieldlessEnum)
37            && arg.typ.reference_parameters.is_none()
38        {
39            parse_quote! { #arg_name: #typ }
40        } else if arg.typ.rust_type == RustWrapperType::Trait {
41            parse_quote! { #arg_name: *mut std::ffi::c_void }
42        } else {
43            parse_quote! { #arg_name: *mut #typ }
44        };
45        arg
46    });
47    parse_quote! { #(#args),* }
48}
49
50pub fn prepare_extern_function_body(
51    function: &Function,
52    associated_type: Option<Type>,
53) -> Box<Block> {
54    let Function {
55        arguments,
56        return_type,
57        name,
58    } = function;
59
60    let lifetime_source = if arguments.iter().any(|arg| {
61        matches!(
62            arg.typ.reference_parameters,
63            Some(ReferenceParameters {
64                is_static: false,
65                ..
66            })
67        ) && arg.typ.rust_type != RustWrapperType::Trait
68            && arg.arg_name != "self"
69    }) {
70        quote! { let mut lifetime_source = (); }
71    } else {
72        TokenStream::default()
73    };
74
75    let trait_cast = arguments.iter().filter_map(|elem| {
76        if elem.typ.rust_type == RustWrapperType::Trait {
77            let variable_name = Ident::new(&elem.arg_name, Span::call_site());
78            let type_name = Ident::new(&elem.typ.wrapper_name, Span::call_site());
79            Some(quote! { let #variable_name = Box::new(#type_name::new(#variable_name)); })
80        } else {
81            None
82        }
83    });
84
85    let args_list = arguments.iter().map(|arg| {
86        let arg_name = Ident::new(&arg.arg_name, Span::call_site());
87        if arg.typ.rust_type == RustWrapperType::Primitive
88            || arg.typ.rust_type == RustWrapperType::FieldlessEnum
89            || arg.typ.rust_type == RustWrapperType::Trait
90        {
91            quote! { #arg_name }
92        } else if let Some(reference_parameters) = &arg.typ.reference_parameters {
93            match reference_parameters {
94                ReferenceParameters {
95                    boxed: true,
96                    is_mut: true,
97                    is_static: true,
98                } => {
99                    quote! { &mut Box::from_raw(#arg_name) }
100                }
101                ReferenceParameters {
102                    is_static: true, ..
103                } => quote! { &*#arg_name },
104                ReferenceParameters { is_mut: true, .. } => {
105                    quote! { limit_lifetime_mut(&mut *#arg_name, &mut lifetime_source) }
106                }
107                _ => quote! { limit_lifetime(& *#arg_name, &lifetime_source) },
108            }
109        } else {
110            quote! { *Box::from_raw(#arg_name) }
111        }
112    });
113
114    let function_name = Ident::new(name, Span::call_site());
115
116    let call = if let Some(self_arg) = arguments.first().filter(|arg| arg.arg_name == "self") {
117        let args_list = args_list.skip(1);
118        if self_arg.typ.reference_parameters.is_some() {
119            let self_arg_ref = match self_arg.typ.rust_type {
120                RustWrapperType::ExceptionTrait => quote! { (& *_self) },
121                RustWrapperType::Custom => {
122                    if self_arg.typ.reference_parameters.to_owned().unwrap().is_mut {
123                        quote! { (&mut *_self) }
124                    } else {
125                        quote! { (&*_self) }
126                    }
127                }
128                _ => quote! { (&mut *_self) },
129            };
130            if self_arg.typ.rust_type == RustWrapperType::ArcMutex {
131                quote! { unsafe { #self_arg_ref.lock().unwrap().#function_name ( #(#args_list),* ) } }
132            } else {
133                quote! { unsafe { #self_arg_ref.#function_name ( #(#args_list),* ) } }
134            }
135        } else {
136            quote! { unsafe { Box::from_raw(_self).#function_name ( #(#args_list),* ) } }
137        }
138    } else if let Some(associated_type) = associated_type {
139        quote! { unsafe { #associated_type::#function_name ( #(#args_list),* ) } }
140    } else {
141        quote! { unsafe { super::#function_name ( #(#args_list),* ) } }
142    };
143
144    match return_type {
145        Some(WrapperType {
146            rust_type:
147                RustWrapperType::Primitive | RustWrapperType::FieldlessEnum | RustWrapperType::Ptr(_),
148            ..
149        })
150        | None => parse_quote! ( { #lifetime_source; #(#trait_cast)*; #call } ),
151
152        _ => {
153            parse_quote! ( { #lifetime_source; #(#trait_cast)*; Box::into_raw(Box::new(#call)) } )
154        }
155    }
156}
157
158pub fn prepare_extern_function_call_body(function: &Function, wrapper_name: &str) -> Box<Block> {
159    let Function {
160        arguments,
161        return_type,
162        name,
163    } = function;
164
165    let args_list = arguments.iter().map(|arg| {
166        let arg_name = Ident::new(&arg.arg_name, Span::call_site());
167
168        if arg.typ.rust_type == RustWrapperType::Trait {
169            quote! { #arg_name.0.load(std::sync::atomic::Ordering::Relaxed) }
170        } else if arg.typ.rust_type == RustWrapperType::Primitive
171            || arg.typ.rust_type == RustWrapperType::FieldlessEnum
172            || arg.typ.reference_parameters.is_some()
173        {
174            quote! { #arg_name }
175        } else {
176            quote! { Box::into_raw(Box::new(#arg_name)) }
177        }
178    });
179
180    let function_name = Ident::new(
181        &format!("{EXPORTED_SYMBOLS_PREFIX}{wrapper_name}_{name}"),
182        Span::call_site(),
183    );
184
185    let call = quote! { #function_name ( #(#args_list),* ) };
186
187    match return_type {
188        Some(WrapperType {
189            rust_type: RustWrapperType::Primitive | RustWrapperType::FieldlessEnum,
190            ..
191        })
192        | None => parse_quote! ( { unsafe { #call } } ),
193        _ => parse_quote! ( { unsafe { *Box::from_raw(#call) } } ),
194    }
195}
196
197pub fn prepare_extern_function_tokens(
198    block: Box<Block>,
199    return_type: Option<Type>,
200    function_name: &str,
201    signature: Punctuated<FnArg, Token![,]>,
202    class_name: Option<&str>,
203) -> (String, TokenStream) {
204    let class_name = class_name
205        .map(|class_name| format!("{class_name}$"))
206        .unwrap_or_else(|| "".to_owned());
207    let function_name = Ident::new(function_name, Span::call_site());
208    let extern_function_name = format!("{EXPORTED_SYMBOLS_PREFIX}${class_name}{function_name}");
209    let return_type = return_type.unwrap_or_else(|| parse_quote! { () });
210    (
211        extern_function_name.clone(),
212        quote! {
213            const _: () = {
214                #[doc(hidden)]
215                #[export_name = #extern_function_name]
216                pub extern "C" fn #function_name(#signature) -> #return_type
217                    #block
218            };
219        },
220    )
221}
222
223/// Generates a wrapped function declaration for each method in each user's
224/// trait type declaration.
225///
226pub fn generate_trait_methods<'a>(
227    functions: impl Iterator<Item = &'a Function>,
228    wrapper_name: &str,
229) -> Vec<ItemFn> {
230    functions
231        .map(|function| {
232            let args = function.arguments.iter().map(|arg| {
233                let arg_name = Ident::new(&arg.arg_name, Span::call_site());
234                let arg_type = &arg.typ.original_type_name;
235                let arg: FnArg = if arg.arg_name == "self" {
236                    let reference_parameters = arg.typ.reference_parameters.as_ref().unwrap();
237                    if reference_parameters.is_mut {
238                        parse_quote!(&mut self)
239                    } else {
240                        parse_quote!(&self)
241                    }
242                } else {
243                    parse_quote! (#arg_name: #arg_type)
244                };
245                arg
246            });
247            let function_name = Ident::new(&function.name, Span::call_site());
248            let return_type = function
249                .return_type
250                .as_ref()
251                .map(|typ| typ.original_type_name.clone())
252                .unwrap_or_else(|| parse_quote! {()});
253            let body = prepare_extern_function_call_body(function, wrapper_name);
254            parse_quote! ( fn #function_name ( #(#args),* ) -> #return_type #body )
255        })
256        .collect()
257}
258
259pub fn create_extern_function_for_custom_type(
260    class_name: &str,
261    function: &Function,
262) -> ExternFunction {
263    let block = prepare_extern_function_body(function, None);
264    let return_type = extern_function_return_type(function);
265    let function_name = &function.name;
266    let signature = prepare_extern_function_signature(function);
267    let (extern_function_name, tokens) = prepare_extern_function_tokens(
268        block,
269        return_type,
270        function_name,
271        signature,
272        Some(class_name),
273    );
274    ExternFunction {
275        arguments: function
276            .arguments
277            .iter()
278            .map(|arg| arg.typ.clone())
279            .collect(),
280        return_type: function.return_type.clone(),
281        name: extern_function_name,
282        tokens,
283    }
284}
285
286pub fn create_extern_function_for_exception_trait_method(
287    class_name: &str,
288    function: &Function,
289) -> ExternFunction {
290    let receiver = Ident::new("_self", Span::call_site());
291    let arg_name = Ident::new(class_name, Span::call_site());
292    let block = prepare_extern_function_body(function, None);
293    let return_type = extern_function_return_type(function);
294    let function_name = &function.name;
295
296    let signature = parse_quote! { #receiver: *const #arg_name };
297    let (extern_function_name, tokens) = prepare_extern_function_tokens(
298        block,
299        return_type,
300        function_name,
301        signature,
302        Some(class_name),
303    );
304    ExternFunction {
305        arguments: function
306            .arguments
307            .iter()
308            .map(|arg| arg.typ.clone())
309            .collect(),
310        return_type: function.return_type.clone(),
311        name: extern_function_name,
312        tokens,
313    }
314}
315
316pub fn create_extern_associated_function_for_custom_type(
317    wrapper: WrapperType,
318    function: &Function,
319) -> ExternFunction {
320    let block = prepare_extern_function_body(function, Some(wrapper.original_type_name));
321    let return_type = extern_function_return_type(function);
322    let function_name = &function.name;
323    let signature = prepare_extern_function_signature(function);
324    let (extern_function_name, tokens) = prepare_extern_function_tokens(
325        block,
326        return_type,
327        function_name,
328        signature,
329        Some(&wrapper.wrapper_name),
330    );
331    ExternFunction {
332        arguments: function
333            .arguments
334            .iter()
335            .map(|arg| arg.typ.clone())
336            .collect(),
337        return_type: function.return_type.clone(),
338        name: extern_function_name,
339        tokens,
340    }
341}
342
343pub fn create_clone_extern_function(wrapper: WrapperType) -> ExternFunction {
344    let block = parse_quote!({ unsafe { Box::into_raw(Box::new((*_self).clone())) } });
345    let original_type_name = &wrapper.original_type_name;
346    let signature: Punctuated<FnArg, Token![,]> = parse_quote! { _self: *mut #original_type_name };
347    let (extern_function_name, tokens) = prepare_extern_function_tokens(
348        block,
349        Some(parse_quote! { *mut #original_type_name }),
350        "clone",
351        signature,
352        Some(&wrapper.wrapper_name),
353    );
354    ExternFunction {
355        arguments: vec![wrapper.clone()],
356        return_type: Some(WrapperType {
357            original_type_name: parse_quote! { *mut #original_type_name },
358            wrapper_name: wrapper.wrapper_name,
359            rust_type: RustWrapperType::Custom,
360            reference_parameters: None,
361            impl_traits: vec![],
362        }),
363        name: extern_function_name,
364        tokens,
365    }
366}
367
368pub fn create_from_c_str_extern_function(wrapper: WrapperType) -> ExternFunction {
369    let block = parse_quote!({
370        unsafe {
371            Box::into_raw(Box::new(
372                std::ffi::CStr::from_ptr(_self).to_str().unwrap().to_owned(),
373            ))
374        }
375    });
376    let original_type_name = &wrapper.original_type_name;
377    let signature: Punctuated<FnArg, Token![,]> =
378        parse_quote! { _self: *const std::os::raw::c_char };
379    let (extern_function_name, tokens) = prepare_extern_function_tokens(
380        block,
381        Some(parse_quote! { *mut #original_type_name }),
382        "from_c_str",
383        signature,
384        Some(&wrapper.wrapper_name),
385    );
386    ExternFunction {
387        arguments: vec![wrapper.clone()],
388        return_type: Some(WrapperType {
389            original_type_name: parse_quote! { *mut String },
390            wrapper_name: wrapper.wrapper_name,
391            rust_type: RustWrapperType::String,
392            reference_parameters: None,
393            impl_traits: vec![],
394        }),
395        name: extern_function_name,
396        tokens,
397    }
398}
399
400pub fn create_drop_extern_function(wrapper: WrapperType) -> ExternFunction {
401    let block = parse_quote!({
402        unsafe {
403            #[allow(unused_must_use)]
404            {
405                Box::from_raw(_self);
406            }
407        }
408    });
409    let original_type_name = &wrapper.original_type_name;
410    let signature: Punctuated<FnArg, Token![,]> = parse_quote! { _self: *mut #original_type_name };
411    let (extern_function_name, tokens) =
412        prepare_extern_function_tokens(block, None, "drop", signature, Some(&wrapper.wrapper_name));
413    ExternFunction {
414        arguments: vec![wrapper.clone()],
415        return_type: None,
416        name: extern_function_name,
417        tokens,
418    }
419}
420
421pub fn create_get_from_vec_extern_function(
422    wrapper: WrapperType,
423    inner_type: WrapperType,
424) -> ExternFunction {
425    let inner_type_original = &inner_type.original_type_name;
426    let block = parse_quote!({
427        unsafe { Box::into_raw(Box::new(unsafe { (&*_self).get(index).cloned() })) }
428    });
429    let original_type_name = &wrapper.original_type_name;
430    let signature: Punctuated<FnArg, Token![,]> =
431        parse_quote! { _self: *mut #original_type_name, index: usize };
432    let (extern_function_name, tokens) = prepare_extern_function_tokens(
433        block,
434        Some(parse_quote! { *mut Option<#inner_type_original> }),
435        "get",
436        signature,
437        Some(&wrapper.wrapper_name),
438    );
439    ExternFunction {
440        arguments: vec![
441            wrapper.clone(),
442            WrapperType {
443                original_type_name: parse_quote! { usize },
444                wrapper_name: "usize".to_owned(),
445                rust_type: RustWrapperType::Primitive,
446                reference_parameters: None,
447                impl_traits: vec![],
448            },
449        ],
450        return_type: Some(WrapperType {
451            original_type_name: parse_quote! { *mut Option<#inner_type_original> },
452            wrapper_name: format!("Optional{}", inner_type.wrapper_name),
453            rust_type: RustWrapperType::Option(inner_type.boxed()),
454            reference_parameters: None,
455            impl_traits: vec![],
456        }),
457        name: extern_function_name,
458        tokens,
459    }
460}
461
462pub fn create_extern_global_function(function: &Function) -> ExternFunction {
463    let block = prepare_extern_function_body(function, None);
464    let return_type = extern_function_return_type(function);
465    let function_name = &function.name;
466    let signature = prepare_extern_function_signature(function);
467    let (extern_function_name, tokens) =
468        prepare_extern_function_tokens(block, return_type, function_name, signature, None);
469    ExternFunction {
470        arguments: function
471            .arguments
472            .iter()
473            .map(|arg| arg.typ.clone())
474            .collect(),
475        return_type: function.return_type.clone(),
476        name: extern_function_name,
477        tokens,
478    }
479}
480
481pub fn from_ok_extern_function(
482    wrapper: &WrapperType,
483    ok_wrapper_type: &WrapperType,
484) -> ExternFunction {
485    let result_wrapper_ident = &wrapper.wrapper_name;
486    let ok_type = &ok_wrapper_type.original_type_name;
487    let original_type_name = &wrapper.original_type_name;
488    let from_ok_extern_function_name =
489        format!("{EXPORTED_SYMBOLS_PREFIX}${result_wrapper_ident}$from_ok");
490    ExternFunction {
491        arguments: vec![ok_wrapper_type.clone()],
492        return_type: Some(wrapper.clone()),
493        name: from_ok_extern_function_name.clone(),
494        tokens: if ok_wrapper_type.rust_type != RustWrapperType::Primitive
495            && ok_wrapper_type.rust_type != RustWrapperType::FieldlessEnum
496        {
497            quote! (
498                const _: () = {
499                    #[doc(hidden)]
500                    #[export_name = #from_ok_extern_function_name]
501                    pub extern "C" fn from_ok(val: *mut #ok_type) -> *mut #original_type_name {
502                        Box::into_raw(unsafe { Box::new(Ok( *Box::from_raw(val))) } )
503                    }
504                };
505            )
506        } else {
507            quote! (
508                const _: () = {
509                    #[doc(hidden)]
510                    #[export_name = #from_ok_extern_function_name]
511                    pub extern "C" fn from_ok(val: #ok_type) -> *mut #original_type_name {
512                        Box::into_raw(Box::new(Ok(val)))
513                    }
514                };
515            )
516        },
517    }
518}
519
520pub fn from_err_extern_function(
521    wrapper: &WrapperType,
522    exceptions_wrapper_type: &WrapperType,
523) -> ExternFunction {
524    let result_wrapper_ident = &wrapper.wrapper_name;
525    let from_err_extern_function_name =
526        format!("{EXPORTED_SYMBOLS_PREFIX}${result_wrapper_ident}$from_err");
527    let exc_type = &exceptions_wrapper_type.original_type_name;
528    let original_type_name = &wrapper.original_type_name;
529    ExternFunction {
530        arguments: vec![exceptions_wrapper_type.clone()],
531        return_type: Some(wrapper.clone()),
532        name: from_err_extern_function_name.clone(),
533        tokens: if !matches!(
534            exceptions_wrapper_type.rust_type,
535            RustWrapperType::Exceptions(Exceptions::Primitive(_))
536        ) {
537            quote! (
538                const _: () = {
539                    #[doc(hidden)]
540                    #[export_name = #from_err_extern_function_name]
541                    pub extern "C" fn from_err(val: *mut #exc_type) -> *mut #original_type_name {
542                        Box::into_raw(unsafe { Box::new(Err(*Box::from_raw(val))) } )
543                    }
544                };
545            )
546        } else {
547            quote! (
548                const _: () = {
549                    #[doc(hidden)]
550                    #[export_name = #from_err_extern_function_name]
551                    pub extern "C" fn from_err(val: #exc_type) -> *mut #original_type_name {
552                        Box::into_raw(Box::new(Err(val)))
553                    }
554                };
555            )
556        },
557    }
558}
559
560fn extern_function_return_type(function: &Function) -> Option<Type> {
561    function
562        .return_type
563        .as_ref()
564        .map(|wrapper_type| match wrapper_type {
565            WrapperType {
566                original_type_name,
567                rust_type: RustWrapperType::Ptr(_inner),
568                ..
569            } => {
570                parse_quote! { #original_type_name }
571            }
572            WrapperType {
573                reference_parameters,
574                rust_type,
575                ..
576            } if reference_parameters.is_some()
577                || (*rust_type != RustWrapperType::Primitive
578                    && *rust_type != RustWrapperType::FieldlessEnum) =>
579            {
580                let original_type_name = &wrapper_type.original_type_name;
581                parse_quote! { *mut #original_type_name }
582            }
583            _ => wrapper_type.original_type_name.clone(),
584        })
585}