xref: /DragonOS/kernel/crates/intertrait/macros/src/gen_caster.rs (revision fae6e9ade46a52976ad5d099643d51cc20876448)
1 use core::str::from_utf8_unchecked;
2 
3 use proc_macro2::TokenStream;
4 use uuid::adapter::Simple;
5 use uuid::Uuid;
6 
7 use quote::format_ident;
8 use quote::quote;
9 use quote::ToTokens;
10 
11 pub fn generate_caster(ty: &impl ToTokens, trait_: &impl ToTokens, sync: bool) -> TokenStream {
12     let mut fn_buf = [0u8; FN_BUF_LEN];
13     let fn_ident = format_ident!("{}", new_fn_name(&mut fn_buf));
14     // 生成从dyn trait转换为具体类型结构体ty的caster
15     let new_caster = if sync {
16         quote! {
17             ::intertrait::Caster::<dyn #trait_>::new_sync(
18                 |from| from.downcast_ref::<#ty>().unwrap(),
19                 |from| from.downcast_mut::<#ty>().unwrap(),
20                 |from| from.downcast::<#ty>().unwrap(),
21                 |from| from.downcast::<#ty>().unwrap(),
22                 |from| from.downcast::<#ty>().unwrap()
23             )
24         }
25     } else {
26         quote! {
27             ::intertrait::Caster::<dyn #trait_>::new(
28                 |from| from.downcast_ref::<#ty>().unwrap(),
29                 |from| from.downcast_mut::<#ty>().unwrap(),
30                 |from| from.downcast::<#ty>().unwrap(),
31                 |from| from.downcast::<#ty>().unwrap(),
32             )
33         }
34     };
35 
36     // 由于过程宏是在预编译期执行的,这里的target_os是linux。
37     // 编译完成的proc macro会交给下一阶段进行编译,因此,#[cfg(target_os)]会在下一阶段生效。
38     // 我们必须在预处理阶段把两种代码的token stream都生成出来,然后在下一阶段选择性地使用其中一种。
39     quote! {
40 
41         #[cfg(not(target_os = "none"))]
42         #[::linkme::distributed_slice(::intertrait::CASTERS)]
43         fn #fn_ident() -> (::std::any::TypeId, ::intertrait::BoxedCaster) {
44             (::std::any::TypeId::of::<#ty>(), Box::new(#new_caster))
45         }
46 
47         #[cfg(target_os = "none")]
48         #[::linkme::distributed_slice(::intertrait::CASTERS)]
49         fn #fn_ident() -> (::core::any::TypeId, ::intertrait::BoxedCaster) {
50             (::core::any::TypeId::of::<#ty>(), alloc::boxed::Box::new(#new_caster))
51         }
52     }
53 }
54 
55 const FN_PREFIX: &[u8] = b"__";
56 const FN_BUF_LEN: usize = FN_PREFIX.len() + Simple::LENGTH;
57 
58 fn new_fn_name(buf: &mut [u8]) -> &str {
59     buf[..FN_PREFIX.len()].copy_from_slice(FN_PREFIX);
60     Uuid::new_v4()
61         .to_simple()
62         .encode_lower(&mut buf[FN_PREFIX.len()..]);
63     unsafe { from_utf8_unchecked(&buf[..FN_BUF_LEN]) }
64 }
65