Support FromStr and Display impls, even if impl'd in a priv module
authorMatt Corallo <git@bluematt.me>
Mon, 19 Apr 2021 21:17:41 +0000 (17:17 -0400)
committerMatt Corallo <git@bluematt.me>
Thu, 29 Apr 2021 18:59:50 +0000 (18:59 +0000)
c-bindings-gen/src/main.rs
c-bindings-gen/src/types.rs
lightning-c-bindings/src/c_types/mod.rs

index faa51f5460163b759ada3d936855b7bca0ecbe16..a38e6e3750bdd8953a0d850c3a5c3ed42dfe66ad 100644 (file)
@@ -33,6 +33,8 @@ mod blocks;
 use types::*;
 use blocks::*;
 
+const DEFAULT_IMPORTS: &'static str = "\nuse std::str::FromStr;\nuse std::ffi::c_void;\nuse bitcoin::hashes::Hash;\nuse crate::c_types::*;\n";
+
 // *************************************
 // *** Manually-expanded conversions ***
 // *************************************
@@ -957,6 +959,30 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
                                                writeln!(w, "pub extern \"C\" fn {}_clone(orig: &{}) -> {} {{", ident, ident, ident).unwrap();
                                                writeln!(w, "\torig.clone()").unwrap();
                                                writeln!(w, "}}").unwrap();
+                                       } else if path_matches_nongeneric(&trait_path.1, &["FromStr"]) {
+                                               if let Some(container) = types.get_c_mangled_container_type(
+                                                               vec![&*i.self_ty, &syn::Type::Tuple(syn::TypeTuple { paren_token: Default::default(), elems: syn::punctuated::Punctuated::new() })],
+                                                               Some(&gen_types), "Result") {
+                                                       writeln!(w, "#[no_mangle]").unwrap();
+                                                       writeln!(w, "/// Read a {} object from a string", ident).unwrap();
+                                                       writeln!(w, "pub extern \"C\" fn {}_from_str(s: crate::c_types::Str) -> {} {{", ident, container).unwrap();
+                                                       writeln!(w, "\tmatch {}::from_str(s.into()) {{", resolved_path).unwrap();
+                                                       writeln!(w, "\t\tOk(r) => {{").unwrap();
+                                                       let new_var = types.write_to_c_conversion_new_var(w, &syn::Ident::new("r", Span::call_site()), &*i.self_ty, Some(&gen_types), false);
+                                                       write!(w, "\t\t\tcrate::c_types::CResultTempl::ok(\n\t\t\t\t").unwrap();
+                                                       types.write_to_c_conversion_inline_prefix(w, &*i.self_ty, Some(&gen_types), false);
+                                                       write!(w, "{}r", if new_var { "local_" } else { "" }).unwrap();
+                                                       types.write_to_c_conversion_inline_suffix(w, &*i.self_ty, Some(&gen_types), false);
+                                                       writeln!(w, "\n\t\t\t)\n\t\t}},").unwrap();
+                                                       writeln!(w, "\t\tErr(e) => crate::c_types::CResultTempl::err(0u8),").unwrap();
+                                                       writeln!(w, "\t}}.into()\n}}").unwrap();
+                                               }
+                                       } else if path_matches_nongeneric(&trait_path.1, &["Display"]) {
+                                               writeln!(w, "#[no_mangle]").unwrap();
+                                               writeln!(w, "/// Get the string representation of a {} object", ident).unwrap();
+                                               writeln!(w, "pub extern \"C\" fn {}_to_str(o: &{}) -> Str {{", ident, resolved_path).unwrap();
+                                               writeln!(w, "\tformat!(\"{{}}\", o).into()").unwrap();
+                                               writeln!(w, "}}").unwrap();
                                        } else {
                                                //XXX: implement for other things like ToString
                                                // If we have no generics, try a manual implementation:
@@ -1289,6 +1315,35 @@ fn writeln_fn<'a, 'b, W: std::io::Write>(w: &mut W, f: &'a syn::ItemFn, types: &
 // *** File/Crate Walking Logic ***
 // ********************************
 
+fn convert_priv_mod<'a, 'b: 'a, W: std::io::Write>(w: &mut W, libast: &'b FullLibraryAST, crate_types: &CrateTypes<'b>, out_dir: &str, mod_path: &str, module: &'b syn::ItemMod) {
+       // We want to ignore all items declared in this module (as they are not pub), but we still need
+       // to give the ImportResolver any use statements, so we copy them here.
+       let mut use_items = Vec::new();
+       for item in module.content.as_ref().unwrap().1.iter() {
+               if let syn::Item::Use(_) = item {
+                       use_items.push(item);
+               }
+       }
+       let import_resolver = ImportResolver::from_borrowed_items(mod_path.splitn(2, "::").next().unwrap(), &libast.dependencies, mod_path, &use_items);
+       let mut types = TypeResolver::new(mod_path, import_resolver, crate_types);
+
+       writeln!(w, "mod {} {{\n{}", module.ident, DEFAULT_IMPORTS).unwrap();
+       for item in module.content.as_ref().unwrap().1.iter() {
+               match item {
+                       syn::Item::Mod(m) => convert_priv_mod(w, libast, crate_types, out_dir, &format!("{}::{}", mod_path, module.ident), m),
+                       syn::Item::Impl(i) => {
+                               if let &syn::Type::Path(ref p) = &*i.self_ty {
+                                       if p.path.get_ident().is_some() {
+                                               writeln_impl(w, i, &mut types);
+                                       }
+                               }
+                       },
+                       _ => {},
+               }
+       }
+       writeln!(w, "}}").unwrap();
+}
+
 /// Do the Real Work of mapping an original file to C-callable wrappers. Creates a new file at
 /// `out_path` and fills it with wrapper structs/functions to allow calling the things in the AST
 /// at `module` from C.
@@ -1337,7 +1392,7 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &CrateTypes<'a>
                        writeln!(out, "pub mod c_types;").unwrap();
                        writeln!(out, "pub mod bitcoin;").unwrap();
                } else {
-                       writeln!(out, "\nuse std::ffi::c_void;\nuse bitcoin::hashes::Hash;\nuse crate::c_types::*;\n").unwrap();
+                       writeln!(out, "{}", DEFAULT_IMPORTS).unwrap();
                }
 
                for m in submods {
@@ -1371,7 +1426,9 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &CrateTypes<'a>
                                                writeln_trait(&mut out, &t, &mut type_resolver, header_file, cpp_header_file);
                                        }
                                },
-                               syn::Item::Mod(_) => {}, // We don't have to do anything - the top loop handles these.
+                               syn::Item::Mod(m) => {
+                                       convert_priv_mod(&mut out, libast, crate_types, out_dir, &format!("{}::{}", module, m.ident), m);
+                               },
                                syn::Item::Const(c) => {
                                        // Re-export any primitive-type constants.
                                        if let syn::Visibility::Public(_) = c.vis {
index e4f37c659ac4c949a83f32d0e8680471167aa931..f8ecc7ca843d275e53a33b2e30ebf3873f629151 100644 (file)
@@ -2351,6 +2351,13 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                }
                self.write_c_mangled_container_path_intern(w, args, generics, ident, is_ref, is_mut, ptr_for_ref, false)
        }
+       pub fn get_c_mangled_container_type(&self, args: Vec<&syn::Type>, generics: Option<&GenericTypes>, template_name: &str) -> Option<String> {
+               let mut out = Vec::new();
+               if !self.write_c_mangled_container_path(&mut out, args, generics, template_name, false, false, false) {
+                       return None;
+               }
+               Some(String::from_utf8(out).unwrap())
+       }
 
        // **********************************
        // *** C Type Equivalent Printing ***
index 2248ec2befbfec64e08fd43484cbac004c3e02e5..cd96dffe1969bbe6c845fcfa0ff2ec73e64dff4a 100644 (file)
@@ -338,18 +338,20 @@ pub(crate) fn deserialize_obj_arg<A, I: lightning::util::ser::ReadableArgs<A>>(s
 }
 
 #[repr(C)]
-#[derive(Copy, Clone)]
+#[derive(Clone)]
 /// A Rust str object, ie a reference to a UTF8-valid string.
 /// This is *not* null-terminated so cannot be used directly as a C string!
 pub struct Str {
        /// A pointer to the string's bytes, in UTF8 encoding
        pub chars: *const u8,
        /// The number of bytes (not characters!) pointed to by `chars`
-       pub len: usize
+       pub len: usize,
+       /// Whether the data pointed to by `chars` should be freed or not.
+       pub chars_is_owned: bool,
 }
 impl Into<Str> for &'static str {
        fn into(self) -> Str {
-               Str { chars: self.as_ptr(), len: self.len() }
+               Str { chars: self.as_ptr(), len: self.len(), chars_is_owned: false }
        }
 }
 impl Into<&'static str> for Str {
@@ -358,6 +360,23 @@ impl Into<&'static str> for Str {
                std::str::from_utf8(unsafe { std::slice::from_raw_parts(self.chars, self.len) }).unwrap()
        }
 }
+impl Into<Str> for String {
+       fn into(self) -> Str {
+               let s = Box::leak(self.into_boxed_str());
+               Str { chars: s.as_ptr(), len: s.len(), chars_is_owned: true }
+       }
+}
+
+impl Drop for Str {
+       fn drop(&mut self) {
+               if self.chars_is_owned && self.len != 0 {
+                       let _ = derived::CVec_u8Z { data: self.chars as *mut u8, datalen: self.len };
+               }
+       }
+}
+#[no_mangle]
+/// Frees the data buffer, if chars_is_owned is set and len > 0.
+pub extern "C" fn Str_free(_res: Str) { }
 
 // Note that the C++ headers memset(0) all the Templ types to avoid deallocation!
 // Thus, they must gracefully handle being completely null in _free.