diff options
Diffstat (limited to 'third_party/rust/diplomat/src')
15 files changed, 1099 insertions, 0 deletions
diff --git a/third_party/rust/diplomat/src/enum_convert.rs b/third_party/rust/diplomat/src/enum_convert.rs new file mode 100644 index 0000000000..0244f320e6 --- /dev/null +++ b/third_party/rust/diplomat/src/enum_convert.rs @@ -0,0 +1,85 @@ +use quote::{quote, ToTokens}; +use syn::parse::{Parse, ParseStream}; +use syn::*; + +// An attribute that is a list of idents +pub struct EnumConvertAttribute { + path: Path, + + needs_wildcard: bool, +} + +impl Parse for EnumConvertAttribute { + fn parse(input: ParseStream) -> Result<Self> { + let paths = input.parse_terminated(Path::parse, Token![,])?; + if paths.is_empty() { + return Err(input.error("#[diplomat::enum_convert] needs a path argument")); + } + let needs_wildcard = if paths.len() == 2 { + if let Some(ident) = paths[1].get_ident() { + if ident == "needs_wildcard" { + true + } else { + return Err(input.error( + "#[diplomat::enum_convert] only recognizes needs_wildcard keyword", + )); + } + } else { + return Err( + input.error("#[diplomat::enum_convert] only recognizes needs_wildcard keyword") + ); + } + } else if paths.len() > 1 { + return Err(input.error("#[diplomat::enum_convert] only supports up to two arguments")); + } else { + // no needs_wildcard marker + false + }; + Ok(EnumConvertAttribute { + path: paths[0].clone(), + needs_wildcard, + }) + } +} + +pub fn gen_enum_convert(attr: EnumConvertAttribute, input: ItemEnum) -> proc_macro2::TokenStream { + let mut from_arms = vec![]; + let mut into_arms = vec![]; + + let this_name = &input.ident; + let other_name = &attr.path; + for variant in &input.variants { + if variant.fields != Fields::Unit { + return Error::new(variant.ident.span(), "variant may not have fields") + .to_compile_error(); + } + + let variant_name = &variant.ident; + from_arms.push(quote!(#other_name::#variant_name => Self::#variant_name)); + into_arms.push(quote!(#this_name::#variant_name => Self::#variant_name)); + } + + if attr.needs_wildcard { + let error = format!( + "Encountered unknown field for {}", + other_name.to_token_stream() + ); + from_arms.push(quote!(_ => unreachable!(#error))) + } + quote! { + impl From<#other_name> for #this_name { + fn from(other: #other_name) -> Self { + match other { + #(#from_arms,)* + } + } + } + impl From<#this_name> for #other_name { + fn from(this: #this_name) -> Self { + match this { + #(#into_arms,)* + } + } + } + } +} diff --git a/third_party/rust/diplomat/src/lib.rs b/third_party/rust/diplomat/src/lib.rs new file mode 100644 index 0000000000..dad44bff62 --- /dev/null +++ b/third_party/rust/diplomat/src/lib.rs @@ -0,0 +1,691 @@ +use proc_macro2::Span; +use quote::{quote, ToTokens}; +use syn::*; + +use diplomat_core::ast; + +mod enum_convert; +mod transparent_convert; + +fn cfgs_to_stream(attrs: &[Attribute]) -> proc_macro2::TokenStream { + attrs + .iter() + .fold(quote!(), |prev, attr| quote!(#prev #attr)) +} + +fn gen_params_at_boundary(param: &ast::Param, expanded_params: &mut Vec<FnArg>) { + match ¶m.ty { + ast::TypeName::StrReference(_) | ast::TypeName::PrimitiveSlice(..) => { + let data_type = if let ast::TypeName::PrimitiveSlice(.., prim) = ¶m.ty { + ast::TypeName::Primitive(*prim).to_syn().to_token_stream() + } else { + quote! { u8 } + }; + expanded_params.push(FnArg::Typed(PatType { + attrs: vec![], + pat: Box::new(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site()), + subpat: None, + })), + colon_token: syn::token::Colon(Span::call_site()), + ty: Box::new( + parse2({ + if let ast::TypeName::PrimitiveSlice(_, ast::Mutability::Mutable, _) = + ¶m.ty + { + quote! { *mut #data_type } + } else { + quote! { *const #data_type } + } + }) + .unwrap(), + ), + })); + + expanded_params.push(FnArg::Typed(PatType { + attrs: vec![], + pat: Box::new(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site()), + subpat: None, + })), + colon_token: syn::token::Colon(Span::call_site()), + ty: Box::new( + parse2(quote! { + usize + }) + .unwrap(), + ), + })); + } + o => { + expanded_params.push(FnArg::Typed(PatType { + attrs: vec![], + pat: Box::new(Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new(param.name.as_str(), Span::call_site()), + subpat: None, + })), + colon_token: syn::token::Colon(Span::call_site()), + ty: Box::new(o.to_syn()), + })); + } + } +} + +fn gen_params_invocation(param: &ast::Param, expanded_params: &mut Vec<Expr>) { + match ¶m.ty { + ast::TypeName::StrReference(_) | ast::TypeName::PrimitiveSlice(..) => { + let data_ident = + Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site()); + let len_ident = Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site()); + + let tokens = if let ast::TypeName::PrimitiveSlice(_, mutability, _) = ¶m.ty { + match mutability { + ast::Mutability::Mutable => quote! { + unsafe { core::slice::from_raw_parts_mut(#data_ident, #len_ident) } + }, + ast::Mutability::Immutable => quote! { + unsafe { core::slice::from_raw_parts(#data_ident, #len_ident) } + }, + } + } else { + // TODO(#57): don't just unwrap? or should we assume that the other side gives us a good value? + quote! { + unsafe { + core::str::from_utf8(core::slice::from_raw_parts(#data_ident, #len_ident)).unwrap() + } + } + }; + expanded_params.push(parse2(tokens).unwrap()); + } + ast::TypeName::Result(_, _, _) => { + let param = ¶m.name; + expanded_params.push(parse2(quote!(#param.into())).unwrap()); + } + _ => { + expanded_params.push(Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Ident::new(param.name.as_str(), Span::call_site()).into(), + })); + } + } +} + +fn gen_custom_type_method(strct: &ast::CustomType, m: &ast::Method) -> Item { + let self_ident = Ident::new(strct.name().as_str(), Span::call_site()); + let method_ident = Ident::new(m.name.as_str(), Span::call_site()); + let extern_ident = Ident::new(m.full_path_name.as_str(), Span::call_site()); + + let mut all_params = vec![]; + m.params.iter().for_each(|p| { + gen_params_at_boundary(p, &mut all_params); + }); + + let mut all_params_invocation = vec![]; + m.params.iter().for_each(|p| { + gen_params_invocation(p, &mut all_params_invocation); + }); + + let this_ident = Pat::Ident(PatIdent { + attrs: vec![], + by_ref: None, + mutability: None, + ident: Ident::new("this", Span::call_site()), + subpat: None, + }); + + if let Some(self_param) = &m.self_param { + all_params.insert( + 0, + FnArg::Typed(PatType { + attrs: vec![], + pat: Box::new(this_ident.clone()), + colon_token: syn::token::Colon(Span::call_site()), + ty: Box::new(self_param.to_typename().to_syn()), + }), + ); + } + + let lifetimes = { + let lifetime_env = &m.lifetime_env; + if lifetime_env.is_empty() { + quote! {} + } else { + quote! { <#lifetime_env> } + } + }; + + let method_invocation = if m.self_param.is_some() { + quote! { #this_ident.#method_ident } + } else { + quote! { #self_ident::#method_ident } + }; + + let (return_tokens, maybe_into) = if let Some(return_type) = &m.return_type { + if let ast::TypeName::Result(ok, err, true) = return_type { + let ok = ok.to_syn(); + let err = err.to_syn(); + ( + quote! { -> diplomat_runtime::DiplomatResult<#ok, #err> }, + quote! { .into() }, + ) + } else { + let return_type_syn = return_type.to_syn(); + (quote! { -> #return_type_syn }, quote! {}) + } + } else { + (quote! {}, quote! {}) + }; + + let writeable_flushes = m + .params + .iter() + .filter(|p| p.is_writeable()) + .map(|p| { + let p = &p.name; + quote! { #p.flush(); } + }) + .collect::<Vec<_>>(); + + let cfg = cfgs_to_stream(&m.attrs.cfg); + + if writeable_flushes.is_empty() { + Item::Fn(syn::parse_quote! { + #[no_mangle] + #cfg + extern "C" fn #extern_ident#lifetimes(#(#all_params),*) #return_tokens { + #method_invocation(#(#all_params_invocation),*) #maybe_into + } + }) + } else { + Item::Fn(syn::parse_quote! { + #[no_mangle] + #cfg + extern "C" fn #extern_ident#lifetimes(#(#all_params),*) #return_tokens { + let ret = #method_invocation(#(#all_params_invocation),*); + #(#writeable_flushes)* + ret #maybe_into + } + }) + } +} + +struct AttributeInfo { + repr: bool, + opaque: bool, +} + +impl AttributeInfo { + fn extract(attrs: &mut Vec<Attribute>) -> Self { + let mut repr = false; + let mut opaque = 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 + return true; + } else if ident == "diplomat" { + if attr.path().segments.len() == 2 { + let seg = &attr.path().segments.iter().nth(1).unwrap().ident; + if seg == "opaque" { + opaque = true; + return false; + } else if seg == "rust_link" || seg == "out" || seg == "attr" { + // diplomat-tool reads these, not diplomat::bridge. + // throw them away so rustc doesn't complain about unknown attributes + return false; + } else if seg == "enum_convert" || seg == "transparent_convert" { + // diplomat::bridge doesn't read this, but it's handled separately + // as an attribute + return true; + } 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 } + } +} + +fn gen_bridge(input: ItemMod) -> ItemMod { + let module = ast::Module::from_syn(&input, true); + let (brace, mut new_contents) = input.content.unwrap(); + + new_contents.iter_mut().for_each(|c| match c { + Item::Struct(s) => { + let info = AttributeInfo::extract(&mut s.attrs); + if info.opaque || !info.repr { + let repr = if info.opaque { + // 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 + quote!() + } else { + quote!(#[repr(C)]) + }; + *s = syn::parse_quote! { + #repr + #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)] + #e + }; + } + + Item::Impl(i) => { + for item in &mut i.items { + if let syn::ImplItem::Fn(ref mut 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( + format!("{}_destroy", custom_type.name()).as_str(), + Span::call_site(), + ); + + let type_ident = custom_type.name().to_syn(); + + let (lifetime_defs, lifetimes) = if let Some(lifetime_env) = custom_type.lifetimes() { + ( + quote! { <#lifetime_env> }, + lifetime_env.lifetimes_to_tokens(), + ) + } else { + (quote! {}, quote! {}) + }; + + 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>) {} + })); + } + + ItemMod { + attrs: input.attrs, + vis: input.vis, + mod_token: input.mod_token, + ident: input.ident, + content: Some((brace, new_contents)), + semi: input.semi, + unsafety: None, + } +} + +/// Mark a module to be exposed through Diplomat-generated FFI. +#[proc_macro_attribute] +pub fn 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] +pub fn 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] +pub fn 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; + + use super::gen_bridge; + + fn rustfmt_code(code: &str) -> String { + let dir = tempdir().unwrap(); + let file_path = dir.path().join("temp.rs"); + let mut file = File::create(file_path.clone()).unwrap(); + + writeln!(file, "{code}").unwrap(); + drop(file); + + Command::new("rustfmt") + .arg(file_path.to_str().unwrap()) + .spawn() + .unwrap() + .wait() + .unwrap(); + + let mut file = File::open(file_path).unwrap(); + let mut data = String::new(); + file.read_to_string(&mut data).unwrap(); + drop(file); + dir.close().unwrap(); + data + } + + #[test] + fn method_taking_str() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn from_str(s: &str) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn method_taking_slice() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn from_slice(s: &[f64]) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn method_taking_mutable_slice() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn fill_slice(s: &mut [f64]) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn mod_with_enum() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + enum Abc { + A, + B = 123, + } + + impl Abc { + pub fn do_something(&self) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn mod_with_writeable_result() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn to_string(&self, to: &mut DiplomatWriteable) -> Result<(), ()> { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn mod_with_rust_result() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + pub fn bar(&self) -> Result<(), ()> { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn multilevel_borrows() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + #[diplomat::opaque] + struct Foo<'a>(&'a str); + + #[diplomat::opaque] + struct Bar<'b, 'a: 'b>(&'b Foo<'a>); + + struct Baz<'x, 'y> { + foo: &'y Foo<'x>, + } + + impl<'a> Foo<'a> { + pub fn new(x: &'a str) -> Box<Foo<'a>> { + unimplemented!() + } + + pub fn get_bar<'b>(&'b self) -> Box<Bar<'b, 'a>> { + unimplemented!() + } + + pub fn get_baz<'b>(&'b self) -> Baz<'b, 'a> { + Bax { foo: self } + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn self_params() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + #[diplomat::opaque] + struct RefList<'a> { + data: &'a i32, + next: Option<Box<Self>>, + } + + impl<'b> RefList<'b> { + pub fn extend(&mut self, other: &Self) -> Self { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn cfged_method() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + impl Foo { + #[cfg(feature = "foo")] + pub fn bar(s: u8) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + struct Foo {} + + #[cfg(feature = "bar")] + impl Foo { + #[cfg(feature = "foo")] + pub fn bar(s: u8) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } + + #[test] + fn cfgd_struct() { + insta::assert_display_snapshot!(rustfmt_code( + &gen_bridge(parse_quote! { + mod ffi { + #[diplomat::opaque] + #[cfg(feature = "foo")] + struct Foo {} + #[cfg(feature = "foo")] + impl Foo { + pub fn bar(s: u8) { + unimplemented!() + } + } + } + }) + .to_token_stream() + .to_string() + )); + } +} diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap new file mode 100644 index 0000000000..4328309170 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap @@ -0,0 +1,23 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] #[cfg(feature = \"foo\")] struct Foo {}\n #[cfg(feature = \"foo\")] impl Foo\n { pub fn bar(s : u8) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[cfg(feature = "foo")] + struct Foo {} + #[cfg(feature = "foo")] + impl Foo { + pub fn bar(s: u8) { + unimplemented!() + } + } + #[no_mangle] + #[cfg(feature = "foo")] + extern "C" fn Foo_bar(s: u8) { + Foo::bar(s) + } + #[no_mangle] + #[cfg(feature = "foo")] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap new file mode 100644 index 0000000000..b026704b8d --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap @@ -0,0 +1,24 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} #[cfg(feature = \"bar\")] impl Foo\n {\n #[cfg(feature = \"foo\")] pub fn bar(s : u8)\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + #[cfg(feature = "bar")] + impl Foo { + #[cfg(feature = "foo")] + pub fn bar(s: u8) { + unimplemented!() + } + } + #[no_mangle] + #[cfg(feature = "foo")] + #[cfg(feature = "bar")] + extern "C" fn Foo_bar(s: u8) { + Foo::bar(s) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap new file mode 100644 index 0000000000..bc4adbb73f --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap @@ -0,0 +1,22 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n #[cfg(feature = \"foo\")] pub fn bar(s : u8)\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + #[cfg(feature = "foo")] + pub fn bar(s: u8) { + unimplemented!() + } + } + #[no_mangle] + #[cfg(feature = "foo")] + extern "C" fn Foo_bar(s: u8) { + Foo::bar(s) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap new file mode 100644 index 0000000000..01fea65c2e --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap @@ -0,0 +1,20 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn fill_slice(s : & mut [f64]) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn fill_slice(s: &mut [f64]) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_fill_slice(s_diplomat_data: *mut f64, s_diplomat_len: usize) { + Foo::fill_slice(unsafe { core::slice::from_raw_parts_mut(s_diplomat_data, s_diplomat_len) }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap new file mode 100644 index 0000000000..bebbcf8455 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap @@ -0,0 +1,26 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn make_uppercase(s : & mut str) { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn make_uppercase(s: &mut str) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_make_uppercase(s_diplomat_data: *mut u8, s_diplomat_len: usize) { + Foo::make_uppercase(unsafe { + core::str::from_utf8_mut(core::slice::from_raw_parts_mut( + s_diplomat_data, + s_diplomat_len, + )) + .unwrap() + }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap new file mode 100644 index 0000000000..94daec2af5 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap @@ -0,0 +1,20 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn from_slice(s : & [f64]) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn from_slice(s: &[f64]) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_from_slice(s_diplomat_data: *const f64, s_diplomat_len: usize) { + Foo::from_slice(unsafe { core::slice::from_raw_parts(s_diplomat_data, s_diplomat_len) }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap new file mode 100644 index 0000000000..7e736e5599 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap @@ -0,0 +1,23 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn from_str(s : & str) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn from_str(s: &str) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_from_str(s_diplomat_data: *const u8, s_diplomat_len: usize) { + Foo::from_str(unsafe { + core::str::from_utf8(core::slice::from_raw_parts(s_diplomat_data, s_diplomat_len)) + .unwrap() + }) + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap new file mode 100644 index 0000000000..723e17d786 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap @@ -0,0 +1,23 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n enum Abc { A, B = 123, } impl Abc\n { pub fn do_something(& self) { unimplemented! () } }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + enum Abc { + A, + B = 123, + } + impl Abc { + pub fn do_something(&self) { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Abc_do_something(this: &Abc) { + this.do_something() + } + #[no_mangle] + extern "C" fn Abc_destroy(this: Box<Abc>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap new file mode 100644 index 0000000000..1eeb28808a --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap @@ -0,0 +1,20 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn bar(& self) -> Result < (), () >\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn bar(&self) -> Result<(), ()> { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_bar(this: &Foo) -> diplomat_runtime::DiplomatResult<(), ()> { + this.bar().into() + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap new file mode 100644 index 0000000000..870193052f --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap @@ -0,0 +1,25 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn to_string(& self, to : & mut DiplomatWriteable) ->\n Result < (), () > { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + #[repr(C)] + struct Foo {} + impl Foo { + pub fn to_string(&self, to: &mut DiplomatWriteable) -> Result<(), ()> { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn Foo_to_string( + this: &Foo, + to: &mut diplomat_runtime::DiplomatWriteable, + ) -> diplomat_runtime::DiplomatResult<(), ()> { + let ret = this.to_string(to); + to.flush(); + ret.into() + } + #[no_mangle] + extern "C" fn Foo_destroy(this: Box<Foo>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap new file mode 100644 index 0000000000..f662608096 --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap @@ -0,0 +1,45 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] struct Foo < 'a > (& 'a str) ;\n #[diplomat :: opaque] struct Bar < 'b, 'a : 'b >\n (& 'b Foo < 'a >) ; struct Baz < 'x, 'y >\n { foo : & 'y Foo < 'x >, } impl < 'a > Foo < 'a >\n {\n pub fn new(x : & 'a str) -> Box < Foo < 'a >>\n { unimplemented! () } pub fn get_bar < 'b > (& 'b self) ->\n Box < Bar < 'b, 'a >> { unimplemented! () } pub fn get_baz <\n 'b > (& 'b self) -> Baz < 'b, 'a > { Bax { foo : self } }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + struct Foo<'a>(&'a str); + struct Bar<'b, 'a: 'b>(&'b Foo<'a>); + #[repr(C)] + struct Baz<'x, 'y> { + foo: &'y Foo<'x>, + } + impl<'a> Foo<'a> { + pub fn new(x: &'a str) -> Box<Foo<'a>> { + unimplemented!() + } + pub fn get_bar<'b>(&'b self) -> Box<Bar<'b, 'a>> { + unimplemented!() + } + pub fn get_baz<'b>(&'b self) -> Baz<'b, 'a> { + Bax { foo: self } + } + } + #[no_mangle] + extern "C" fn Bar_destroy<'b, 'a: 'b>(this: Box<Bar<'b, 'a>>) {} + #[no_mangle] + extern "C" fn Baz_destroy<'x: 'y, 'y>(this: Box<Baz<'x, 'y>>) {} + #[no_mangle] + extern "C" fn Foo_new<'a>(x_diplomat_data: *const u8, x_diplomat_len: usize) -> Box<Foo<'a>> { + Foo::new(unsafe { + core::str::from_utf8(core::slice::from_raw_parts(x_diplomat_data, x_diplomat_len)) + .unwrap() + }) + } + #[no_mangle] + extern "C" fn Foo_get_bar<'a: 'b, 'b>(this: &'b Foo<'a>) -> Box<Bar<'b, 'a>> { + this.get_bar() + } + #[no_mangle] + extern "C" fn Foo_get_baz<'a: 'b, 'b>(this: &'b Foo<'a>) -> Baz<'b, 'a> { + this.get_baz() + } + #[no_mangle] + extern "C" fn Foo_destroy<'a>(this: Box<Foo<'a>>) {} +} + diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap new file mode 100644 index 0000000000..6b931197ab --- /dev/null +++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap @@ -0,0 +1,22 @@ +--- +source: macro/src/lib.rs +expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] struct RefList < 'a >\n { data : & 'a i32, next : Option < Box < Self >>, } impl <\n 'b > RefList < 'b >\n {\n pub fn extend(& mut self, other : & Self) -> Self\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())" +--- +mod ffi { + struct RefList<'a> { + data: &'a i32, + next: Option<Box<Self>>, + } + impl<'b> RefList<'b> { + pub fn extend(&mut self, other: &Self) -> Self { + unimplemented!() + } + } + #[no_mangle] + extern "C" fn RefList_extend<'b>(this: &mut RefList<'b>, other: &RefList<'b>) -> RefList<'b> { + this.extend(other) + } + #[no_mangle] + extern "C" fn RefList_destroy<'a>(this: Box<RefList<'a>>) {} +} + diff --git a/third_party/rust/diplomat/src/transparent_convert.rs b/third_party/rust/diplomat/src/transparent_convert.rs new file mode 100644 index 0000000000..9ac8bf64e7 --- /dev/null +++ b/third_party/rust/diplomat/src/transparent_convert.rs @@ -0,0 +1,30 @@ +use quote::quote; +use syn::*; + +pub fn gen_transparent_convert(s: ItemStruct) -> proc_macro2::TokenStream { + let mut fields = s.fields.iter(); + let field1 = if let Some(field1) = fields.next() { + &field1.ty + } else { + panic!("#[diplomat::transparent_convert] only allowed on structs with a single field") + }; + + if fields.next().is_some() { + panic!("#[diplomat::transparent_convert] only allowed on structs with a single field") + } + let struct_name = &s.ident; + let (impl_generics, ty_generics, _) = s.generics.split_for_impl(); + let mut impl_generics: Generics = parse_quote!(#impl_generics); + let custom_lifetime: GenericParam = parse_quote!('transparent_convert_outer); + impl_generics.params.push(custom_lifetime); + quote! { + impl #impl_generics #struct_name #ty_generics { + // can potentially add transparent_convert_owned, _mut later + pub(crate) fn transparent_convert(from: &'transparent_convert_outer #field1) -> &'transparent_convert_outer Self { + unsafe { + &*(from as *const #field1 as *const Self) + } + } + } + } +} |