commonlibsse_ng_proc_macro_common\relocate/
mod.rs

1mod 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)?)?; // from cast_as string
70    let (deref_type_alias, deref_code) = if deref_once.is_some_and(|b| b) {
71        let deref_type = peel_pointer(&cast_type); // returns Option<Type>
72        let deref_type = deref_type.as_ref().map(|inner| {
73            quote! {
74                /// Type dereferenced once(by `read_unaligned`) from `cast_as`.
75                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            /// Function signature for self.
104            /// `self` is automatically `this: *const ()`, `this: *mut ()`, etc..
105            ///
106            /// This is created because Rust does not have a function equivalent to `decltype(T)` in C++.
107            type SelfSignature = #fn_type;
108            /// Casted type.
109            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 }) // intended stmts: `|ptr| unsafe { ptr.read_unaligned() }`
134            }
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}