use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::punctuated::Punctuated;
use syn::{parse_quote, Block, FnArg, ItemFn, Token, Type};
use crate::binding_types::*;
use crate::EXPORTED_SYMBOLS_PREFIX;
pub fn prepare_extern_function_signature(function: &Function) -> Punctuated<FnArg, Token![,]> {
let args = function.arguments.iter().map(|arg| {
let Arg { arg_name, typ } = arg;
let typ = typ.original_type_name.clone();
let arg_name = if arg_name == "self" {
Ident::new("_self", Span::call_site())
} else {
Ident::new(arg_name, Span::call_site())
};
let arg: FnArg = if (arg.typ.rust_type == RustWrapperType::Primitive
|| arg.typ.rust_type == RustWrapperType::FieldlessEnum)
&& arg.typ.reference_parameters.is_none()
{
parse_quote! { #arg_name: #typ }
} else if arg.typ.rust_type == RustWrapperType::Trait {
parse_quote! { #arg_name: *mut std::ffi::c_void }
} else {
parse_quote! { #arg_name: *mut #typ }
};
arg
});
parse_quote! { #(#args),* }
}
pub fn prepare_extern_function_body(
function: &Function,
associated_type: Option<Type>,
) -> Box<Block> {
let Function {
arguments,
return_type,
name,
} = function;
let lifetime_source = if arguments.iter().any(|arg| {
matches!(
arg.typ.reference_parameters,
Some(ReferenceParameters {
is_static: false,
..
})
) && arg.typ.rust_type != RustWrapperType::Trait
&& arg.arg_name != "self"
}) {
quote! { let mut lifetime_source = (); }
} else {
TokenStream::default()
};
let trait_cast = arguments.iter().filter_map(|elem| {
if elem.typ.rust_type == RustWrapperType::Trait {
let variable_name = Ident::new(&elem.arg_name, Span::call_site());
let type_name = Ident::new(&elem.typ.wrapper_name, Span::call_site());
Some(quote! { let #variable_name = Box::new(#type_name::new(#variable_name)); })
} else {
None
}
});
let args_list = arguments.iter().map(|arg| {
let arg_name = Ident::new(&arg.arg_name, Span::call_site());
if arg.typ.rust_type == RustWrapperType::Primitive
|| arg.typ.rust_type == RustWrapperType::FieldlessEnum
|| arg.typ.rust_type == RustWrapperType::Trait
{
quote! { #arg_name }
} else if let Some(reference_parameters) = &arg.typ.reference_parameters {
match reference_parameters {
ReferenceParameters {
is_static: true, ..
} => quote! { &*#arg_name },
ReferenceParameters { is_mut: true, .. } => {
quote! { limit_lifetime_mut(&mut *#arg_name, &mut lifetime_source) }
}
_ => quote! { limit_lifetime(& *#arg_name, &lifetime_source) },
}
} else {
quote! { *Box::from_raw(#arg_name) }
}
});
let function_name = Ident::new(name, Span::call_site());
let call = if let Some(self_arg) = arguments.first().filter(|arg| arg.arg_name == "self") {
let args_list = args_list.skip(1);
if self_arg.typ.reference_parameters.is_some() {
let self_arg_ref = match self_arg.typ.rust_type {
RustWrapperType::ExceptionTrait => quote! { (& *_self) },
_ => quote! { (&mut *_self) },
};
if self_arg.typ.rust_type == RustWrapperType::ArcMutex {
quote! { unsafe { #self_arg_ref.lock().unwrap().#function_name ( #(#args_list),* ) } }
} else {
quote! { unsafe { #self_arg_ref.#function_name ( #(#args_list),* ) } }
}
} else {
quote! { unsafe { Box::from_raw(_self).#function_name ( #(#args_list),* ) } }
}
} else if let Some(associated_type) = associated_type {
quote! { unsafe { #associated_type::#function_name ( #(#args_list),* ) } }
} else {
quote! { unsafe { super::#function_name ( #(#args_list),* ) } }
};
match return_type {
Some(WrapperType {
rust_type: RustWrapperType::Primitive | RustWrapperType::FieldlessEnum,
..
})
| None => parse_quote! ( { #lifetime_source; #(#trait_cast)*; #call } ),
_ => {
parse_quote! ( { #lifetime_source; #(#trait_cast)*; Box::into_raw(Box::new(#call)) } )
}
}
}
pub fn prepare_extern_function_call_body(function: &Function, wrapper_name: &str) -> Box<Block> {
let Function {
arguments,
return_type,
name,
} = function;
let args_list = arguments.iter().map(|arg| {
let arg_name = Ident::new(&arg.arg_name, Span::call_site());
if arg.typ.rust_type == RustWrapperType::Trait {
quote! { #arg_name.0.load(std::sync::atomic::Ordering::Relaxed) }
} else if arg.typ.rust_type == RustWrapperType::Primitive
|| arg.typ.rust_type == RustWrapperType::FieldlessEnum
|| arg.typ.reference_parameters.is_some()
{
quote! { #arg_name }
} else {
quote! { Box::into_raw(Box::new(#arg_name)) }
}
});
let function_name = Ident::new(
&format!("{EXPORTED_SYMBOLS_PREFIX}{wrapper_name}_{name}"),
Span::call_site(),
);
let call = quote! { #function_name ( #(#args_list),* ) };
match return_type {
Some(WrapperType {
rust_type: RustWrapperType::Primitive | RustWrapperType::FieldlessEnum,
..
})
| None => parse_quote! ( { unsafe { #call } } ),
_ => parse_quote! ( { unsafe { *Box::from_raw(#call) } } ),
}
}
pub fn prepare_extern_function_tokens(
block: Box<Block>,
return_type: Option<Type>,
function_name: &str,
signature: Punctuated<FnArg, Token![,]>,
class_name: Option<&str>,
) -> (String, TokenStream) {
let class_name = class_name
.map(|class_name| format!("{class_name}$"))
.unwrap_or_else(|| "".to_owned());
let function_name = Ident::new(function_name, Span::call_site());
let extern_function_name = format!("{EXPORTED_SYMBOLS_PREFIX}${class_name}{function_name}");
let return_type = return_type.unwrap_or_else(|| parse_quote! { () });
(
extern_function_name.clone(),
quote! {
const _: () = {
#[doc(hidden)]
#[export_name = #extern_function_name]
pub extern "C" fn #function_name(#signature) -> #return_type
#block
};
},
)
}
pub fn generate_trait_methods<'a>(
functions: impl Iterator<Item = &'a Function>,
wrapper_name: &str,
) -> Vec<ItemFn> {
functions
.map(|function| {
let args = function.arguments.iter().map(|arg| {
let arg_name = Ident::new(&arg.arg_name, Span::call_site());
let arg_type = &arg.typ.original_type_name;
let arg: FnArg = if arg.arg_name == "self" {
let reference_parameters = arg.typ.reference_parameters.as_ref().unwrap();
if reference_parameters.is_mut {
parse_quote!(&mut self)
} else {
parse_quote!(&self)
}
} else {
parse_quote! (#arg_name: #arg_type)
};
arg
});
let function_name = Ident::new(&function.name, Span::call_site());
let return_type = function
.return_type
.as_ref()
.map(|typ| typ.original_type_name.clone())
.unwrap_or_else(|| parse_quote! {()});
let body = prepare_extern_function_call_body(function, wrapper_name);
parse_quote! ( fn #function_name ( #(#args),* ) -> #return_type #body )
})
.collect()
}
pub fn create_extern_function_for_custom_type(
class_name: &str,
function: &Function,
) -> ExternFunction {
let block = prepare_extern_function_body(function, None);
let return_type = extern_function_return_type(function);
let function_name = &function.name;
let signature = prepare_extern_function_signature(function);
let (extern_function_name, tokens) = prepare_extern_function_tokens(
block,
return_type,
function_name,
signature,
Some(class_name),
);
ExternFunction {
arguments: function
.arguments
.iter()
.map(|arg| arg.typ.clone())
.collect(),
return_type: function.return_type.clone(),
name: extern_function_name,
tokens,
}
}
pub fn create_extern_function_for_exception_trait_method(
class_name: &str,
function: &Function,
) -> ExternFunction {
let receiver = Ident::new("_self", Span::call_site());
let arg_name = Ident::new(class_name, Span::call_site());
let block = prepare_extern_function_body(function, None);
let return_type = extern_function_return_type(function);
let function_name = &function.name;
let signature = parse_quote! { #receiver: *const #arg_name };
let (extern_function_name, tokens) = prepare_extern_function_tokens(
block,
return_type,
function_name,
signature,
Some(class_name),
);
ExternFunction {
arguments: function
.arguments
.iter()
.map(|arg| arg.typ.clone())
.collect(),
return_type: function.return_type.clone(),
name: extern_function_name,
tokens,
}
}
pub fn create_extern_associated_function_for_custom_type(
wrapper: WrapperType,
function: &Function,
) -> ExternFunction {
let block = prepare_extern_function_body(function, Some(wrapper.original_type_name));
let return_type = extern_function_return_type(function);
let function_name = &function.name;
let signature = prepare_extern_function_signature(function);
let (extern_function_name, tokens) = prepare_extern_function_tokens(
block,
return_type,
function_name,
signature,
Some(&wrapper.wrapper_name),
);
ExternFunction {
arguments: function
.arguments
.iter()
.map(|arg| arg.typ.clone())
.collect(),
return_type: function.return_type.clone(),
name: extern_function_name,
tokens,
}
}
pub fn create_clone_extern_function(wrapper: WrapperType) -> ExternFunction {
let block = parse_quote!({ unsafe { Box::into_raw(Box::new((*_self).clone())) } });
let original_type_name = &wrapper.original_type_name;
let signature: Punctuated<FnArg, Token![,]> = parse_quote! { _self: *mut #original_type_name };
let (extern_function_name, tokens) = prepare_extern_function_tokens(
block,
Some(parse_quote! { *mut #original_type_name }),
"clone",
signature,
Some(&wrapper.wrapper_name),
);
ExternFunction {
arguments: vec![wrapper.clone()],
return_type: Some(WrapperType {
original_type_name: parse_quote! { *mut #original_type_name },
wrapper_name: wrapper.wrapper_name,
rust_type: RustWrapperType::Custom,
reference_parameters: None,
}),
name: extern_function_name,
tokens,
}
}
pub fn create_from_c_str_extern_function(wrapper: WrapperType) -> ExternFunction {
let block = parse_quote!({
unsafe {
Box::into_raw(Box::new(
std::ffi::CStr::from_ptr(_self).to_str().unwrap().to_owned(),
))
}
});
let original_type_name = &wrapper.original_type_name;
let signature: Punctuated<FnArg, Token![,]> =
parse_quote! { _self: *const std::os::raw::c_char };
let (extern_function_name, tokens) = prepare_extern_function_tokens(
block,
Some(parse_quote! { *mut #original_type_name }),
"from_c_str",
signature,
Some(&wrapper.wrapper_name),
);
ExternFunction {
arguments: vec![wrapper.clone()],
return_type: Some(WrapperType {
original_type_name: parse_quote! { *mut String },
wrapper_name: wrapper.wrapper_name,
rust_type: RustWrapperType::String,
reference_parameters: None,
}),
name: extern_function_name,
tokens,
}
}
pub fn create_drop_extern_function(wrapper: WrapperType) -> ExternFunction {
let block = parse_quote!({
unsafe {
#[allow(unused_must_use)]
{
Box::from_raw(_self);
}
}
});
let original_type_name = &wrapper.original_type_name;
let signature: Punctuated<FnArg, Token![,]> = parse_quote! { _self: *mut #original_type_name };
let (extern_function_name, tokens) =
prepare_extern_function_tokens(block, None, "drop", signature, Some(&wrapper.wrapper_name));
ExternFunction {
arguments: vec![wrapper.clone()],
return_type: None,
name: extern_function_name,
tokens,
}
}
pub fn create_get_from_vec_extern_function(
wrapper: WrapperType,
inner_type: WrapperType,
) -> ExternFunction {
let inner_type_original = &inner_type.original_type_name;
let block = parse_quote!({
unsafe { Box::into_raw(Box::new(unsafe { (&*_self).get(index).cloned() })) }
});
let original_type_name = &wrapper.original_type_name;
let signature: Punctuated<FnArg, Token![,]> =
parse_quote! { _self: *mut #original_type_name, index: usize };
let (extern_function_name, tokens) = prepare_extern_function_tokens(
block,
Some(parse_quote! { *mut Option<#inner_type_original> }),
"get",
signature,
Some(&wrapper.wrapper_name),
);
ExternFunction {
arguments: vec![
wrapper.clone(),
WrapperType {
original_type_name: parse_quote! { usize },
wrapper_name: "usize".to_owned(),
rust_type: RustWrapperType::Primitive,
reference_parameters: None,
},
],
return_type: Some(WrapperType {
original_type_name: parse_quote! { *mut Option<#inner_type_original> },
wrapper_name: format!("Optional{}", inner_type.wrapper_name),
rust_type: RustWrapperType::Option(inner_type.boxed()),
reference_parameters: None,
}),
name: extern_function_name,
tokens,
}
}
pub fn create_extern_global_function(function: &Function) -> ExternFunction {
let block = prepare_extern_function_body(function, None);
let return_type = extern_function_return_type(function);
let function_name = &function.name;
let signature = prepare_extern_function_signature(function);
let (extern_function_name, tokens) =
prepare_extern_function_tokens(block, return_type, function_name, signature, None);
ExternFunction {
arguments: function
.arguments
.iter()
.map(|arg| arg.typ.clone())
.collect(),
return_type: function.return_type.clone(),
name: extern_function_name,
tokens,
}
}
pub fn from_ok_extern_function(
wrapper: &WrapperType,
ok_wrapper_type: &WrapperType,
) -> ExternFunction {
let result_wrapper_ident = &wrapper.wrapper_name;
let ok_type = &ok_wrapper_type.original_type_name;
let original_type_name = &wrapper.original_type_name;
let from_ok_extern_function_name =
format!("{EXPORTED_SYMBOLS_PREFIX}${result_wrapper_ident}$from_ok");
ExternFunction {
arguments: vec![ok_wrapper_type.clone()],
return_type: Some(wrapper.clone()),
name: from_ok_extern_function_name.clone(),
tokens: if ok_wrapper_type.rust_type != RustWrapperType::Primitive
&& ok_wrapper_type.rust_type != RustWrapperType::FieldlessEnum
{
quote! (
const _: () = {
#[doc(hidden)]
#[export_name = #from_ok_extern_function_name]
pub extern "C" fn from_ok(val: *mut #ok_type) -> *mut #original_type_name {
Box::into_raw(unsafe { Box::new(Ok( *Box::from_raw(val))) } )
}
};
)
} else {
quote! (
const _: () = {
#[doc(hidden)]
#[export_name = #from_ok_extern_function_name]
pub extern "C" fn from_ok(val: #ok_type) -> *mut #original_type_name {
Box::into_raw(Box::new(Ok(val)))
}
};
)
},
}
}
pub fn from_err_extern_function(
wrapper: &WrapperType,
exceptions_wrapper_type: &WrapperType,
) -> ExternFunction {
let result_wrapper_ident = &wrapper.wrapper_name;
let from_err_extern_function_name =
format!("{EXPORTED_SYMBOLS_PREFIX}${result_wrapper_ident}$from_err");
let exc_type = &exceptions_wrapper_type.original_type_name;
let original_type_name = &wrapper.original_type_name;
ExternFunction {
arguments: vec![exceptions_wrapper_type.clone()],
return_type: Some(wrapper.clone()),
name: from_err_extern_function_name.clone(),
tokens: if !matches!(
exceptions_wrapper_type.rust_type,
RustWrapperType::Exceptions(Exceptions::Primitive(_))
) {
quote! (
const _: () = {
#[doc(hidden)]
#[export_name = #from_err_extern_function_name]
pub extern "C" fn from_err(val: *mut #exc_type) -> *mut #original_type_name {
Box::into_raw(unsafe { Box::new(Err(*Box::from_raw(val))) } )
}
};
)
} else {
quote! (
const _: () = {
#[doc(hidden)]
#[export_name = #from_err_extern_function_name]
pub extern "C" fn from_err(val: #exc_type) -> *mut #original_type_name {
Box::into_raw(Box::new(Err(val)))
}
};
)
},
}
}
fn extern_function_return_type(function: &Function) -> Option<Type> {
function.return_type.as_ref().map(|wrapper_type| {
if wrapper_type.reference_parameters.is_some()
|| (wrapper_type.rust_type != RustWrapperType::Primitive
&& wrapper_type.rust_type != RustWrapperType::FieldlessEnum)
{
let original_type_name = &wrapper_type.original_type_name;
parse_quote! { *mut #original_type_name }
} else {
wrapper_type.original_type_name.clone()
}
})
}