static_reflect_derive_internals/
func.rs1use syn::{Error, FnArg, ReturnType, ForeignItem, Attribute, ItemFn, Meta, ItemForeignMod, Item, Lit, Type};
2use syn::parse::{self, Parse, ParseStream};
3use proc_macro2::{Ident, TokenStream, Span};
4use quote::{ToTokens, TokenStreamExt};
5use syn::Signature;
6use syn::spanned::Spanned;
7use itertools::Itertools;
8use quote::quote;
9
10
11const FUNC_ATTR_NAME: &str = "reflect_func";
12
13#[derive(Debug)]
14#[non_exhaustive]
15pub struct FuncArgs {
16 pub absolute: bool,
19}
20
21impl Parse for FuncArgs {
22 fn parse(input: ParseStream) -> parse::Result<Self> {
23 let mut args = FuncArgs {
24 absolute: false,
26 };
27 while !input.is_empty() {
28 if input.peek(syn::Ident) {
29 let ident = input.parse::<Ident>()?;
30 match &*ident.to_string() {
31 "absolute" => {
32 args.absolute = true;
33 }
34 _ => {
35 return Err(input.error(format_args!("Invalid flag: {}", ident)))
36 }
37 }
38 } else {
39 return Err(input.error("Unexpected token"))
40 }
41 }
42 Ok(args)
43 }
44}
45
46#[derive(Debug, Clone)]
47struct FunctionDefOpts {
48 assume_c_abi: bool,
52 location: FunctionLocation,
54 is_unsafe: bool,
56}
57
58fn determine_fn_link_name(item: &ItemFn) -> Result<Option<String>, syn::Error> {
61 for attr in &item.attrs {
62 match attr.parse_meta()? {
63 Meta::Path(ref p) if p.is_ident("no_mangle") => {
64 return Ok(None)
65 },
66 Meta::NameValue(ref item) if item.path.is_ident("export_name") => {
67 match item.lit {
68 Lit::Str(ref s) => {
69 return Ok(Some(s.value()))
70 },
71 _ => {
72 return Err(syn::Error::new(item.span(), "Expected a string for export_name"))
73 }
74 }
75 },
76 _ => {}
77 }
78 }
79 Err(syn::Error::new(
80 item.span(),
81 "Function must be #[no_mangle] to support dynamic linking"
82 ))
83}
84
85fn determine_foreign_link_name(attrs: &[Attribute]) -> Result<Option<String>, syn::Error> {
86 for attr in attrs {
87 match attr.parse_meta()? {
88 Meta::NameValue(ref l) if l.path.is_ident("link_name") => {
89 match l.lit {
90 Lit::Str(ref s) => {
91 return Ok(Some(s.value()))
92 },
93 _ => {
94 return Err(syn::Error::new(
95 l.span(),
96 "Expected a string for #[link_name]"
97 ))
98 }
99 }
100 },
101 _ => {}
102 }
103 }
104 Ok(None)
105}
106
107pub fn handle_item(item: &Item, args: FuncArgs) -> Result<TokenStream, syn::Error> {
108 match *item {
109 Item::Fn(ref func) => handle_fn_def(func, args),
110 Item::ForeignMod(ref foreign_mod) => handle_foreign_mod(foreign_mod, args),
111 _ => {
112 Err(syn::Error::new(
113 item.span(),
114 format!("Invalid target for #[{}]", FUNC_ATTR_NAME)
115 ))
116 }
117 }
118}
119
120fn handle_fn_def(item: &ItemFn, args: FuncArgs) -> Result<TokenStream, syn::Error> {
121 let location = if args.absolute {
122 let name = &item.sig.ident;
123 FunctionLocation::AbsoluteAddress(quote!({ #name as *const () }))
124 } else {
125 let name = determine_fn_link_name(&item)?;
126 FunctionLocation::DynamicallyLinked {
127 link_name: name.map(|s| quote!(#s))
128 }
129 };
130 let def = emit_def_from_signature(&item.sig, FunctionDefOpts {
131 assume_c_abi: false, location,
132 is_unsafe: item.sig.unsafety.is_some()
133 })?;
134 let verify_types = types_from_signature(&item.sig);
135 let def_const = def.make_constant(&verify_types);
136 Ok(quote! {
137 #def_const
138 #item
139 })
140}
141
142fn handle_foreign_mod(item: &ItemForeignMod, default_args: FuncArgs) -> Result<TokenStream, syn::Error> {
143 if default_args.absolute {
145 return Err(syn::Error::new(
146 item.span(),
147 "Absolute locations aren't supported in foreign functions"
148 ));
149 }
150 match item.abi.name.as_ref() {
151 Some(abi_name) if &*abi_name.value() == "C" => {},
152 None => {},
153 _ => {
154 return Err(Error::new(item.abi.span(), "Expected C ABI"))
155 }
156 }
157 let mut result_static_defs = Vec::new();
158 let mut result_items = Vec::new();
159 for item in &item.items {
160 match *item {
161 ForeignItem::Fn(ref item) => {
162 let mut result_item = (*item).clone();
163 result_item.attrs.clear();
164 for attr in &item.attrs {
165 if attr.path.is_ident(FUNC_ATTR_NAME) {
166 let override_args = syn::parse2::<FuncArgs>(attr.tokens.clone())?;
167 if override_args.absolute {
169 return Err(syn::Error::new(
170 item.span(),
171 "Absolute locations aren't supported in foreign functions"
172 ));
173 }
174 } else {
176 result_item.attrs.push(attr.clone());
177 }
178 }
179 let link_name = determine_foreign_link_name(&item.attrs)?
180 .map(|s| quote!(#s));
181 let args = FunctionDefOpts {
182 location: FunctionLocation::DynamicallyLinked { link_name },
183 assume_c_abi: true,
184 is_unsafe: true };
186 let verify_types = types_from_signature(&item.sig);
187 result_static_defs.push((
188 emit_def_from_signature(&item.sig, args)?,
189 verify_types
190 ));
191 result_items.push(ForeignItem::Fn(result_item));
192 },
193 _ => {
194 result_items.push((*item).clone());
196 }
197 }
198 }
199 let function_def_consts = result_static_defs.iter()
200 .map(|&(ref def, ref verify_types)| def.make_constant(&verify_types))
201 .collect_vec();
202 Ok(quote! {
203 #(#function_def_consts)*
204 extern "C" {
205 #(#result_items)*
206 }
207 })
208}
209
210fn emit_def_from_signature(
211 item: &Signature,
212 opts: FunctionDefOpts,
213) -> Result<StaticFunctionDef, syn::Error> {
214 match item.abi.as_ref().and_then(|abi| abi.name.as_ref()) {
215 Some(abi_name) if &*abi_name.value() == "C" => {},
216 None if opts.assume_c_abi => {},
217 _ => {
218 return Err(Error::new(item.span(), "Expected C ABI"))
219 }
220 }
221 let mut argument_types = Vec::new();
222 let mut static_arg_types = Vec::new();
223 for input in &item.inputs {
224 match input {
225 FnArg::Receiver(ref item) => {
226 return Err(Error::new(item.span(), "Invalid input"))
227 },
228 FnArg::Typed(ref item) => {
229 let ty = &item.ty;
230 static_arg_types.push(quote!(#ty));
231 argument_types.push(quote!(<#ty as static_reflect::StaticReflect>::TYPE_INFO))
232 },
233 }
234 }
235 let return_type = match item.output {
236 ReturnType::Default => quote!(&static_reflect::types::TypeInfo::Unit),
237 ReturnType::Type(_, ref ty) => {
238 quote!(&<#ty as static_reflect::StaticReflect>::TYPE_INFO)
239 },
240 };
241 let signature = StaticSignatureDef { argument_types, return_type };
242 Ok(StaticFunctionDef {
243 name: item.ident.to_string(),
244 location: opts.location,
245 signature, is_unsafe: opts.is_unsafe,
246 static_return_type: match item.output {
247 ReturnType::Default => quote!(()),
248 ReturnType::Type(_, ref ty) => quote!(#ty),
249 },
250 static_arg_types: quote!((#(#static_arg_types,)*))
251 })
252}
253
254pub fn types_from_signature(sig: &Signature) -> Vec<Type> {
256 sig.inputs.iter().map(|arg| match *arg {
257 FnArg::Receiver(_) => Type::Verbatim(quote!(Self)),
258 FnArg::Typed(ref t) => (*t.ty).clone()
259 }).chain(std::iter::once(match sig.output {
260 ReturnType::Default => Type::Tuple(syn::TypeTuple {
261 paren_token: Default::default(),
262 elems: Default::default()
263 }),
264 ReturnType::Type(_, ref ty) => (**ty).clone(),
265 })).collect()
266}
267
268#[derive(Clone, Debug)]
270struct StaticFunctionDef {
271 name: String,
272 is_unsafe: bool,
273 location: FunctionLocation,
274 signature: StaticSignatureDef,
275 static_return_type: TokenStream,
276 static_arg_types: TokenStream,
277}
278impl StaticFunctionDef {
279 fn make_constant(&self, verify_types: &[Type]) -> TokenStream {
280 let const_name = format!("_FUNC_{}", self.name);
281 let const_name = Ident::new(&const_name, Span::call_site());
282 let def = self;
283 let return_type = &self.static_return_type;
284 let arg_types = &self.static_arg_types;
285 quote! {
286 #[doc(hidden)]
287 #[allow(non_snake_case)]
288 pub const #const_name: static_reflect::funcs::FunctionDeclaration<#return_type, #arg_types> = {
289 #(let _ = <#verify_types as static_reflect::StaticReflect>::TYPE_INFO;)*
291 #def
292 };
293 }
294 }
295}
296
297#[derive(Clone, Debug)]
298struct StaticSignatureDef {
299 argument_types: Vec<TokenStream>,
300 return_type: TokenStream
301}
302
303#[derive(Clone, Debug)]
304enum FunctionLocation {
305 DynamicallyLinked {
306 link_name: Option<TokenStream>
307 },
308 AbsoluteAddress(TokenStream),
309}
310
311impl ToTokens for StaticFunctionDef {
312 fn to_tokens(&self, tokens: &mut TokenStream) {
313 let StaticFunctionDef {
314 ref name,
315 ref signature,
316 ref location,
317 ref is_unsafe,
318 ref static_return_type,
319 static_arg_types: ref staitc_arg_types
320 } = *self;
321 tokens.append_all(quote!(static_reflect::funcs::FunctionDeclaration::<'static, #static_return_type, #staitc_arg_types> {
322 name: #name,
323 is_unsafe: #is_unsafe,
324 signature: #signature,
325 location: #location,
326 return_type: ::std::marker::PhantomData,
327 arg_types: ::std::marker::PhantomData,
328 }));
329 }
330}
331
332impl ToTokens for FunctionLocation {
333 fn to_tokens(&self, tokens: &mut TokenStream) {
334 tokens.append_all(match *self {
335 FunctionLocation::DynamicallyLinked { link_name: None } => {
336 quote!(Some(static_reflect::funcs::FunctionLocation::DynamicallyLinked { link_name: None }))
337 },
338 FunctionLocation::DynamicallyLinked { link_name: Some(ref name) } => {
339 quote!(Some(static_reflect::funcs::FunctionLocation::DynamicallyLinked { link_name: Some(#name) }))
340 },
341 FunctionLocation::AbsoluteAddress(ref value) => {
342 quote!(Some(static_reflect::funcs::FunctionLocation::AbsoluteAddress(#value)))
343 },
344 });
345 }
346}
347
348impl ToTokens for StaticSignatureDef {
349 fn to_tokens(&self, tokens: &mut TokenStream) {
350 let StaticSignatureDef {
351 ref argument_types,
352 ref return_type
353 } = *self;
354 tokens.append_all(quote!(static_reflect::funcs::SignatureDef {
355 argument_types: &[#(#argument_types),*],
356 return_type: #return_type,
357 calling_convention: static_reflect::funcs::CallingConvention::StandardC
359 }))
360 }
361}