cpp_macros-0.5.6/.cargo_vcs_info.json0000644000000001121377257570000132370ustar { "git": { "sha1": "65b553eaaa2e9fa7acefe12e25ff47724005d057" } } cpp_macros-0.5.6/Cargo.toml0000644000000026571377257570000112550ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "cpp_macros" version = "0.5.6" authors = ["Nika Layzell ", "Olivier Goffart "] description = "Procedural macro implementation for the `cpp` crate" documentation = "https://docs.rs/cpp_macros" readme = "../README.md" keywords = ["c", "cxx", "ffi", "compiler"] categories = ["development-tools::ffi"] license = "MIT/Apache-2.0" repository = "https://github.com/mystor/rust-cpp" [package.metadata.docs.rs] features = ["docs-only"] [lib] proc-macro = true [dependencies.aho-corasick] version = "0.7.15" [dependencies.byteorder] version = "1.0" [dependencies.cpp_common] version = "=0.5.6" [dependencies.if_rust_version] version = "1.0" [dependencies.lazy_static] version = "1.0" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "1.0" features = ["full", "visit"] [features] docs-only = [] cpp_macros-0.5.6/Cargo.toml.orig010064400017520001752000000016641377257547600147620ustar 00000000000000[package] name = "cpp_macros" version = "0.5.6" authors = ["Nika Layzell ", "Olivier Goffart "] edition = "2018" description = "Procedural macro implementation for the `cpp` crate" readme = "../README.md" license = "MIT/Apache-2.0" keywords = ["c", "cxx", "ffi", "compiler"] categories = ["development-tools::ffi"] repository = "https://github.com/mystor/rust-cpp" documentation = "https://docs.rs/cpp_macros" [features] # Ignore compilations error of the C++ code when building the documentation, as the docs.rs server # might not have the required libraries docs-only = [] [lib] proc-macro = true [dependencies] lazy_static = "1.0" cpp_common = { path = "../cpp_common", version = "=0.5.6" } syn = { version = "1.0", features=["full", "visit"] } quote = "1.0" proc-macro2 = "1.0" aho-corasick = "0.7.15" byteorder = "1.0" if_rust_version = "1.0" [package.metadata.docs.rs] features = [ "docs-only" ] cpp_macros-0.5.6/src/lib.rs010064400017520001752000000525651377240243600137650ustar 00000000000000//! This crate is the `cpp` procedural macro implementation. It is useless //! without the companion crates `cpp`, and `cpp_build`. //! //! For more information, see the [`cpp` crate module level //! documentation](https://docs.rs/cpp). #![recursion_limit = "128"] #[macro_use] extern crate syn; extern crate proc_macro; use proc_macro2::Span; use cpp_common::{flags, kw, RustInvocation, FILE_HASH, LIB_NAME, MSVC_LIB_NAME, OUT_DIR, VERSION}; use std::collections::HashMap; use std::iter::FromIterator; use syn::parse::Parser; use syn::Ident; use byteorder::{LittleEndian, ReadBytesExt}; use if_rust_version::if_rust_version; use lazy_static::lazy_static; use quote::{quote, quote_spanned}; use std::fs::File; use std::io::{self, BufReader, Read, Seek, SeekFrom}; struct MetaData { size: usize, align: usize, flags: u64, } impl MetaData { fn has_flag(&self, f: u32) -> bool { self.flags & (1 << f) != 0 } } lazy_static! { static ref METADATA: HashMap> = { let file = match open_lib_file() { Ok(x) => x, Err(e) => { #[cfg(not(feature = "docs-only"))] panic!( r#" -- rust-cpp fatal error -- Failed to open the target library file. NOTE: Did you make sure to add the rust-cpp build script? {}"#, e ); #[cfg(feature = "docs-only")] { eprintln!("Error while opening target library: {}", e); return Default::default(); }; } }; read_metadata(file).expect( r#" -- rust-cpp fatal error -- I/O error while reading metadata from target library file."#, ) }; } /// NOTE: This panics when it can produce a better error message fn read_metadata(file: File) -> io::Result>> { let mut file = BufReader::new(file); let end = { const AUTO_KEYWORD: &[&[u8]] = &[&cpp_common::STRUCT_METADATA_MAGIC]; let aut = aho_corasick::AhoCorasick::new(AUTO_KEYWORD); let found = aut.stream_find_iter(&mut file).next().expect( r#" -- rust-cpp fatal error -- Struct metadata not present in target library file. NOTE: Double-check that the version of cpp_build and cpp_macros match"#, )?; found.end() }; file.seek(SeekFrom::Start(end as u64))?; // Read & convert the version buffer into a string & compare with our // version. let mut version_buf = [0; 16]; file.read_exact(&mut version_buf)?; let version = version_buf .iter() .take_while(|b| **b != b'\0') .map(|b| *b as char) .collect::(); assert_eq!( version, VERSION, r#" -- rust-cpp fatal error -- Version mismatch between cpp_macros and cpp_build for same crate."# ); let length = file.read_u64::()?; let mut metadata = HashMap::new(); for _ in 0..length { let hash = file.read_u64::()?; let size = file.read_u64::()? as usize; let align = file.read_u64::()? as usize; let flags = file.read_u64::()? as u64; metadata .entry(hash) .or_insert_with(Vec::new) .push(MetaData { size, align, flags }); } Ok(metadata) } /// Try to open a file handle to the lib file. This is used to scan it for /// metadata. We check both `MSVC_LIB_NAME` and `LIB_NAME`, in case we are on /// or are targeting Windows. fn open_lib_file() -> io::Result { if let Ok(file) = File::open(OUT_DIR.join(MSVC_LIB_NAME)) { Ok(file) } else { File::open(OUT_DIR.join(LIB_NAME)) } } fn find_all_rust_macro( input: syn::parse::ParseStream, ) -> Result, syn::parse::Error> { let mut r = Vec::::new(); while !input.is_empty() { if input.peek(kw::rust) { if let Ok(ri) = input.parse::() { r.push(ri); } } else if input.peek(syn::token::Brace) { let c; braced!(c in input); r.extend(find_all_rust_macro(&c)?); } else if input.peek(syn::token::Paren) { let c; parenthesized!(c in input); r.extend(find_all_rust_macro(&c)?); } else if input.peek(syn::token::Bracket) { let c; bracketed!(c in input); r.extend(find_all_rust_macro(&c)?); } else { input.parse::()?; } } Ok(r) } /// Find the occurrence of the `stringify!` macro within the macro derive fn extract_original_macro(input: &syn::DeriveInput) -> Option { #[derive(Default)] struct Finder(Option); impl<'ast> syn::visit::Visit<'ast> for Finder { fn visit_macro(&mut self, mac: &'ast syn::Macro) { if mac.path.segments.len() == 1 && mac.path.segments[0].ident == "stringify" { self.0 = Some(mac.tokens.clone()); } } } let mut f = Finder::default(); syn::visit::visit_derive_input(&mut f, &input); f.0 } #[proc_macro_derive(__cpp_internal_closure)] pub fn expand_internal(input: proc_macro::TokenStream) -> proc_macro::TokenStream { assert_eq!( env!("CARGO_PKG_VERSION"), VERSION, "Internal Error: mismatched cpp_common and cpp_macros versions" ); // Parse the macro input let input = extract_original_macro(&parse_macro_input!(input as syn::DeriveInput)).unwrap(); let closure = match syn::parse2::(input) { Ok(x) => x, Err(err) => return err.to_compile_error().into(), }; // Get the size data compiled by the build macro let size_data = match METADATA.get(&closure.sig.name_hash()) { Some(x) => x, None => { #[cfg(not(feature = "docs-only"))] return quote!(compile_error! { r#"This cpp! macro is not found in the library's rust-cpp metadata. NOTE: Only cpp! macros found directly in the program source will be parsed - NOTE: They cannot be generated by macro expansion."#}) .into(); #[cfg(feature = "docs-only")] { return quote! { macro_rules! __cpp_closure_impl { ($($x:tt)*) => { panic!("docs-only"); } } } .into(); }; } }; let mut extern_params = Vec::new(); let mut tt_args = Vec::new(); let mut call_args = Vec::new(); for (i, capture) in closure.sig.captures.iter().enumerate() { let written_name = &capture.name; let span = written_name.span(); let mac_name = Ident::new(&format!("var_{}", written_name), span); let mac_cty = Ident::new(&format!("cty_{}", written_name), span); // Generate the assertion to check that the size and align of the types // match before calling. let MetaData { size, align, .. } = size_data[i + 1]; let sizeof_msg = format!( "size_of for argument `{}` does not match between c++ and \ rust", &capture.name ); let alignof_msg = format!( "align_of for argument `{}` does not match between c++ and \ rust", &capture.name ); let assertion = quote_spanned! {span=> // Perform a compile time check that the sizes match. This should be // a no-op. if false { ::core::mem::transmute::<_, [u8; #size]>( ::core::ptr::read(&$#mac_name)); } // NOTE: Both of these calls should be dead code in opt builds. assert!(::core::mem::size_of_val(&$#mac_name) == #size, #sizeof_msg); assert!(::core::mem::align_of_val(&$#mac_name) == #align, #alignof_msg); }; let mb_mut = if capture.mutable { quote_spanned!(span=> mut) } else { quote!() }; let ptr = if capture.mutable { quote_spanned!(span=> *mut) } else { quote_spanned!(span=> *const) }; let arg_name = Ident::new(&format!("arg_{}", written_name), span); extern_params.push(quote_spanned!(span=> #arg_name : #ptr u8)); tt_args.push(quote_spanned!(span=> #mb_mut $#mac_name : ident as $#mac_cty : tt)); call_args.push(quote_spanned!(span=> { #assertion &#mb_mut $#mac_name as #ptr _ as #ptr u8 })); } let extern_name = closure.sig.extern_name(); let ret_ty = &closure.sig.ret; let MetaData { size: ret_size, align: ret_align, flags, } = size_data[0]; let is_void = closure.sig.cpp == "void"; let decl = if is_void { quote! { fn #extern_name(#(#extern_params),*); } } else { quote! { fn #extern_name(#(#extern_params,)* _result: *mut #ret_ty); } }; let call = if is_void { assert!(ret_size == 0, "`void` should have a size of 0!"); quote! { #extern_name(#(#call_args),*); #[cfg_attr(feature = "cargo-clippy", allow(useless_transmute))] ::core::mem::transmute::<(), (#ret_ty)>(()) } } else { // static assert that the size and alignement are the same let assert_size = quote! { if false { const _assert_size: [(); #ret_size] = [(); ::core::mem::size_of::<#ret_ty>()]; const _assert_align: [(); #ret_align] = [(); ::core::mem::align_of::<#ret_ty>()]; } }; if_rust_version!(>= 1.36 { quote!( #assert_size let mut result = ::core::mem::MaybeUninit::<#ret_ty>::uninit(); #extern_name(#(#call_args,)* result.as_mut_ptr()); result.assume_init() )} else { quote!( #assert_size let mut result: #ret_ty = ::core::mem::uninitialized(); #extern_name(#(#call_args,)* &mut result); result )}) }; let input = proc_macro2::TokenStream::from_iter([closure.body].iter().cloned()); let rust_invocations = find_all_rust_macro.parse2(input).expect("rust! macro"); let init_callbacks = if !rust_invocations.is_empty() { let rust_cpp_callbacks = Ident::new( &format!("rust_cpp_callbacks{}", *FILE_HASH), Span::call_site(), ); let offset = (flags >> 32) as isize; let callbacks: Vec = rust_invocations.iter().map(|x| x.id.clone()).collect(); quote! { use ::std::sync::Once; static INIT_INVOCATIONS: Once = Once::new(); INIT_INVOCATIONS.call_once(|| { // #rust_cpp_callbacks is in fact an array. Since we cannot represent it in rust, // we just are gonna take the pointer to it can offset from that. extern "C" { #[no_mangle] static mut #rust_cpp_callbacks: *const ::std::os::raw::c_void; } let callbacks_array : *mut *const ::std::os::raw::c_void = &mut #rust_cpp_callbacks; let mut offset = #offset; #( offset += 1; *callbacks_array.offset(offset - 1) = #callbacks as *const ::std::os::raw::c_void; )* }); } } else { quote!() }; let result = quote! { extern "C" { #decl } macro_rules! __cpp_closure_impl { (#(#tt_args),*) => { { #init_callbacks #call } } } }; result.into() } #[proc_macro_derive(__cpp_internal_class)] pub fn expand_wrap_class(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Parse the macro input let input = extract_original_macro(&parse_macro_input!(input as syn::DeriveInput)).unwrap(); let class = match ::syn::parse2::(input) { Ok(x) => x, Err(err) => return err.to_compile_error().into(), }; let hash = class.name_hash(); let class_name = class.name.clone(); // Get the size data compiled by the build macro let size_data = match METADATA.get(&hash) { Some(x) => x, None => { #[cfg(not(feature = "docs-only"))] return quote!(compile_error! { r#"This cpp_class! macro is not found in the library's rust-cpp metadata. NOTE: Only cpp_class! macros found directly in the program source will be parsed - NOTE: They cannot be generated by macro expansion."#}) .into(); #[cfg(feature = "docs-only")] { let mut result = quote! { #[doc(hidden)] impl ::cpp::CppTrait for #class_name { type BaseType = usize; const ARRAY_SIZE: usize = 1; const CPP_TYPE: &'static str = stringify!(#class_name); } #[doc = "NOTE: this trait will only be enabled if the C++ underlying type is trivially copyable"] impl ::core::marker::Copy for #class_name { } #[doc = "NOTE: this trait will only be enabled if the C++ underlying type is copyable"] impl ::core::clone::Clone for #class_name { fn clone(&self) -> Self { panic!("docs-only") } } #[doc = "NOTE: this trait will only be enabled if the C++ underlying type is default constructible"] impl ::core::default::Default for #class_name { fn default() -> Self { panic!("docs-only") } } }; if class.derives("PartialEq") { result = quote! { #result impl ::core::cmp::PartialEq for #class_name { fn eq(&self, other: &#class_name) -> bool { panic!("docs-only") } } }; } if class.derives("PartialOrd") { result = quote! { #result impl ::core::cmp::PartialOrd for #class_name { fn partial_cmp(&self, other: &#class_name) -> ::core::option::Option<::core::cmp::Ordering> { panic!("docs-only") } } }; } if class.derives("Ord") { result = quote! { #result impl ::core::cmp::Ord for #class_name { fn cmp(&self, other: &#class_name) -> ::core::cmp::Ordering { panic!("docs-only") } } }; } return result.into(); }; } }; let (size, align) = (size_data[0].size, size_data[0].align); let base_type = match align { 1 => quote!(u8), 2 => quote!(u16), 4 => quote!(u32), 8 => quote!(u64), _ => panic!("unsupported alignment"), }; let destructor_name = Ident::new(&format!("__cpp_destructor_{}", hash), Span::call_site()); let copyctr_name = Ident::new(&format!("__cpp_copy_{}", hash), Span::call_site()); let defaultctr_name = Ident::new(&format!("__cpp_default_{}", hash), Span::call_site()); let mut result = quote! { #[doc(hidden)] impl ::cpp::CppTrait for #class_name { type BaseType = #base_type; const ARRAY_SIZE: usize = #size / #align; const CPP_TYPE: &'static str = stringify!(#class_name); } }; if !size_data[0].has_flag(flags::IS_TRIVIALLY_DESTRUCTIBLE) { result = quote! { #result impl ::core::ops::Drop for #class_name { fn drop(&mut self) { unsafe { extern "C" { fn #destructor_name(_: *mut #class_name); } #destructor_name(&mut *self); } } } }; }; if size_data[0].has_flag(flags::IS_COPY_CONSTRUCTIBLE) { if !size_data[0].has_flag(flags::IS_TRIVIALLY_COPYABLE) && !class.derives("Copy") { let call_construct = if_rust_version!(>= 1.36 { quote!( let mut result = ::core::mem::MaybeUninit::::uninit(); #copyctr_name(& *self, result.as_mut_ptr()); result.assume_init() )} else { quote!( let mut result : Self = ::core::mem::uninitialized(); #copyctr_name(& *self, &mut result); result )}); result = quote! { #result impl ::core::clone::Clone for #class_name { fn clone(&self) -> Self { unsafe { extern "C" { fn #copyctr_name(src: *const #class_name, dst: *mut #class_name); } #call_construct } } } }; } else { result = quote! { #result impl ::core::marker::Copy for #class_name { } impl ::core::clone::Clone for #class_name { fn clone(&self) -> Self { *self } } }; }; } else if class.derives("Clone") { panic!("C++ class is not copyable"); } if size_data[0].has_flag(flags::IS_DEFAULT_CONSTRUCTIBLE) { let call_construct = if_rust_version!(>= 1.36 { quote!( let mut result = ::core::mem::MaybeUninit::::uninit(); #defaultctr_name(result.as_mut_ptr()); result.assume_init() )} else { quote!( let mut result : Self = ::core::mem::uninitialized(); #defaultctr_name(&mut result); result )}); result = quote! { #result impl ::core::default::Default for #class_name { fn default() -> Self { unsafe { extern "C" { fn #defaultctr_name(dst: *mut #class_name); } #call_construct } } } }; } else if class.derives("Default") { panic!("C++ class is not default constructible"); } if class.derives("PartialEq") { let equal_name = Ident::new(&format!("__cpp_equal_{}", hash), Span::call_site()); result = quote! { #result impl ::core::cmp::PartialEq for #class_name { fn eq(&self, other: &#class_name) -> bool { unsafe { extern "C" { fn #equal_name(a: *const #class_name, b: *const #class_name) -> bool; } #equal_name(& *self, other) } } } }; } if class.derives("PartialOrd") { let compare_name = Ident::new(&format!("__cpp_compare_{}", hash), Span::call_site()); let f = |func, cmp| { quote! { fn #func(&self, other: &#class_name) -> bool { unsafe { extern "C" { fn #compare_name(a: *const #class_name, b: *const #class_name, cmp : i32) -> i32; } #compare_name(& *self, other, #cmp) != 0 } } } }; let lt = f(quote! {lt}, -2); let gt = f(quote! {gt}, 2); let le = f(quote! {le}, -1); let ge = f(quote! {ge}, 1); result = quote! { #result impl ::core::cmp::PartialOrd for #class_name { #lt #gt #le #ge fn partial_cmp(&self, other: &#class_name) -> ::core::option::Option<::core::cmp::Ordering> { use ::core::cmp::Ordering; unsafe { extern "C" { fn #compare_name(a: *const #class_name, b: *const #class_name, cmp : i32) -> i32; } ::core::option::Option::Some(match #compare_name(& *self, other, 0) { -1 => Ordering::Less, 0 => Ordering::Equal, 1 => Ordering::Greater, _ => panic!() }) } } } }; } if class.derives("Ord") { let compare_name = Ident::new(&format!("__cpp_compare_{}", hash), Span::call_site()); result = quote! { #result impl ::core::cmp::Ord for #class_name { fn cmp(&self, other: &#class_name) -> ::core::cmp::Ordering { unsafe { use ::core::cmp::Ordering; extern "C" { fn #compare_name(a: *const #class_name, b: *const #class_name, cmp : i32) -> i32; } match #compare_name(& *self, other, 0) { -1 => Ordering::Less, 0 => Ordering::Equal, 1 => Ordering::Greater, _ => panic!() } } } } }; } if class.derives("Hash") { panic!("Deriving from Hash is not implemented") }; if class.derives("Debug") { panic!("Deriving from Debug is not implemented") }; result.into() }