| 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 | |