1use 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
223pub 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}