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 let len_meta_args = meta_args.len();
128
129 let rng_start = opts
142 .use_rng
143 .then(|| {
144 quote!(single_threaded(|| unsafe {
145 extendr_api::GetRNGstate();
146 });)
147 })
148 .unwrap_or_default();
149 let rng_end = opts
150 .use_rng
151 .then(|| {
152 quote!(single_threaded(|| unsafe {
153 extendr_api::PutRNGstate();
154 });)
155 })
156 .unwrap_or_default();
157
158 let return_is_ref_self = {
164 match sig.output {
165 syn::ReturnType::Default => false,
167 syn::ReturnType::Type(_, ref return_type) => match return_type.as_ref() {
170 Type::Reference(ref reference_type) => {
171 if let Type::Path(path) = reference_type.elem.as_ref() {
173 let is_typename_impl_type = self_ty
174 .map(|x| x == reference_type.elem.as_ref())
175 .unwrap_or(false);
176 path.path.is_ident("Self") || is_typename_impl_type
177 } else {
178 false
179 }
180 }
181 _ => false,
182 },
183 }
184 };
185
186 let return_type_conversion = if return_is_ref_self {
187 quote!(
190 let return_ref_to_self = #call_name(#actual_args);
191
192 #(
193 let arg_ref = extendr_api::R_ExternalPtrAddr(#sexp_args)
194 .cast::<Box<dyn std::any::Any>>()
195 .as_ref()
196 .unwrap()
197 .downcast_ref::<#self_ty>()
198 .unwrap();
199 if std::ptr::addr_eq(
200 arg_ref,
201 std::ptr::from_ref(return_ref_to_self)) {
202 return Ok(extendr_api::Robj::from_sexp(#sexp_args))
203 }
204 )*
205 Err(Error::ExpectedExternalPtrReference)
206 )
207 } else {
208 quote!(Ok(extendr_api::Robj::from(#call_name(#actual_args))))
209 };
210
211 wrappers.push(parse_quote!(
213 #[no_mangle]
214 #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)]
215 pub extern "C" fn #wrap_name(#formal_args) -> extendr_api::SEXP {
216 use extendr_api::robj::*;
217
218 #rng_start
220
221 let wrap_result_state: std::result::Result<
222 std::result::Result<extendr_api::Robj, extendr_api::Error>,
223 Box<dyn std::any::Any + Send>
224 > = unsafe {
225 std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || -> std::result::Result<extendr_api::Robj, extendr_api::Error> {
226 #(#convert_args)*
227 #return_type_conversion
228 }))
229 };
230
231 #rng_end
233
234 match wrap_result_state {
236 Ok(Ok(zz)) => {
237 return unsafe { zz.get() };
238 }
239 Ok(Err(conversion_err)) => {
241 let err_string = conversion_err.to_string();
242 drop(conversion_err); extendr_api::throw_r_error(&err_string);
244 }
245 Err(unwind_err) => {
247 drop(unwind_err); let err_string = format!("User function panicked: {}", #r_name_str);
253 extendr_api::handle_panic(err_string.as_str(), || panic!());
256 }
257 }
258 unreachable!("internal extendr error, this should never happen.")
259 }
260 ));
261
262 wrappers.push(parse_quote!(
264 #[allow(non_snake_case)]
265 fn #meta_name(metadata: &mut Vec<extendr_api::metadata::Func>) {
266 let mut args = Vec::with_capacity(#len_meta_args);
267 #(
268 args.push(#meta_args);
269 )*
270 let args = args;
271
272 metadata.push(extendr_api::metadata::Func {
273 doc: #doc_string,
274 rust_name: #rust_name_str,
275 r_name: #r_name_str,
276 mod_name: #c_name_str,
277 args: args,
278 return_type: #return_type_string,
279 func_ptr: #wrap_name as * const u8,
280 hidden: false,
281 })
282 }
283 ));
284
285 Ok(())
286}
287
288pub fn get_doc_string(attrs: &[syn::Attribute]) -> String {
290 let mut res = String::new();
291 for attr in attrs {
292 if !attr.path().is_ident("doc") {
293 continue;
294 }
295
296 if let syn::Meta::NameValue(ref nv) = attr.meta {
297 if let Expr::Lit(ExprLit {
298 lit: syn::Lit::Str(ref litstr),
299 ..
300 }) = nv.value
301 {
302 if !res.is_empty() {
303 res.push('\n');
304 }
305 res.push_str(&litstr.value());
306 }
307 }
308 }
309 res
310}
311
312pub fn get_return_type(sig: &syn::Signature) -> String {
313 match &sig.output {
314 syn::ReturnType::Default => "()".into(),
315 syn::ReturnType::Type(_, ref rettype) => type_name(rettype),
316 }
317}
318
319pub fn mangled_type_name(type_: &Type) -> String {
320 let src = quote!( #type_ ).to_string();
321 let mut res = String::new();
322 for c in src.chars() {
323 if c != ' ' {
324 if c.is_alphanumeric() {
325 res.push(c)
326 } else {
327 let f = format!("_{:02x}", c as u32);
328 res.push_str(&f);
329 }
330 }
331 }
332 res
333}
334
335pub fn type_name(type_: &Type) -> String {
344 match type_ {
345 Type::Path(syn::TypePath { path, .. }) => {
346 if let Some(ident) = path.get_ident() {
347 ident.to_string()
348 } else if path.segments.len() == 1 {
349 let seg = path.segments.clone().into_iter().next().unwrap();
350 seg.ident.to_string()
351 } else {
352 mangled_type_name(type_)
353 }
354 }
355 Type::Group(syn::TypeGroup { elem, .. }) => type_name(elem),
356 Type::Reference(syn::TypeReference { elem, .. }) => type_name(elem),
357 Type::Paren(syn::TypeParen { elem, .. }) => type_name(elem),
358 Type::Ptr(syn::TypePtr { elem, .. }) => type_name(elem),
359 _ => mangled_type_name(type_),
360 }
361}
362
363pub fn translate_formal(input: &FnArg, self_ty: Option<&syn::Type>) -> syn::Result<FnArg> {
365 match input {
366 FnArg::Typed(ref pattype) => {
368 let pat = pattype.pat.as_ref();
369 let pat_ident = translate_only_alias(pat)?;
371 Ok(parse_quote! { #pat_ident: extendr_api::SEXP })
372 }
373 FnArg::Receiver(ref receiver) => {
375 if !receiver.attrs.is_empty() || receiver.reference.is_none() {
376 return Err(syn::Error::new_spanned(
377 input,
378 "expected &self or &mut self",
379 ));
380 }
381 if self_ty.is_none() {
382 return Err(syn::Error::new_spanned(
383 input,"found &self in non-impl function - have you missed the #[extendr] before the impl?"
384 ));
385 }
386 Ok(parse_quote! { _self : extendr_api::SEXP })
387 }
388 }
389}
390
391fn translate_only_alias(pat: &syn::Pat) -> Result<&Ident, syn::Error> {
396 Ok(match pat {
397 syn::Pat::Ident(ref pat_ident) => &pat_ident.ident,
398 _ => {
399 return Err(syn::Error::new_spanned(
400 pat,
401 "failed to translate name of argument",
402 ));
403 }
404 })
405}
406
407fn translate_meta_arg(input: &mut FnArg, self_ty: Option<&syn::Type>) -> syn::Result<Expr> {
409 match input {
410 FnArg::Typed(ref mut pattype) => {
412 let pat = pattype.pat.as_ref();
413 let ty = pattype.ty.as_ref();
414 let pat_ident = translate_only_alias(pat)?;
417 let name_string = quote! { #pat_ident }.to_string();
418 let type_string = type_name(ty);
419 let default = if let Some(default) = get_named_lit(&mut pattype.attrs, "default") {
420 quote!(Some(#default))
421 } else {
422 quote!(None)
423 };
424 Ok(parse_quote! {
425 extendr_api::metadata::Arg {
426 name: #name_string,
427 arg_type: #type_string,
428 default: #default
429 }
430 })
431 }
432 FnArg::Receiver(ref receiver) => {
434 if !receiver.attrs.is_empty() || receiver.reference.is_none() {
435 return Err(syn::Error::new_spanned(
436 input,
437 "expected &self or &mut self",
438 ));
439 }
440 if self_ty.is_none() {
441 return Err(syn::Error::new_spanned(
442 input,
443 "found &self in non-impl function - have you missed the #[extendr] before the impl?"
444 )
445 );
446 }
447 let type_string = type_name(self_ty.unwrap());
448 Ok(parse_quote! {
449 extendr_api::metadata::Arg {
450 name: "self",
451 arg_type: #type_string,
452 default: None
453 }
454 })
455 }
456 }
457}
458
459fn translate_to_robj(input: &FnArg) -> syn::Result<syn::Stmt> {
464 match input {
465 FnArg::Typed(ref pattype) => {
466 let pat = &pattype.pat.as_ref();
467 if let syn::Pat::Ident(ref ident) = pat {
468 let varname = format_ident!("_{}_robj", ident.ident);
469 let ident = &ident.ident;
470 Ok(parse_quote! { let #varname = extendr_api::robj::Robj::from_sexp(#ident); })
472 } else {
473 Err(syn::Error::new_spanned(
474 input,
475 "expect identifier as arg name",
476 ))
477 }
478 }
479 FnArg::Receiver(_) => {
480 Ok(parse_quote! { let mut _self_robj = extendr_api::robj::Robj::from_sexp(_self); })
482 }
483 }
484}
485
486fn translate_actual(input: &FnArg) -> Option<Expr> {
488 match input {
489 FnArg::Typed(ref pattype) => {
490 let pat = &pattype.pat.as_ref();
491 if let syn::Pat::Ident(ref ident) = pat {
492 let varname = format_ident!("_{}_robj", ident.ident);
493 Some(parse_quote! {
494 #varname.try_into()?
495 })
496 } else {
497 None
498 }
499 }
500 FnArg::Receiver(_) => {
501 None
503 }
504 }
505}
506
507fn get_named_lit(attrs: &mut Vec<syn::Attribute>, name: &str) -> Option<String> {
511 let mut new_attrs = Vec::new();
512 let mut res = None;
513 for a in attrs.drain(0..) {
514 if let syn::Meta::NameValue(ref nv) = a.meta {
515 if nv.path.is_ident(name) {
516 if let Expr::Lit(ExprLit {
517 lit: syn::Lit::Str(ref litstr),
518 ..
519 }) = nv.value
520 {
521 res = Some(litstr.value());
522 continue;
523 }
524 }
525 }
526
527 new_attrs.push(a);
528 }
529 *attrs = new_attrs;
530 res
531}
532
533fn sanitize_identifier(ident: Ident) -> Ident {
536 static PREFIX: &str = "r#";
537 let (ident, span) = (ident.to_string(), ident.span());
538 let ident = match ident.strip_prefix(PREFIX) {
539 Some(ident) => ident.into(),
540 None => ident,
541 };
542
543 Ident::new(&ident, span)
544}