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
67 let inputs = &mut sig.inputs;
68 let has_self = matches!(inputs.iter().next(), Some(FnArg::Receiver(_)));
69
70 let call_name = if has_self {
71 let is_mut = match inputs.iter().next() {
72 Some(FnArg::Receiver(ref receiver)) => receiver.mutability.is_some(),
73 _ => false,
74 };
75 if is_mut {
76 quote! { extendr_api::unwrap_or_throw_error(
78 <&mut #self_ty>::try_from(&mut _self_robj)
79 ).#rust_name }
80 } else {
81 quote! { extendr_api::unwrap_or_throw_error(
83 <&#self_ty>::try_from(&_self_robj)
84 ).#rust_name }
85 }
86 } else if let Some(ref self_ty) = &self_ty {
87 quote! { <#self_ty>::#rust_name }
89 } else {
90 quote! { #rust_name }
92 };
93
94 let formal_args = inputs
96 .iter()
97 .map(|input| translate_formal(input, self_ty))
98 .collect::<syn::Result<Punctuated<FnArg, Token![,]>>>()?;
99
100 let sexp_args = formal_args
102 .clone()
103 .into_iter()
104 .map(|x| match x {
105 FnArg::Receiver(_) => unreachable!(),
107 FnArg::Typed(ref typed) => match typed.pat.as_ref() {
108 syn::Pat::Ident(ref pat_ident) => pat_ident.ident.clone(),
109 _ => unreachable!(),
110 },
111 })
112 .collect::<Vec<Ident>>();
113
114 let convert_args: Vec<syn::Stmt> = inputs
116 .iter()
117 .map(translate_to_robj)
118 .collect::<syn::Result<Vec<syn::Stmt>>>()?;
119
120 let actual_args: Punctuated<Expr, Token![,]> =
121 inputs.iter().filter_map(translate_actual).collect();
122
123 let meta_args: Vec<Expr> = inputs
124 .iter_mut()
125 .map(|input| translate_meta_arg(input, self_ty))
126 .collect::<syn::Result<Vec<Expr>>>()?;
127
128 let rng_start = opts
141 .use_rng
142 .then(|| {
143 quote!(single_threaded(|| unsafe {
144 extendr_api::GetRNGstate();
145 });)
146 })
147 .unwrap_or_default();
148 let rng_end = opts
149 .use_rng
150 .then(|| {
151 quote!(single_threaded(|| unsafe {
152 extendr_api::PutRNGstate();
153 });)
154 })
155 .unwrap_or_default();
156
157 let return_is_ref_self = {
163 match sig.output {
164 syn::ReturnType::Default => false,
166 syn::ReturnType::Type(_, ref return_type) => match return_type.as_ref() {
169 Type::Reference(ref reference_type) => {
170 if let Type::Path(path) = reference_type.elem.as_ref() {
172 let is_typename_impl_type = self_ty
173 .map(|x| x == reference_type.elem.as_ref())
174 .unwrap_or(false);
175 path.path.is_ident("Self") || is_typename_impl_type
176 } else {
177 false
178 }
179 }
180 _ => false,
181 },
182 }
183 };
184
185 let return_type_conversion = if return_is_ref_self {
186 quote!(
189 let return_ref_to_self = #call_name(#actual_args);
190
191 #(
192 let arg_ref = extendr_api::R_ExternalPtrAddr(#sexp_args)
193 .cast::<Box<dyn std::any::Any>>()
194 .as_ref()
195 .unwrap()
196 .downcast_ref::<#self_ty>()
197 .unwrap();
198 if std::ptr::addr_eq(
199 arg_ref,
200 std::ptr::from_ref(return_ref_to_self)) {
201 return Ok(extendr_api::Robj::from_sexp(#sexp_args))
202 }
203 )*
204 Err(Error::ExpectedExternalPtrReference)
205 )
206 } else {
207 quote!(Ok(extendr_api::Robj::from(#call_name(#actual_args))))
208 };
209
210 wrappers.push(parse_quote!(
212 #[no_mangle]
213 #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
214 pub extern "C" fn #wrap_name(#formal_args) -> extendr_api::SEXP {
215 use extendr_api::robj::*;
216
217 #rng_start
219
220 let wrap_result_state: std::result::Result<
221 std::result::Result<extendr_api::Robj, extendr_api::Error>,
222 Box<dyn std::any::Any + Send>
223 > = unsafe {
224 std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || -> std::result::Result<extendr_api::Robj, extendr_api::Error> {
225 #(#convert_args)*
226 #return_type_conversion
227 }))
228 };
229
230 #rng_end
232
233 match wrap_result_state {
235 Ok(Ok(zz)) => {
236 return unsafe { zz.get() };
237 }
238 Ok(Err(conversion_err)) => {
240 let err_string = conversion_err.to_string();
241 drop(conversion_err); extendr_api::throw_r_error(&err_string);
243 }
244 Err(unwind_err) => {
246 drop(unwind_err); let err_string = format!("User function panicked: {}", #r_name_str);
252 extendr_api::handle_panic(err_string.as_str(), || panic!());
255 }
256 }
257 unreachable!("internal extendr error, this should never happen.")
258 }
259 ));
260
261 wrappers.push(parse_quote!(
263 #[allow(non_snake_case)]
264 fn #meta_name(metadata: &mut Vec<extendr_api::metadata::Func>) {
265 let args = vec![
266 #( #meta_args, )*
267 ];
268
269 metadata.push(extendr_api::metadata::Func {
270 doc: #doc_string,
271 rust_name: #rust_name_str,
272 r_name: #r_name_str,
273 mod_name: #c_name_str,
274 args: args,
275 return_type: #return_type_string,
276 func_ptr: #wrap_name as * const u8,
277 hidden: false,
278 })
279 }
280 ));
281
282 Ok(())
283}
284
285pub fn get_doc_string(attrs: &[syn::Attribute]) -> String {
287 let mut res = String::new();
288 for attr in attrs {
289 if !attr.path().is_ident("doc") {
290 continue;
291 }
292
293 if let syn::Meta::NameValue(ref nv) = attr.meta {
294 if let Expr::Lit(ExprLit {
295 lit: syn::Lit::Str(ref litstr),
296 ..
297 }) = nv.value
298 {
299 if !res.is_empty() {
300 res.push('\n');
301 }
302 res.push_str(&litstr.value());
303 }
304 }
305 }
306 res
307}
308
309pub fn get_return_type(sig: &syn::Signature) -> String {
310 match &sig.output {
311 syn::ReturnType::Default => "()".into(),
312 syn::ReturnType::Type(_, ref rettype) => type_name(rettype),
313 }
314}
315
316pub fn mangled_type_name(type_: &Type) -> String {
317 let src = quote!( #type_ ).to_string();
318 let mut res = String::new();
319 for c in src.chars() {
320 if c != ' ' {
321 if c.is_alphanumeric() {
322 res.push(c)
323 } else {
324 let f = format!("_{:02x}", c as u32);
325 res.push_str(&f);
326 }
327 }
328 }
329 res
330}
331
332pub fn type_name(type_: &Type) -> String {
341 match type_ {
342 Type::Path(syn::TypePath { path, .. }) => {
343 if let Some(ident) = path.get_ident() {
344 ident.to_string()
345 } else if path.segments.len() == 1 {
346 let seg = path.segments.clone().into_iter().next().unwrap();
347 seg.ident.to_string()
348 } else {
349 mangled_type_name(type_)
350 }
351 }
352 Type::Group(syn::TypeGroup { elem, .. }) => type_name(elem),
353 Type::Reference(syn::TypeReference { elem, .. }) => type_name(elem),
354 Type::Paren(syn::TypeParen { elem, .. }) => type_name(elem),
355 Type::Ptr(syn::TypePtr { elem, .. }) => type_name(elem),
356 _ => mangled_type_name(type_),
357 }
358}
359
360pub fn translate_formal(input: &FnArg, self_ty: Option<&syn::Type>) -> syn::Result<FnArg> {
362 match input {
363 FnArg::Typed(ref pattype) => {
365 let pat = pattype.pat.as_ref();
366 let pat_ident = translate_only_alias(pat)?;
368 Ok(parse_quote! { #pat_ident: extendr_api::SEXP })
369 }
370 FnArg::Receiver(ref receiver) => {
372 if !receiver.attrs.is_empty() || receiver.reference.is_none() {
373 return Err(syn::Error::new_spanned(
374 input,
375 "expected &self or &mut self",
376 ));
377 }
378 if self_ty.is_none() {
379 return Err(syn::Error::new_spanned(
380 input,"found &self in non-impl function - have you missed the #[extendr] before the impl?"
381 ));
382 }
383 Ok(parse_quote! { _self : extendr_api::SEXP })
384 }
385 }
386}
387
388fn translate_only_alias(pat: &syn::Pat) -> Result<&Ident, syn::Error> {
393 Ok(match pat {
394 syn::Pat::Ident(ref pat_ident) => &pat_ident.ident,
395 _ => {
396 return Err(syn::Error::new_spanned(
397 pat,
398 "failed to translate name of argument",
399 ));
400 }
401 })
402}
403
404fn translate_meta_arg(input: &mut FnArg, self_ty: Option<&syn::Type>) -> syn::Result<Expr> {
406 match input {
407 FnArg::Typed(ref mut pattype) => {
409 let pat = pattype.pat.as_ref();
410 let ty = pattype.ty.as_ref();
411 let pat_ident = translate_only_alias(pat)?;
414 let name_string = quote! { #pat_ident }.to_string();
415 let type_string = type_name(ty);
416 let default = if let Some(default) = get_named_lit(&mut pattype.attrs, "default") {
417 quote!(Some(#default))
418 } else {
419 quote!(None)
420 };
421 Ok(parse_quote! {
422 extendr_api::metadata::Arg {
423 name: #name_string,
424 arg_type: #type_string,
425 default: #default
426 }
427 })
428 }
429 FnArg::Receiver(ref receiver) => {
431 if !receiver.attrs.is_empty() || receiver.reference.is_none() {
432 return Err(syn::Error::new_spanned(
433 input,
434 "expected &self or &mut self",
435 ));
436 }
437 if self_ty.is_none() {
438 return Err(syn::Error::new_spanned(
439 input,
440 "found &self in non-impl function - have you missed the #[extendr] before the impl?"
441 )
442 );
443 }
444 let type_string = type_name(self_ty.unwrap());
445 Ok(parse_quote! {
446 extendr_api::metadata::Arg {
447 name: "self",
448 arg_type: #type_string,
449 default: None
450 }
451 })
452 }
453 }
454}
455
456fn translate_to_robj(input: &FnArg) -> syn::Result<syn::Stmt> {
461 match input {
462 FnArg::Typed(ref pattype) => {
463 let pat = &pattype.pat.as_ref();
464 if let syn::Pat::Ident(ref ident) = pat {
465 let varname = format_ident!("_{}_robj", ident.ident);
466 let ident = &ident.ident;
467 Ok(parse_quote! { let #varname = extendr_api::robj::Robj::from_sexp(#ident); })
469 } else {
470 Err(syn::Error::new_spanned(
471 input,
472 "expect identifier as arg name",
473 ))
474 }
475 }
476 FnArg::Receiver(_) => {
477 Ok(parse_quote! { let mut _self_robj = extendr_api::robj::Robj::from_sexp(_self); })
479 }
480 }
481}
482
483fn translate_actual(input: &FnArg) -> Option<Expr> {
485 match input {
486 FnArg::Typed(ref pattype) => {
487 let pat = &pattype.pat.as_ref();
488 if let syn::Pat::Ident(ref ident) = pat {
489 let varname = format_ident!("_{}_robj", ident.ident);
490 Some(parse_quote! {
491 #varname.try_into()?
492 })
493 } else {
494 None
495 }
496 }
497 FnArg::Receiver(_) => {
498 None
500 }
501 }
502}
503
504fn get_named_lit(attrs: &mut Vec<syn::Attribute>, name: &str) -> Option<String> {
508 let mut new_attrs = Vec::new();
509 let mut res = None;
510 for a in attrs.drain(0..) {
511 if let syn::Meta::NameValue(ref nv) = a.meta {
512 if nv.path.is_ident(name) {
513 if let Expr::Lit(ExprLit {
514 lit: syn::Lit::Str(ref litstr),
515 ..
516 }) = nv.value
517 {
518 res = Some(litstr.value());
519 continue;
520 }
521 }
522 }
523
524 new_attrs.push(a);
525 }
526 *attrs = new_attrs;
527 res
528}
529
530fn sanitize_identifier(ident: Ident) -> Ident {
533 static PREFIX: &str = "r#";
534 let (ident, span) = (ident.to_string(), ident.span());
535 let ident = match ident.strip_prefix(PREFIX) {
536 Some(ident) => ident.into(),
537 None => ident,
538 };
539
540 Ident::new(&ident, span)
541}