1use proc_macro2::Ident;
27use quote::{format_ident, quote};
28use syn::{parse_quote, punctuated::Punctuated, Expr, ExprLit, FnArg, ItemFn, Token, Type};
29
30use crate::extendr_options::ExtendrOptions;
31
32pub const META_PREFIX: &str = "meta__";
33pub const WRAP_PREFIX: &str = "wrap__";
34
35pub(crate) fn make_function_wrappers(
37 opts: &ExtendrOptions,
38 wrappers: &mut Vec<ItemFn>,
39 prefix: &str,
40 attrs: &[syn::Attribute],
41 sig: &mut syn::Signature,
42 self_ty: Option<&syn::Type>,
43) -> syn::Result<()> {
44 let rust_name = sig.ident.clone();
45
46 let r_name_str = if let Some(r_name) = opts.r_name.as_ref() {
47 r_name.clone()
48 } else {
49 sig.ident.to_string()
50 };
51
52 let mod_name = if let Some(mod_name) = opts.mod_name.as_ref() {
53 format_ident!("{}", mod_name)
54 } else {
55 sig.ident.clone()
56 };
57
58 let mod_name = sanitize_identifier(mod_name);
59 let wrap_name = format_ident!("{}{}{}", WRAP_PREFIX, prefix, mod_name);
60 let meta_name = format_ident!("{}{}{}", META_PREFIX, prefix, mod_name);
61
62 let rust_name_str = format!("{}", rust_name);
63 let c_name_str = format!("{}", mod_name);
64 let doc_string = get_doc_string(attrs);
65 let return_type_string = get_return_type(sig);
66 let opts_invisible = match opts.invisible {
67 Some(true) => quote!(Some(true)),
68 Some(false) => quote!(Some(false)),
69 None => quote!(None),
70 };
71
72 let inputs = &mut sig.inputs;
73 let has_self = matches!(inputs.iter().next(), Some(FnArg::Receiver(_)));
74
75 let call_name = if has_self {
76 let is_mut = match inputs.iter().next() {
77 Some(FnArg::Receiver(ref receiver)) => receiver.mutability.is_some(),
78 _ => false,
79 };
80 if is_mut {
81 quote! { extendr_api::unwrap_or_throw_error(
83 <&mut #self_ty>::try_from(&mut _self_robj)
84 ).#rust_name }
85 } else {
86 quote! { extendr_api::unwrap_or_throw_error(
88 <&#self_ty>::try_from(&_self_robj)
89 ).#rust_name }
90 }
91 } else if let Some(ref self_ty) = &self_ty {
92 quote! { <#self_ty>::#rust_name }
94 } else {
95 quote! { #rust_name }
97 };
98
99 let formal_args = inputs
101 .iter()
102 .map(|input| translate_formal(input, self_ty))
103 .collect::<syn::Result<Punctuated<FnArg, Token![,]>>>()?;
104
105 let sexp_args = formal_args
107 .clone()
108 .into_iter()
109 .map(|x| match x {
110 FnArg::Receiver(_) => unreachable!(),
112 FnArg::Typed(ref typed) => match typed.pat.as_ref() {
113 syn::Pat::Ident(ref pat_ident) => pat_ident.ident.clone(),
114 _ => unreachable!(),
115 },
116 })
117 .collect::<Vec<Ident>>();
118
119 let convert_args: Vec<syn::Stmt> = inputs
121 .iter()
122 .map(translate_to_robj)
123 .collect::<syn::Result<Vec<syn::Stmt>>>()?;
124
125 let actual_args: Punctuated<Expr, Token![,]> =
126 inputs.iter().filter_map(translate_actual).collect();
127
128 let meta_args: Vec<Expr> = inputs
129 .iter_mut()
130 .map(|input| translate_meta_arg(input, self_ty))
131 .collect::<syn::Result<Vec<Expr>>>()?;
132 let len_meta_args = meta_args.len();
133
134 let rng_start = opts
147 .use_rng
148 .then(|| {
149 quote!(single_threaded(|| unsafe {
150 extendr_api::GetRNGstate();
151 });)
152 })
153 .unwrap_or_default();
154 let rng_end = opts
155 .use_rng
156 .then(|| {
157 quote!(single_threaded(|| unsafe {
158 extendr_api::PutRNGstate();
159 });)
160 })
161 .unwrap_or_default();
162
163 let return_is_ref_self = {
169 match sig.output {
170 syn::ReturnType::Default => false,
172 syn::ReturnType::Type(_, ref return_type) => match return_type.as_ref() {
175 Type::Reference(ref reference_type) => {
176 if let Type::Path(path) = reference_type.elem.as_ref() {
178 let is_typename_impl_type = self_ty
179 .map(|x| x == reference_type.elem.as_ref())
180 .unwrap_or(false);
181 path.path.is_ident("Self") || is_typename_impl_type
182 } else {
183 false
184 }
185 }
186 _ => false,
187 },
188 }
189 };
190
191 let return_type_conversion = if return_is_ref_self {
192 quote!(
195 let return_ref_to_self = #call_name(#actual_args);
196
197 #(
198 let arg_ref = extendr_api::R_ExternalPtrAddr(#sexp_args)
199 .cast::<Box<dyn std::any::Any>>()
200 .as_ref()
201 .unwrap()
202 .downcast_ref::<#self_ty>()
203 .unwrap();
204 if std::ptr::addr_eq(
205 arg_ref,
206 std::ptr::from_ref(return_ref_to_self)) {
207 return Ok(extendr_api::Robj::from_sexp(#sexp_args))
208 }
209 )*
210 Err(Error::ExpectedExternalPtrReference)
211 )
212 } else {
213 quote!(Ok(extendr_api::Robj::from(#call_name(#actual_args))))
214 };
215
216 wrappers.push(parse_quote!(
218 #[no_mangle]
219 #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
220 pub extern "C" fn #wrap_name(#formal_args) -> extendr_api::SEXP {
221 use extendr_api::robj::*;
222
223 #rng_start
225
226 let wrap_result_state: std::result::Result<
227 std::result::Result<extendr_api::Robj, extendr_api::Error>,
228 Box<dyn std::any::Any + Send>
229 > = unsafe {
230 std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || -> std::result::Result<extendr_api::Robj, extendr_api::Error> {
231 #(#convert_args)*
232 #return_type_conversion
233 }))
234 };
235
236 #rng_end
238
239 match wrap_result_state {
241 Ok(Ok(zz)) => {
242 return unsafe { zz.get() };
243 }
244 Ok(Err(conversion_err)) => {
246 let err_string = conversion_err.to_string();
247 drop(conversion_err); extendr_api::throw_r_error(&err_string);
249 }
250 Err(unwind_err) => {
252 drop(unwind_err); let err_string = format!("User function panicked: {}", #r_name_str);
258 extendr_api::handle_panic(err_string.as_str(), || panic!());
261 }
262 }
263 unreachable!("internal extendr error, this should never happen.")
264 }
265 ));
266
267 wrappers.push(parse_quote!(
269 #[allow(non_snake_case)]
270 fn #meta_name(metadata: &mut Vec<extendr_api::metadata::Func>) {
271 let mut args = Vec::with_capacity(#len_meta_args);
272 #(
273 args.push(#meta_args);
274 )*
275 let args = args;
276
277 metadata.push(extendr_api::metadata::Func {
278 doc: #doc_string,
279 rust_name: #rust_name_str,
280 r_name: #r_name_str,
281 mod_name: #c_name_str,
282 args: args,
283 return_type: #return_type_string,
284 func_ptr: #wrap_name as * const u8,
285 hidden: false,
286 invisible: #opts_invisible,
287 })
288 }
289 ));
290
291 Ok(())
292}
293
294pub fn get_doc_string(attrs: &[syn::Attribute]) -> String {
296 let mut res = String::new();
297 for attr in attrs {
298 if !attr.path().is_ident("doc") {
299 continue;
300 }
301
302 if let syn::Meta::NameValue(ref nv) = attr.meta {
303 if let Expr::Lit(ExprLit {
304 lit: syn::Lit::Str(ref litstr),
305 ..
306 }) = nv.value
307 {
308 if !res.is_empty() {
309 res.push('\n');
310 }
311 res.push_str(&litstr.value());
312 }
313 }
314 }
315 res
316}
317
318pub fn get_return_type(sig: &syn::Signature) -> String {
319 match &sig.output {
320 syn::ReturnType::Default => "()".into(),
321 syn::ReturnType::Type(_, ref rettype) => type_name(rettype),
322 }
323}
324
325pub fn mangled_type_name(type_: &Type) -> String {
326 let src = quote!( #type_ ).to_string();
327 let mut res = String::new();
328 for c in src.chars() {
329 if c != ' ' {
330 if c.is_alphanumeric() {
331 res.push(c)
332 } else {
333 let f = format!("_{:02x}", c as u32);
334 res.push_str(&f);
335 }
336 }
337 }
338 res
339}
340
341pub fn type_name(type_: &Type) -> String {
350 match type_ {
351 Type::Path(syn::TypePath { path, .. }) => {
352 if let Some(ident) = path.get_ident() {
353 ident.to_string()
354 } else if path.segments.len() == 1 {
355 let seg = path.segments.clone().into_iter().next().unwrap();
356 seg.ident.to_string()
357 } else {
358 mangled_type_name(type_)
359 }
360 }
361 Type::Group(syn::TypeGroup { elem, .. }) => type_name(elem),
362 Type::Reference(syn::TypeReference { elem, .. }) => type_name(elem),
363 Type::Paren(syn::TypeParen { elem, .. }) => type_name(elem),
364 Type::Ptr(syn::TypePtr { elem, .. }) => type_name(elem),
365 _ => mangled_type_name(type_),
366 }
367}
368
369pub fn translate_formal(input: &FnArg, self_ty: Option<&syn::Type>) -> syn::Result<FnArg> {
371 match input {
372 FnArg::Typed(ref pattype) => {
374 let pat = pattype.pat.as_ref();
375 let pat_ident = translate_only_alias(pat)?;
377 Ok(parse_quote! { #pat_ident: extendr_api::SEXP })
378 }
379 FnArg::Receiver(ref receiver) => {
381 if !receiver.attrs.is_empty() || receiver.reference.is_none() {
382 return Err(syn::Error::new_spanned(
383 input,
384 "expected &self or &mut self",
385 ));
386 }
387 if self_ty.is_none() {
388 return Err(syn::Error::new_spanned(
389 input,"found &self in non-impl function - have you missed the #[extendr] before the impl?"
390 ));
391 }
392 Ok(parse_quote! { _self : extendr_api::SEXP })
393 }
394 }
395}
396
397fn translate_only_alias(pat: &syn::Pat) -> Result<&Ident, syn::Error> {
402 Ok(match pat {
403 syn::Pat::Ident(ref pat_ident) => &pat_ident.ident,
404 _ => {
405 return Err(syn::Error::new_spanned(
406 pat,
407 "failed to translate name of argument",
408 ));
409 }
410 })
411}
412
413fn translate_meta_arg(input: &mut FnArg, self_ty: Option<&syn::Type>) -> syn::Result<Expr> {
415 match input {
416 FnArg::Typed(ref mut pattype) => {
418 let pat = pattype.pat.as_ref();
419 let ty = pattype.ty.as_ref();
420 let pat_ident = translate_only_alias(pat)?;
423 let name_string = quote! { #pat_ident }.to_string();
424 let type_string = type_name(ty);
425 let default = if let Some(default) = get_named_lit(&mut pattype.attrs, "default") {
426 quote!(Some(#default))
427 } else {
428 quote!(None)
429 };
430 Ok(parse_quote! {
431 extendr_api::metadata::Arg {
432 name: #name_string,
433 arg_type: #type_string,
434 default: #default
435 }
436 })
437 }
438 FnArg::Receiver(ref receiver) => {
440 if !receiver.attrs.is_empty() || receiver.reference.is_none() {
441 return Err(syn::Error::new_spanned(
442 input,
443 "expected &self or &mut self",
444 ));
445 }
446 if self_ty.is_none() {
447 return Err(syn::Error::new_spanned(
448 input,
449 "found &self in non-impl function - have you missed the #[extendr] before the impl?"
450 )
451 );
452 }
453 let type_string = type_name(self_ty.unwrap());
454 Ok(parse_quote! {
455 extendr_api::metadata::Arg {
456 name: "self",
457 arg_type: #type_string,
458 default: None
459 }
460 })
461 }
462 }
463}
464
465fn translate_to_robj(input: &FnArg) -> syn::Result<syn::Stmt> {
470 match input {
471 FnArg::Typed(ref pattype) => {
472 let pat = &pattype.pat.as_ref();
473 if let syn::Pat::Ident(ref ident) = pat {
474 let varname = format_ident!("_{}_robj", ident.ident);
475 let ident = &ident.ident;
476 Ok(parse_quote! { let #varname = extendr_api::robj::Robj::from_sexp(#ident); })
478 } else {
479 Err(syn::Error::new_spanned(
480 input,
481 "expect identifier as arg name",
482 ))
483 }
484 }
485 FnArg::Receiver(_) => {
486 Ok(parse_quote! { let mut _self_robj = extendr_api::robj::Robj::from_sexp(_self); })
488 }
489 }
490}
491
492fn translate_actual(input: &FnArg) -> Option<Expr> {
494 match input {
495 FnArg::Typed(ref pattype) => {
496 let pat = &pattype.pat.as_ref();
497 if let syn::Pat::Ident(ref ident) = pat {
498 let varname = format_ident!("_{}_robj", ident.ident);
499 Some(parse_quote! {
500 #varname.try_into()?
501 })
502 } else {
503 None
504 }
505 }
506 FnArg::Receiver(_) => {
507 None
509 }
510 }
511}
512
513fn get_named_lit(attrs: &mut Vec<syn::Attribute>, name: &str) -> Option<String> {
517 let mut new_attrs = Vec::new();
518 let mut res = None;
519 for a in attrs.drain(0..) {
520 if let syn::Meta::NameValue(ref nv) = a.meta {
521 if nv.path.is_ident(name) {
522 if let Expr::Lit(ExprLit {
523 lit: syn::Lit::Str(ref litstr),
524 ..
525 }) = nv.value
526 {
527 res = Some(litstr.value());
528 continue;
529 }
530 }
531 }
532
533 new_attrs.push(a);
534 }
535 *attrs = new_attrs;
536 res
537}
538
539fn sanitize_identifier(ident: Ident) -> Ident {
542 static PREFIX: &str = "r#";
543 let (ident, span) = (ident.to_string(), ident.span());
544 let ident = match ident.strip_prefix(PREFIX) {
545 Some(ident) => ident.into(),
546 None => ident,
547 };
548
549 Ident::new(&ident, span)
550}