commonlibsse_ng_proc_macro_common\relocate/
mod.rs1mod attr_args;
2
3use crate::fn_args_parser::{FnArgs, create_fn_args};
4use core::str::FromStr;
5use proc_macro2::TokenStream;
6use quote::quote;
7use syn::Type;
8
9pub fn gen_relocate(
10 attrs: TokenStream,
11 item_fn: syn::ItemFn,
12 crate_root_name: TokenStream,
13) -> TokenStream {
14 let args = {
15 let attr_args = match darling::ast::NestedMeta::parse_meta_list(attrs) {
16 Ok(v) => v,
17 Err(e) => {
18 return darling::Error::from(e).write_errors();
19 }
20 };
21
22 match <attr_args::MacroArgs as darling::FromMeta>::from_list(&attr_args) {
23 Ok(v) => v,
24 Err(e) => {
25 return e.write_errors();
26 }
27 }
28 };
29 generate_code(args, item_fn, crate_root_name).unwrap_or_else(syn::Error::into_compile_error)
30}
31
32fn generate_code(
33 args: attr_args::MacroArgs,
34 item_fn: syn::ItemFn,
35 crate_root_name: TokenStream,
36) -> syn::Result<TokenStream> {
37 let attr_args::MacroArgs { cast_as, default, deref_once, id } = args;
38 let attr_args::RelocationId { se: se_id, ae: ae_id, vr: vr_id } = id;
39 let vr_id = vr_id.unwrap_or(se_id);
40
41 let syn::ItemFn { attrs, vis, sig, block } = &item_fn;
42 let syn::Signature {
43 constness,
44 asyncness,
45 unsafety,
46 abi,
47 ident,
48 generics,
49 inputs: fn_inputs,
50 variadic,
51 output: fn_output,
52 ..
53 } = &sig;
54
55 let FnArgs { type_args, self_type, .. } = create_fn_args(fn_inputs, variadic);
56
57 let fn_type = quote! { #constness #asyncness #unsafety #abi fn #generics (#self_type #type_args) #fn_output };
58
59 let fn_sig = quote! { #vis #constness #asyncness #unsafety #abi fn #ident #generics (#fn_inputs) #fn_output };
60 let closure = extract_closure_expr(block)?;
61 let closure_arg = &closure.inputs;
62 let body = &closure.body;
63
64 #[cfg(feature = "tracing")]
65 let database_err_log = quote! { #crate_root_name::__private::tracing::error!("[Critical Error] Failed to resolve address: {err}") };
66 #[cfg(not(feature = "tracing"))]
67 let database_err_log = quote! {};
68
69 let cast_type: Type = syn::parse2(TokenStream::from_str(&cast_as)?)?; let (deref_type_alias, deref_code) = if deref_once.is_some_and(|b| b) {
71 let deref_type = peel_pointer(&cast_type); let deref_type = deref_type.as_ref().map(|inner| {
73 quote! {
74 type DerefType = #inner
76 }
77 });
78
79 (
80 deref_type,
81 quote! {
82 let ptr: AsType = core::mem::transmute(v.as_ptr());
83 ptr.read_unaligned()
84 },
85 )
86 } else {
87 (
88 None,
89 quote! {
90 let ptr: AsType = core::mem::transmute(v.as_ptr());
91 ptr
92 },
93 )
94 };
95
96 let default = TokenStream::from_str(&default)?;
97
98 Ok(quote! {
99 #(#attrs)*
100 #[allow(clippy::unnecessary_map_or)]
101 #[allow(clippy::use_self)]
102 #fn_sig {
103 type SelfSignature = #fn_type;
108 type AsType = #cast_type;
110 #deref_type_alias;
111
112 {
113 static ADDRESS: #crate_root_name::__private::OnceCell<#crate_root_name::__private::Unique<::core::ffi::c_void>> =
114 #crate_root_name::__private::OnceCell::new();
115 ADDRESS
116 .get_or_try_init(|| {
117 use #crate_root_name::__private::Unique;
118 use #crate_root_name::rel::id::RelocationID;
119 use #crate_root_name::rel::ResolvableAddress as _;
120
121 let address = match RelocationID::new(#se_id, #ae_id, #vr_id).address() {
122 Ok(addr) => addr,
123 Err(err) => {
124 #database_err_log;
125 return Err(err);
126 }
127 };
128
129 unsafe { Ok(Unique::new_unchecked(address.as_ptr())) }
130 })
131 .ok()
132 .map(|v| unsafe { #deref_code })
133 .map_or(#default, |#closure_arg| { #body }) }
135 }
136 })
137}
138
139fn extract_closure_expr(block: &syn::Block) -> syn::Result<&syn::ExprClosure> {
140 use syn::{Expr, Stmt};
141
142 if block.stmts.len() != 1 {
143 return Err(syn::Error::new_spanned(
144 block,
145 "expected a single closure expression inside the function body",
146 ));
147 }
148
149 match &block.stmts[0] {
150 Stmt::Expr(Expr::Closure(closure), _) => Ok(closure),
151 Stmt::Expr(expr, _) => {
152 Err(syn::Error::new_spanned(expr, "expected a closure expression like `|x| x`"))
153 }
154 stmt => Err(syn::Error::new_spanned(stmt, "expected an expression statement (closure)")),
155 }
156}
157
158fn peel_pointer(ty: &Type) -> Option<Type> {
159 if let Type::Ptr(syn::TypePtr { elem, .. }) = ty { Some(*elem.clone()) } else { None }
160}