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]
7extern crate syn;
8extern crate proc_macro2;
9
10#[macro_use]
11extern crate lazy_static;
12
13use std::collections::hash_map::DefaultHasher;
14use std::env;
15use std::hash::{Hash, Hasher};
16use std::path::PathBuf;
17
18use proc_macro2::{Span, TokenStream, TokenTree};
19use syn::ext::IdentExt;
20use syn::parse::{Parse, ParseStream, Result};
21use syn::{Attribute, Ident, Type};
22
23pub const VERSION: &str = env!("CARGO_PKG_VERSION");
24
25pub const LIB_NAME: &str = "librust_cpp_generated.a";
26pub const MSVC_LIB_NAME: &str = "rust_cpp_generated.lib";
27
28pub 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
36pub 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]
49pub 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
61lazy_static! {
62 pub static ref OUT_DIR: PathBuf = PathBuf::from(env::var("OUT_DIR").expect(
63 r#"
64-- rust-cpp fatal error --
65
66The OUT_DIR environment variable was not set.
67NOTE: 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)]
77pub struct Capture {
78 pub mutable: bool,
79 pub name: Ident,
80 pub cpp: String,
81}
82
83impl 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)]
99pub struct ClosureSig {
100 pub captures: Vec<Capture>,
101 pub ret: Option<Type>,
102 pub cpp: String,
103 pub std_body: String,
104}
105
106impl 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)]
120pub 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
127impl 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)]
167pub struct Class {
168 pub name: Ident,
169 pub cpp: String,
170 pub attrs: Vec<Attribute>,
171 pub line: String, // the #line directive
172}
173
174impl 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
199impl 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)]
222pub enum Macro {
223 Closure(Closure),
224 Lit(TokenStream),
225}
226
227impl 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)]
240pub 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
248impl 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