1 | //! Implementation detail for the `cpp` crate. |
2 | //! |
3 | //! The purpose of this crate is only to allow sharing code between the |
4 | //! `cpp_build` and the `cpp_macros` crates. |
5 | |
6 | #[macro_use ] |
7 | extern crate syn; |
8 | extern crate proc_macro2; |
9 | |
10 | #[macro_use ] |
11 | extern crate lazy_static; |
12 | |
13 | use std::collections::hash_map::DefaultHasher; |
14 | use std::env; |
15 | use std::hash::{Hash, Hasher}; |
16 | use std::path::PathBuf; |
17 | |
18 | use proc_macro2::{Span, TokenStream, TokenTree}; |
19 | use syn::ext::IdentExt; |
20 | use syn::parse::{Parse, ParseStream, Result}; |
21 | use syn::{Attribute, Ident, Type}; |
22 | |
23 | pub const VERSION: &str = env!("CARGO_PKG_VERSION" ); |
24 | |
25 | pub const LIB_NAME: &str = "librust_cpp_generated.a" ; |
26 | pub const MSVC_LIB_NAME: &str = "rust_cpp_generated.lib" ; |
27 | |
28 | pub mod flags { |
29 | pub const IS_COPY_CONSTRUCTIBLE: u32 = 0; |
30 | pub const IS_DEFAULT_CONSTRUCTIBLE: u32 = 1; |
31 | pub const IS_TRIVIALLY_DESTRUCTIBLE: u32 = 2; |
32 | pub const IS_TRIVIALLY_COPYABLE: u32 = 3; |
33 | pub const IS_TRIVIALLY_DEFAULT_CONSTRUCTIBLE: u32 = 4; |
34 | } |
35 | |
36 | pub mod kw { |
37 | #![allow (non_camel_case_types)] |
38 | custom_keyword!(rust); |
39 | } |
40 | |
41 | /// This constant is expected to be a unique string within the compiled binary |
42 | /// which precedes a definition of the metadata. It begins with |
43 | /// rustcpp~metadata, which is printable to make it easier to locate when |
44 | /// looking at a binary dump of the metadata. |
45 | /// |
46 | /// NOTE: In the future we may want to use a object file parser and a custom |
47 | /// section rather than depending on this string being unique. |
48 | #[rustfmt::skip] |
49 | pub const STRUCT_METADATA_MAGIC: [u8; 128] = [ |
50 | b'r' , b'u' , b's' , b't' , b'c' , b'p' , b'p' , b'~' , |
51 | b'm' , b'e' , b't' , b'a' , b'd' , b'a' , b't' , b'a' , |
52 | 92, 74, 112, 213, 165, 185, 214, 120, 179, 17, 185, 25, 182, 253, 82, 118, |
53 | 148, 29, 139, 208, 59, 153, 78, 137, 230, 54, 26, 177, 232, 121, 132, 166, |
54 | 44, 106, 218, 57, 158, 33, 69, 32, 54, 204, 123, 226, 99, 117, 60, 173, |
55 | 112, 61, 56, 174, 117, 141, 126, 249, 79, 159, 6, 119, 2, 129, 147, 66, |
56 | 135, 136, 212, 252, 231, 105, 239, 91, 96, 232, 113, 94, 164, 255, 152, 144, |
57 | 64, 207, 192, 90, 225, 171, 59, 154, 60, 2, 0, 191, 114, 182, 38, 134, |
58 | 134, 183, 212, 227, 31, 217, 12, 5, 65, 221, 150, 59, 230, 96, 73, 62, |
59 | ]; |
60 | |
61 | lazy_static! { |
62 | pub static ref OUT_DIR: PathBuf = PathBuf::from(env::var("OUT_DIR" ).expect( |
63 | r#" |
64 | -- rust-cpp fatal error -- |
65 | |
66 | The OUT_DIR environment variable was not set. |
67 | NOTE: rustc must be run by Cargo."# |
68 | )); |
69 | pub static ref FILE_HASH: u64 = { |
70 | let mut hasher = std::collections::hash_map::DefaultHasher::new(); |
71 | OUT_DIR.hash(&mut hasher); |
72 | hasher.finish() |
73 | }; |
74 | } |
75 | |
76 | #[derive (Clone, Debug, Hash, PartialEq, Eq)] |
77 | pub struct Capture { |
78 | pub mutable: bool, |
79 | pub name: Ident, |
80 | pub cpp: String, |
81 | } |
82 | |
83 | impl Parse for Capture { |
84 | /// Parse a single captured variable inside within a `cpp!` macro. |
85 | /// Example: `mut foo as "int"` |
86 | fn parse(input: ParseStream) -> Result<Self> { |
87 | Ok(Capture { |
88 | mutable: input.parse::<Option<Token![mut]>>()?.is_some(), |
89 | name: input.call(function:Ident::parse_any)?, |
90 | cpp: { |
91 | input.parse::<Token![as]>()?; |
92 | input.parse::<syn::LitStr>()?.value() |
93 | }, |
94 | }) |
95 | } |
96 | } |
97 | |
98 | #[derive (Clone, Debug, Hash, PartialEq, Eq)] |
99 | pub struct ClosureSig { |
100 | pub captures: Vec<Capture>, |
101 | pub ret: Option<Type>, |
102 | pub cpp: String, |
103 | pub std_body: String, |
104 | } |
105 | |
106 | impl ClosureSig { |
107 | pub fn name_hash(&self) -> u64 { |
108 | // XXX: Use a better hasher than the default? |
109 | let mut hasher: DefaultHasher = DefaultHasher::new(); |
110 | self.hash(&mut hasher); |
111 | hasher.finish() |
112 | } |
113 | |
114 | pub fn extern_name(&self) -> Ident { |
115 | Ident::new(&format!("__cpp_closure_ {}" , self.name_hash()), Span::call_site()) |
116 | } |
117 | } |
118 | |
119 | #[derive (Clone, Debug)] |
120 | pub struct Closure { |
121 | pub sig: ClosureSig, |
122 | pub body: TokenTree, |
123 | pub body_str: String, // with `rust!` macro replaced |
124 | pub callback_offset: u32, |
125 | } |
126 | |
127 | impl Parse for Closure { |
128 | /// Parse the inside of a `cpp!` macro when this macro is a closure. |
129 | /// Example: `unsafe [foo as "int"] -> u32 as "int" { /*... */ } |
130 | fn parse(input: ParseStream) -> Result<Self> { |
131 | input.parse::<Option<Token![unsafe]>>()?; |
132 | |
133 | // Capture |
134 | let capture_content; |
135 | bracketed!(capture_content in input); |
136 | let captures = |
137 | syn::punctuated::Punctuated::<Capture, Token![,]>::parse_terminated(&capture_content)? |
138 | .into_iter() |
139 | .collect(); |
140 | |
141 | // Optional return type |
142 | let (ret, cpp) = if input.peek(Token![->]) { |
143 | input.parse::<Token![->]>()?; |
144 | let t: syn::Type = input.parse()?; |
145 | input.parse::<Token![as]>()?; |
146 | let s = input.parse::<syn::LitStr>()?.value(); |
147 | (Some(t), s) |
148 | } else { |
149 | (None, "void" .to_owned()) |
150 | }; |
151 | |
152 | let body = input.parse::<TokenTree>()?; |
153 | // Need to filter the spaces because there is a difference between |
154 | // proc_macro2 and proc_macro and the hashes would not match |
155 | let std_body = body.to_string().chars().filter(|x| !x.is_whitespace()).collect(); |
156 | |
157 | Ok(Closure { |
158 | sig: ClosureSig { captures, ret, cpp, std_body }, |
159 | body, |
160 | body_str: String::new(), |
161 | callback_offset: 0, |
162 | }) |
163 | } |
164 | } |
165 | |
166 | #[derive (Clone, Debug)] |
167 | pub struct Class { |
168 | pub name: Ident, |
169 | pub cpp: String, |
170 | pub attrs: Vec<Attribute>, |
171 | pub line: String, // the #line directive |
172 | } |
173 | |
174 | impl Class { |
175 | pub fn name_hash(&self) -> u64 { |
176 | let mut hasher: DefaultHasher = DefaultHasher::new(); |
177 | self.name.hash(&mut hasher); |
178 | self.cpp.hash(&mut hasher); |
179 | hasher.finish() |
180 | } |
181 | |
182 | pub fn derives(&self, i: &str) -> bool { |
183 | self.attrs.iter().any(|x: &Attribute| { |
184 | let mut result: bool = false; |
185 | if x.path().is_ident("derive" ) { |
186 | xResult<(), Error>.parse_nested_meta(|m: ParseNestedMeta<'_>| { |
187 | if m.path.is_ident(i) { |
188 | result = true; |
189 | } |
190 | Ok(()) |
191 | }) |
192 | .unwrap(); |
193 | } |
194 | result |
195 | }) |
196 | } |
197 | } |
198 | |
199 | impl Parse for Class { |
200 | /// Parse the inside of a `cpp_class!` macro. |
201 | /// Example: `#[derive(Default)] pub unsafe struct Foobar as "FooBar"` |
202 | fn parse(input: ParseStream) -> Result<Self> { |
203 | Ok(Class { |
204 | attrs: input.call(function:Attribute::parse_outer)?, |
205 | name: { |
206 | input.parse::<syn::Visibility>()?; |
207 | input.parse::<Token![unsafe]>()?; |
208 | input.parse::<Token![struct]>()?; |
209 | input.parse()? |
210 | }, |
211 | cpp: { |
212 | input.parse::<Token![as]>()?; |
213 | input.parse::<syn::LitStr>()?.value() |
214 | }, |
215 | line: String::new(), |
216 | }) |
217 | } |
218 | } |
219 | |
220 | #[allow (clippy::large_enum_variant)] |
221 | #[derive (Debug)] |
222 | pub enum Macro { |
223 | Closure(Closure), |
224 | Lit(TokenStream), |
225 | } |
226 | |
227 | impl Parse for Macro { |
228 | ///! Parse the inside of a `cpp!` macro (a literal or a closure) |
229 | fn parse(input: ParseStream) -> Result<Self> { |
230 | if input.peek(token:syn::token::Brace) { |
231 | let content: ParseBuffer<'_>; |
232 | braced!(content in input); |
233 | return Ok(Macro::Lit(content.parse()?)); |
234 | } |
235 | Ok(Macro::Closure(input.parse::<Closure>()?)) |
236 | } |
237 | } |
238 | |
239 | #[derive (Debug)] |
240 | pub struct RustInvocation { |
241 | pub begin: Span, |
242 | pub end: Span, |
243 | pub id: Ident, |
244 | pub return_type: Option<String>, |
245 | pub arguments: Vec<(Ident, String)>, // Vec of name and type |
246 | } |
247 | |
248 | impl Parse for RustInvocation { |
249 | /// Parse a `rust!` macro something looking like `rust!(ident [foo : bar as "bar"] { /*...*/ })` |
250 | fn parse(input: ParseStream) -> Result<Self> { |
251 | let rust_token = input.parse::<kw::rust>()?; |
252 | input.parse::<Token![!]>()?; |
253 | let macro_content; |
254 | let p = parenthesized!(macro_content in input); |
255 | let r = RustInvocation { |
256 | begin: rust_token.span, |
257 | end: p.span.close(), |
258 | id: macro_content.parse()?, |
259 | arguments: { |
260 | let capture_content; |
261 | bracketed!(capture_content in macro_content); |
262 | capture_content |
263 | .parse_terminated( |
264 | |input: ParseStream| -> Result<(Ident, String)> { |
265 | let i = input.call(Ident::parse_any)?; |
266 | input.parse::<Token![:]>()?; |
267 | input.parse::<Type>()?; |
268 | input.parse::<Token![as]>()?; |
269 | let s = input.parse::<syn::LitStr>()?.value(); |
270 | Ok((i, s)) |
271 | }, |
272 | Token![,], |
273 | )? |
274 | .into_iter() |
275 | .collect() |
276 | }, |
277 | return_type: if macro_content.peek(Token![->]) { |
278 | macro_content.parse::<Token![->]>()?; |
279 | macro_content.parse::<Type>()?; |
280 | macro_content.parse::<Token![as]>()?; |
281 | Some(macro_content.parse::<syn::LitStr>()?.value()) |
282 | } else { |
283 | None |
284 | }, |
285 | }; |
286 | macro_content.parse::<TokenTree>()?; |
287 | Ok(r) |
288 | } |
289 | } |
290 | |