impl AttributeInfo { fn extract(attrs: &mut Vec<Attribute>) -> Self { letmut repr = false; letmut opaque = false; letmut is_out = false;
attrs.retain(|attr| { let ident = &attr.path().segments.iter().next().unwrap().ident; if ident == "repr" {
repr = true; // don't actually extract repr attrs, just detect them returntrue;
} elseif ident == "diplomat" { if attr.path().segments.len() == 2 { let seg = &attr.path().segments.iter().nth(1).unwrap().ident; if seg == "opaque" {
opaque = true; returnfalse;
} elseif seg == "out" {
is_out = true; returnfalse;
} elseif seg == "rust_link"
|| seg == "out"
|| seg == "attr"
|| seg == "skip_if_ast"
|| seg == "abi_rename"
{ // diplomat-tool reads these, not diplomat::bridge. // throw them away so rustc doesn't complain about unknown attributes returnfalse;
} elseif seg == "enum_convert" || seg == "transparent_convert" { // diplomat::bridge doesn't read this, but it's handled separately // as an attribute returntrue;
} else {
panic!("Only #[diplomat::opaque] and #[diplomat::rust_link] are supported")
}
} else {
panic!("#[diplomat::foo] attrs have a single-segment path name")
}
} true
});
Self {
repr,
opaque,
is_out,
}
}
}
fn gen_bridge(mut input: ItemMod) -> ItemMod { let module = ast::Module::from_syn(&input, true); // Clean out any diplomat attributes so Rust doesn't get mad let _attrs = AttributeInfo::extract(&mut input.attrs); let (brace, mut new_contents) = input.content.unwrap();
new_contents.push(parse2(quote! { use diplomat_runtime::*; }).unwrap());
new_contents.iter_mut().for_each(|c| match c {
Item::Struct(s) => { let info = AttributeInfo::extract(&mut s.attrs);
// Normal opaque types don't need repr(transparent) because the inner type is // never referenced. #[diplomat::transparent_convert] handles adding repr(transparent) // on its own if !info.opaque { let copy = if !info.is_out { // Nothing stops FFI from copying, so we better make sure the struct is Copy.
quote!(#[derive(Clone, Copy)])
} else {
quote!()
};
let repr = if !info.repr {
quote!(#[repr(C)])
} else {
quote!()
};
*s = syn::parse_quote! { #repr #copy #s
}
}
}
Item::Enum(e) => { let info = AttributeInfo::extract(&mut e.attrs); if info.opaque {
panic!("#[diplomat::opaque] not allowed on enums")
} for v in &mut e.variants { let info = AttributeInfo::extract(&mut v.attrs); if info.opaque {
panic!("#[diplomat::opaque] not allowed on enum variants");
}
}
*e = syn::parse_quote! { #[repr(C)] #[derive(Clone, Copy)] #e
};
}
Item::Impl(i) => { for item in &mut i.items { iflet syn::ImplItem::Fn(refmut m) = *item { let info = AttributeInfo::extract(&mut m.attrs); if info.opaque {
panic!("#[diplomat::opaque] not allowed on methods")
}
}
}
}
_ => (),
});
for custom_type in module.declared_types.values() {
custom_type.methods().iter().for_each(|m| {
new_contents.push(gen_custom_type_method(custom_type, m));
});
let destroy_ident = Ident::new(custom_type.dtor_name().as_str(), Span::call_site());
let cfg = cfgs_to_stream(&custom_type.attrs().cfg);
// for now, body is empty since all we need to do is drop the box // TODO(#13): change to take a `*mut` and handle DST boxes appropriately
new_contents.push(Item::Fn(syn::parse_quote! { #[no_mangle] #cfg extern"C"fn#destroy_ident#lifetime_defs(this: Box<#type_ident#lifetimes>) {}
}));
}
/// Mark a module to be exposed through Diplomat-generated FFI. #[proc_macro_attribute] pubfn bridge(
_attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream { let expanded = gen_bridge(parse_macro_input!(input));
proc_macro::TokenStream::from(expanded.to_token_stream())
}
/// Generate From and Into implementations for a Diplomat enum /// /// This is invoked as `#[diplomat::enum_convert(OtherEnumName)]` /// on a Diplomat enum. It will assume the other enum has exactly the same variants /// and generate From and Into implementations using those. In case that enum is `#[non_exhaustive]`, /// you may use `#[diplomat::enum_convert(OtherEnumName, needs_wildcard)]` to generate a panicky wildcard /// branch. It is up to the library author to ensure the enums are kept in sync. You may use the `#[non_exhaustive_omitted_patterns]` /// lint to enforce this. #[proc_macro_attribute] pubfn enum_convert(
attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream { // proc macros handle compile errors by using special error tokens. // In case of an error, we don't want the original code to go away too // (otherwise that will cause more errors) so we hold on to it and we tack it in // with no modifications below let input_cached: proc_macro2::TokenStream = input.clone().into(); let expanded =
enum_convert::gen_enum_convert(parse_macro_input!(attr), parse_macro_input!(input));
let full = quote! { #expanded #input_cached
};
proc_macro::TokenStream::from(full.to_token_stream())
}
/// Generate conversions from inner types for opaque Diplomat types with a single field /// /// This is invoked as `#[diplomat::transparent_convert]` /// on an opaque Diplomat type. It will add `#[repr(transparent)]` and implement `pub(crate) fn transparent_convert()` /// which allows constructing an `&Self` from a reference to the inner field. #[proc_macro_attribute] pubfn transparent_convert(
_attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream { // proc macros handle compile errors by using special error tokens. // In case of an error, we don't want the original code to go away too // (otherwise that will cause more errors) so we hold on to it and we tack it in // with no modifications below let input_cached: proc_macro2::TokenStream = input.clone().into(); let expanded = transparent_convert::gen_transparent_convert(parse_macro_input!(input));
let full = quote! { #expanded #input_cached
};
proc_macro::TokenStream::from(full.to_token_stream())
}
#[cfg(test)] mod tests { use std::fs::File; use std::io::{Read, Write}; use std::process::Command;
use quote::ToTokens; use syn::parse_quote; use tempfile::tempdir;
usesuper::gen_bridge;
fn rustfmt_code(code: &str) -> String { let dir = tempdir().unwrap(); let file_path = dir.path().join("temp.rs"); letmut file = File::create(file_path.clone()).unwrap();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.