X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=c-bindings-gen%2Fsrc%2Fmain.rs;h=4193f6d36206c14bc47c3aa733b23b355f5e529e;hb=32abba7201f4045d31bc7c7df43683028989b4aa;hp=52ee3b05f16e887a09c272606d95394f7c90a15f;hpb=253af8dd617161b2bd79173f9da3ecc0b0991f7e;p=rust-lightning diff --git a/c-bindings-gen/src/main.rs b/c-bindings-gen/src/main.rs index 52ee3b05..4193f6d3 100644 --- a/c-bindings-gen/src/main.rs +++ b/c-bindings-gen/src/main.rs @@ -56,7 +56,7 @@ fn convert_macro(w: &mut W, macro_path: &syn::Path, stream: & /// Convert "impl trait_path for for_obj { .. }" for manually-mapped types (ie (de)serialization) fn maybe_convert_trait_impl(w: &mut W, trait_path: &syn::Path, for_obj: &syn::Ident, types: &TypeResolver) { - if let Some(t) = types.maybe_resolve_path(&trait_path) { + if let Some(t) = types.maybe_resolve_path(&trait_path, None) { let s = types.maybe_resolve_ident(for_obj).unwrap(); if !types.crate_types.opaques.get(&s).is_some() { return; } match &t as &str { @@ -97,7 +97,7 @@ macro_rules! walk_supertraits { ($t: expr, $types: expr, ($( $pat: pat => $e: ex $( $pat => $e, )* } } else { - let path = $types.resolve_path(&supertrait.path); + let path = $types.resolve_path(&supertrait.path, None); match (&path as &str, &supertrait.path.segments.iter().last().unwrap().ident) { $( $pat => $e, )* } @@ -148,6 +148,9 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty } writeln_docs(w, &t.attrs, ""); + let mut gen_types = GenericTypes::new(); + assert!(gen_types.learn_generics(&t.generics, types)); + writeln!(w, "#[repr(C)]\npub struct {} {{", trait_name).unwrap(); writeln!(w, "\tpub this_arg: *mut c_void,").unwrap(); let associated_types = learn_associated_types(t); @@ -158,15 +161,17 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty match export_status(&m.attrs) { ExportStatus::NoExport => { // NoExport in this context means we'll hit an unimplemented!() at runtime, - // so add a comment noting that this needs to change in the output. - writeln!(w, "\t//XXX: Need to export {}", m.sig.ident).unwrap(); - continue; + // so bail out. + unimplemented!(); }, ExportStatus::Export => {}, ExportStatus::TestOnly => continue, } if m.default.is_some() { unimplemented!(); } + gen_types.push_ctx(); + assert!(gen_types.learn_generics(&m.sig.generics, types)); + writeln_docs(w, &m.attrs, "\t"); if let syn::ReturnType::Type(_, rtype) = &m.sig.output { @@ -183,7 +188,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty // called when the trait method is called which allows updating on the fly. write!(w, "\tpub {}: ", m.sig.ident).unwrap(); generated_fields.push(format!("{}", m.sig.ident)); - types.write_c_type(w, &*r.elem, None, false); + types.write_c_type(w, &*r.elem, Some(&gen_types), false); writeln!(w, ",").unwrap(); writeln!(w, "\t/// Fill in the {} field as a reference to it will be given to Rust after this returns", m.sig.ident).unwrap(); writeln!(w, "\t/// Note that this takes a pointer to this object, not the this_ptr like other methods do").unwrap(); @@ -195,6 +200,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty // which does not compile since Thing is not defined before it is used. writeln!(extra_headers, "struct LDK{};", trait_name).unwrap(); writeln!(extra_headers, "typedef struct LDK{} LDK{};", trait_name, trait_name).unwrap(); + gen_types.pop_ctx(); continue; } // Sadly, this currently doesn't do what we want, but it should be easy to get @@ -204,8 +210,10 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty write!(w, "\tpub {}: extern \"C\" fn (", m.sig.ident).unwrap(); generated_fields.push(format!("{}", m.sig.ident)); - write_method_params(w, &m.sig, &associated_types, "c_void", types, None, true, false); + write_method_params(w, &m.sig, &associated_types, "c_void", types, Some(&gen_types), true, false); writeln!(w, ",").unwrap(); + + gen_types.pop_ctx(); }, &syn::TraitItem::Type(_) => {}, _ => unimplemented!(), @@ -218,7 +226,8 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty generated_fields.push("clone".to_owned()); }, ("std::cmp::Eq", _) => { - writeln!(w, "\tpub eq: extern \"C\" fn (this_arg: *const c_void, other_arg: *const c_void) -> bool,").unwrap(); + writeln!(w, "\tpub eq: extern \"C\" fn (this_arg: *const c_void, other_arg: &{}) -> bool,", trait_name).unwrap(); + writeln!(extra_headers, "typedef struct LDK{} LDK{};", trait_name, trait_name).unwrap(); generated_fields.push("eq".to_owned()); }, ("std::hash::Hash", _) => { @@ -243,21 +252,25 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty ("std::cmp::Eq", _) => { writeln!(w, "impl std::cmp::Eq for {} {{}}", trait_name).unwrap(); writeln!(w, "impl std::cmp::PartialEq for {} {{", trait_name).unwrap(); - writeln!(w, "\tfn eq(&self, o: &Self) -> bool {{ (self.eq)(self.this_arg, o.this_arg) }}\n}}").unwrap(); + writeln!(w, "\tfn eq(&self, o: &Self) -> bool {{ (self.eq)(self.this_arg, o) }}\n}}").unwrap(); }, ("std::hash::Hash", _) => { writeln!(w, "impl std::hash::Hash for {} {{", trait_name).unwrap(); writeln!(w, "\tfn hash(&self, hasher: &mut H) {{ hasher.write_u64((self.hash)(self.this_arg)) }}\n}}").unwrap(); }, ("Clone", _) => { - writeln!(w, "impl Clone for {} {{", trait_name).unwrap(); - writeln!(w, "\tfn clone(&self) -> Self {{").unwrap(); - writeln!(w, "\t\tSelf {{").unwrap(); - writeln!(w, "\t\tthis_arg: if let Some(f) = self.clone {{ (f)(self.this_arg) }} else {{ self.this_arg }},").unwrap(); + writeln!(w, "#[no_mangle]").unwrap(); + writeln!(w, "pub extern \"C\" fn {}_clone(orig: &{}) -> {} {{", trait_name, trait_name, trait_name).unwrap(); + writeln!(w, "\t{} {{", trait_name).unwrap(); + writeln!(w, "\t\tthis_arg: if let Some(f) = orig.clone {{ (f)(orig.this_arg) }} else {{ orig.this_arg }},").unwrap(); for field in generated_fields.iter() { - writeln!(w, "\t\t\t{}: self.{}.clone(),", field, field).unwrap(); + writeln!(w, "\t\t{}: orig.{}.clone(),", field, field).unwrap(); } - writeln!(w, "\t\t}}\n\t}}\n}}").unwrap(); + writeln!(w, "\t}}\n}}").unwrap(); + writeln!(w, "impl Clone for {} {{", trait_name).unwrap(); + writeln!(w, "\tfn clone(&self) -> Self {{").unwrap(); + writeln!(w, "\t\t{}_clone(self)", trait_name).unwrap(); + writeln!(w, "\t}}\n}}").unwrap(); }, (s, i) => { if s != "util::events::MessageSendEventsProvider" { unimplemented!(); } @@ -284,8 +297,10 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty m.sig.abi.is_some() || m.sig.variadic.is_some() { unimplemented!(); } + gen_types.push_ctx(); + assert!(gen_types.learn_generics(&m.sig.generics, types)); write!(w, "\tfn {}", m.sig.ident).unwrap(); - types.write_rust_generic_param(w, m.sig.generics.params.iter()); + types.write_rust_generic_param(w, Some(&gen_types), m.sig.generics.params.iter()); write!(w, "(").unwrap(); for inp in m.sig.inputs.iter() { match inp { @@ -309,11 +324,11 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty ident.mutability.is_some() || ident.subpat.is_some() { unimplemented!(); } - write!(w, ", {}{}: ", if types.skip_arg(&*arg.ty, None) { "_" } else { "" }, ident.ident).unwrap(); + write!(w, ", {}{}: ", if types.skip_arg(&*arg.ty, Some(&gen_types)) { "_" } else { "" }, ident.ident).unwrap(); } _ => unimplemented!(), } - types.write_rust_type(w, &*arg.ty); + types.write_rust_type(w, Some(&gen_types), &*arg.ty); } } } @@ -321,15 +336,14 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty match &m.sig.output { syn::ReturnType::Type(_, rtype) => { write!(w, " -> ").unwrap(); - types.write_rust_type(w, &*rtype) + types.write_rust_type(w, Some(&gen_types), &*rtype) }, _ => {}, } write!(w, " {{\n\t\t").unwrap(); match export_status(&m.attrs) { ExportStatus::NoExport => { - writeln!(w, "unimplemented!();\n\t}}").unwrap(); - continue; + unimplemented!(); }, _ => {}, } @@ -339,25 +353,27 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty writeln!(w, "if let Some(f) = self.set_{} {{", m.sig.ident).unwrap(); writeln!(w, "\t\t\t(f)(self);").unwrap(); write!(w, "\t\t}}\n\t\t").unwrap(); - types.write_from_c_conversion_to_ref_prefix(w, &*r.elem, None); + types.write_from_c_conversion_to_ref_prefix(w, &*r.elem, Some(&gen_types)); write!(w, "self.{}", m.sig.ident).unwrap(); - types.write_from_c_conversion_to_ref_suffix(w, &*r.elem, None); + types.write_from_c_conversion_to_ref_suffix(w, &*r.elem, Some(&gen_types)); writeln!(w, "\n\t}}").unwrap(); + gen_types.pop_ctx(); continue; } } - write_method_var_decl_body(w, &m.sig, "\t", types, None, true); + write_method_var_decl_body(w, &m.sig, "\t", types, Some(&gen_types), true); write!(w, "(self.{})(", m.sig.ident).unwrap(); - write_method_call_params(w, &m.sig, &associated_types, "\t", types, None, "", true); + write_method_call_params(w, &m.sig, &associated_types, "\t", types, Some(&gen_types), "", true); writeln!(w, "\n\t}}").unwrap(); + gen_types.pop_ctx(); }, &syn::TraitItem::Type(ref t) => { if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); } let mut bounds_iter = t.bounds.iter(); match bounds_iter.next().unwrap() { syn::TypeParamBound::Trait(tr) => { - writeln!(w, "\ttype {} = crate::{};", t.ident, types.resolve_path(&tr.path)).unwrap(); + writeln!(w, "\ttype {} = crate::{};", t.ident, types.resolve_path(&tr.path, Some(&gen_types))).unwrap(); }, _ => unimplemented!(), } @@ -397,7 +413,7 @@ fn writeln_opaque(w: &mut W, ident: &syn::Ident, struct_name: writeln!(w, ";\n").unwrap(); writeln!(extra_headers, "struct native{}Opaque;\ntypedef struct native{}Opaque LDKnative{};", ident, ident, ident).unwrap(); writeln_docs(w, &attrs, ""); - writeln!(w, "#[must_use]\n#[repr(C)]\npub struct {} {{\n\t/// Nearly everyhwere, inner must be non-null, however in places where", struct_name).unwrap(); + writeln!(w, "#[must_use]\n#[repr(C)]\npub struct {} {{\n\t/// Nearly everywhere, inner must be non-null, however in places where", struct_name).unwrap(); writeln!(w, "\t/// the Rust equivalent takes an Option, it may be set to null to indicate None.").unwrap(); writeln!(w, "\tpub inner: *mut native{},\n\tpub is_owned: bool,\n}}\n", ident).unwrap(); writeln!(w, "impl Drop for {} {{\n\tfn drop(&mut self) {{", struct_name).unwrap(); @@ -439,6 +455,10 @@ fn writeln_opaque(w: &mut W, ident: &syn::Ident, struct_name: writeln!(w, "pub(crate) extern \"C\" fn {}_clone_void(this_ptr: *const c_void) -> *mut c_void {{", struct_name).unwrap(); writeln!(w, "\tBox::into_raw(Box::new(unsafe {{ (*(this_ptr as *mut native{})).clone() }})) as *mut c_void", struct_name).unwrap(); writeln!(w, "}}").unwrap(); + writeln!(w, "#[no_mangle]").unwrap(); + writeln!(w, "pub extern \"C\" fn {}_clone(orig: &{}) -> {} {{", struct_name, struct_name, struct_name).unwrap(); + writeln!(w, "\t{} {{ inner: Box::into_raw(Box::new(unsafe {{ &*orig.inner }}.clone())), is_owned: true }}", struct_name).unwrap(); + writeln!(w, "}}").unwrap(); break 'attr_loop; } } @@ -579,7 +599,7 @@ fn writeln_impl(w: &mut W, i: &syn::ItemImpl, types: &mut Typ if let Some(trait_path) = i.trait_.as_ref() { if trait_path.0.is_some() { unimplemented!(); } if types.understood_c_path(&trait_path.1) { - let full_trait_path = types.resolve_path(&trait_path.1); + let full_trait_path = types.resolve_path(&trait_path.1, None); let trait_obj = *types.crate_types.traits.get(&full_trait_path).unwrap(); // We learn the associated types maping from the original trait object. // That's great, except that they are unresolved idents, so if we learn @@ -628,7 +648,7 @@ fn writeln_impl(w: &mut W, i: &syn::ItemImpl, types: &mut Typ if let syn::ReturnType::Type(_, rtype) = &$m.sig.output { if let syn::Type::Reference(r) = &**rtype { write!(w, "\n\t\t{}{}: ", $indent, $m.sig.ident).unwrap(); - types.write_empty_rust_val(w, &*r.elem); + types.write_empty_rust_val(Some(&gen_types), w, &*r.elem); writeln!(w, ",\n{}\t\tset_{}: Some({}_{}_set_{}),", $indent, $m.sig.ident, ident, trait_obj.ident, $m.sig.ident).unwrap(); printed = true; } @@ -722,7 +742,7 @@ fn writeln_impl(w: &mut W, i: &syn::ItemImpl, types: &mut Typ writeln!(w, "\t// This is a bit race-y in the general case, but for our specific use-cases today, we're safe").unwrap(); writeln!(w, "\t// Specifically, we must ensure that the first time we're called it can never be in parallel").unwrap(); write!(w, "\tif ").unwrap(); - types.write_empty_rust_val_check(w, &*r.elem, &format!("trait_self_arg.{}", $m.sig.ident)); + types.write_empty_rust_val_check(Some(&gen_types), w, &*r.elem, &format!("trait_self_arg.{}", $m.sig.ident)); writeln!(w, " {{").unwrap(); writeln!(w, "\t\tunsafe {{ &mut *(trait_self_arg as *const {} as *mut {}) }}.{} = {}_{}_{}(trait_self_arg.this_arg);", trait_obj.ident, trait_obj.ident, $m.sig.ident, ident, trait_obj.ident, $m.sig.ident).unwrap(); writeln!(w, "\t}}").unwrap(); @@ -980,6 +1000,10 @@ fn writeln_enum<'a, 'b, W: std::io::Write>(w: &mut W, e: &'a syn::ItemEnum, type if needs_free { writeln!(w, "#[no_mangle]\npub extern \"C\" fn {}_free(this_ptr: {}) {{ }}", e.ident, e.ident).unwrap(); } + writeln!(w, "#[no_mangle]").unwrap(); + writeln!(w, "pub extern \"C\" fn {}_clone(orig: &{}) -> {} {{", e.ident, e.ident, e.ident).unwrap(); + writeln!(w, "\torig.clone()").unwrap(); + writeln!(w, "}}").unwrap(); write_cpp_wrapper(cpp_headers, &format!("{}", e.ident), needs_free); } @@ -1057,8 +1081,6 @@ struct FullLibraryAST { /// `out_path` and fills it with wrapper structs/functions to allow calling the things in the AST /// at `module` from C. fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes<'a>, in_dir: &str, out_dir: &str, path: &str, orig_crate: &str, module: &str, header_file: &mut File, cpp_header_file: &mut File) { - eprintln!("Converting {}...", path); - let syntax = if let Some(ast) = libast.files.get(module) { ast } else { return }; assert!(syntax.shebang.is_none()); // Not sure what this is, hope we dont have one @@ -1096,6 +1118,8 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes orig_crate, &new_mod, header_file, cpp_header_file); } + eprintln!("Converting {} entries...", path); + let mut type_resolver = TypeResolver::new(orig_crate, module, crate_types); for item in syntax.items.iter() { @@ -1125,7 +1149,7 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes // Re-export any primitive-type constants. if let syn::Visibility::Public(_) = c.vis { if let syn::Type::Path(p) = &*c.ty { - let resolved_path = type_resolver.resolve_path(&p.path); + let resolved_path = type_resolver.resolve_path(&p.path, None); if type_resolver.is_primitive(&resolved_path) { writeln!(out, "\n#[no_mangle]").unwrap(); writeln!(out, "pub static {}: {} = {}::{}::{};", c.ident, resolved_path, orig_crate, module, c.ident).unwrap(); @@ -1139,8 +1163,18 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes ExportStatus::Export => {}, ExportStatus::NoExport|ExportStatus::TestOnly => continue, } - if t.generics.lt_token.is_none() { - writeln_opaque(&mut out, &t.ident, &format!("{}", t.ident), &t.generics, &t.attrs, &type_resolver, header_file, cpp_header_file); + + let mut process_alias = true; + for tok in t.generics.params.iter() { + if let syn::GenericParam::Lifetime(_) = tok {} + else { process_alias = false; } + } + if process_alias { + match &*t.ty { + syn::Type::Path(_) => + writeln_opaque(&mut out, &t.ident, &format!("{}", t.ident), &t.generics, &t.attrs, &type_resolver, header_file, cpp_header_file), + _ => {} + } } } }, @@ -1180,6 +1214,52 @@ fn load_ast(in_dir: &str, path: &str, module: String, ast_storage: &mut FullLibr ast_storage.files.insert(module, syntax); } +/// Insert ident -> absolute Path resolutions into imports from the given UseTree and path-prefix. +fn process_use_intern<'a>(u: &'a syn::UseTree, mut path: syn::punctuated::Punctuated, imports: &mut HashMap<&'a syn::Ident, syn::Path>) { + match u { + syn::UseTree::Path(p) => { + path.push(syn::PathSegment { ident: p.ident.clone(), arguments: syn::PathArguments::None }); + process_use_intern(&p.tree, path, imports); + }, + syn::UseTree::Name(n) => { + path.push(syn::PathSegment { ident: n.ident.clone(), arguments: syn::PathArguments::None }); + imports.insert(&n.ident, syn::Path { leading_colon: Some(syn::Token![::](Span::call_site())), segments: path }); + }, + syn::UseTree::Group(g) => { + for i in g.items.iter() { + process_use_intern(i, path.clone(), imports); + } + }, + _ => {} + } +} + +/// Map all the Paths in a Type into absolute paths given a set of imports (generated via process_use_intern) +fn resolve_imported_refs(imports: &HashMap<&syn::Ident, syn::Path>, mut ty: syn::Type) -> syn::Type { + match &mut ty { + syn::Type::Path(p) => { + if let Some(ident) = p.path.get_ident() { + if let Some(newpath) = imports.get(ident) { + p.path = newpath.clone(); + } + } else { unimplemented!(); } + }, + syn::Type::Reference(r) => { + r.elem = Box::new(resolve_imported_refs(imports, (*r.elem).clone())); + }, + syn::Type::Slice(s) => { + s.elem = Box::new(resolve_imported_refs(imports, (*s.elem).clone())); + }, + syn::Type::Tuple(t) => { + for e in t.elems.iter_mut() { + *e = resolve_imported_refs(imports, e.clone()); + } + }, + _ => unimplemented!(), + } + ty +} + /// Walk the FullLibraryAST, deciding how things will be mapped and adding tracking to CrateTypes. fn walk_ast<'a>(in_dir: &str, path: &str, module: String, ast_storage: &'a FullLibraryAST, crate_types: &mut CrateTypes<'a>) { let syntax = if let Some(ast) = ast_storage.files.get(&module) { ast } else { return }; @@ -1189,8 +1269,13 @@ fn walk_ast<'a>(in_dir: &str, path: &str, module: String, ast_storage: &'a FullL walk_ast(in_dir, &path, new_mod, ast_storage, crate_types); } + let mut import_maps = HashMap::new(); + for item in syntax.items.iter() { match item { + syn::Item::Use(u) => { + process_use_intern(&u.tree, syn::punctuated::Punctuated::new(), &mut import_maps); + }, syn::Item::Struct(s) => { if let syn::Visibility::Public(_) = s.vis { match export_status(&s.attrs) { @@ -1211,6 +1296,31 @@ fn walk_ast<'a>(in_dir: &str, path: &str, module: String, ast_storage: &'a FullL crate_types.traits.insert(trait_path, &t); } }, + syn::Item::Type(t) => { + if let syn::Visibility::Public(_) = t.vis { + match export_status(&t.attrs) { + ExportStatus::Export => {}, + ExportStatus::NoExport|ExportStatus::TestOnly => continue, + } + let type_path = format!("{}::{}", module, t.ident); + let mut process_alias = true; + for tok in t.generics.params.iter() { + if let syn::GenericParam::Lifetime(_) = tok {} + else { process_alias = false; } + } + if process_alias { + match &*t.ty { + syn::Type::Path(_) => { + // If its a path with no generics, assume we don't map the aliased type and map it opaque + crate_types.opaques.insert(type_path, &t.ident); + }, + _ => { + crate_types.type_aliases.insert(type_path, resolve_imported_refs(&import_maps, (*t.ty).clone())); + } + } + } + } + }, syn::Item::Enum(e) if is_enum_opaque(e) => { if let syn::Visibility::Public(_) = e.vis { match export_status(&e.attrs) { @@ -1264,7 +1374,7 @@ fn main() { // ...then walk the ASTs tracking what types we will map, and how, so that we can resolve them // when parsing other file ASTs... let mut libtypes = CrateTypes { traits: HashMap::new(), opaques: HashMap::new(), mirrored_enums: HashMap::new(), - templates_defined: HashMap::new(), template_file: &mut derived_templates }; + type_aliases: HashMap::new(), templates_defined: HashMap::default(), template_file: &mut derived_templates }; walk_ast(&args[1], "/lib.rs", "".to_string(), &libast, &mut libtypes); // ... finally, do the actual file conversion/mapping, writing out types as we go.