Skip to content

Bindings Updates for #681 and more Clones #749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 48 additions & 24 deletions c-bindings-gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 {
Expand All @@ -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();
Expand All @@ -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
Expand All @@ -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!(),
Expand All @@ -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", _) => {
Expand All @@ -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<H: std::hash::Hasher>(&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!(); }
Expand All @@ -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 {
Expand All @@ -309,27 +324,26 @@ 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);
}
}
}
write!(w, ")").unwrap();
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!();
},
_ => {},
}
Expand All @@ -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, None)).unwrap();
writeln!(w, "\ttype {} = crate::{};", t.ident, types.resolve_path(&tr.path, Some(&gen_types))).unwrap();
},
_ => unimplemented!(),
}
Expand Down Expand Up @@ -439,6 +455,10 @@ fn writeln_opaque<W: std::io::Write>(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;
}
}
Expand Down Expand Up @@ -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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, it's fine that some pub(enum)s in RL don't implement Clone?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I mean I guess we should have them match, but its not an issue per se currently - if all the fields support Clone (as they would have to for the bindings crate to not fail to compile), then exposing a Clone to bindings seems fine.

writeln!(w, "\torig.clone()").unwrap();
writeln!(w, "}}").unwrap();
write_cpp_wrapper(cpp_headers, &format!("{}", e.ident), needs_free);
}

Expand Down
51 changes: 33 additions & 18 deletions c-bindings-gen/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,19 +932,34 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
// *** Original Rust Type Printing ***
// ***********************************

fn write_rust_path<W: std::io::Write>(&self, w: &mut W, path: &syn::Path) {
if let Some(resolved) = self.maybe_resolve_path(&path, None) {
fn in_rust_prelude(resolved_path: &str) -> bool {
match resolved_path {
"Vec" => true,
"Result" => true,
"Option" => true,
_ => false,
}
}

fn write_rust_path<W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, path: &syn::Path) {
if let Some(resolved) = self.maybe_resolve_path(&path, generics_resolver) {
if self.is_primitive(&resolved) {
write!(w, "{}", path.get_ident().unwrap()).unwrap();
} else {
if resolved.starts_with("ln::") || resolved.starts_with("chain::") || resolved.starts_with("util::") {
write!(w, "lightning::{}", resolved).unwrap();
// TODO: We should have a generic "is from a dependency" check here instead of
// checking for "bitcoin" explicitly.
if resolved.starts_with("bitcoin::") || Self::in_rust_prelude(&resolved) {
write!(w, "{}", resolved).unwrap();
// If we're printing a generic argument, it needs to reference the crate, otherwise
// the original crate:
} else if self.maybe_resolve_path(&path, None).as_ref() == Some(&resolved) {
write!(w, "{}::{}", self.orig_crate, resolved).unwrap();
} else {
write!(w, "{}", resolved).unwrap(); // XXX: Probably doens't work, get_ident().unwrap()
write!(w, "crate::{}", resolved).unwrap();
}
}
if let syn::PathArguments::AngleBracketed(args) = &path.segments.iter().last().unwrap().arguments {
self.write_rust_generic_arg(w, args.args.iter());
self.write_rust_generic_arg(w, generics_resolver, args.args.iter());
}
} else {
if path.leading_colon.is_some() {
Expand All @@ -954,12 +969,12 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
if idx != 0 { write!(w, "::").unwrap(); }
write!(w, "{}", seg.ident).unwrap();
if let syn::PathArguments::AngleBracketed(args) = &seg.arguments {
self.write_rust_generic_arg(w, args.args.iter());
self.write_rust_generic_arg(w, generics_resolver, args.args.iter());
}
}
}
}
pub fn write_rust_generic_param<'b, W: std::io::Write>(&self, w: &mut W, generics: impl Iterator<Item=&'b syn::GenericParam>) {
pub fn write_rust_generic_param<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator<Item=&'b syn::GenericParam>) {
let mut had_params = false;
for (idx, arg) in generics.enumerate() {
if idx != 0 { write!(w, ", ").unwrap(); } else { write!(w, "<").unwrap(); }
Expand All @@ -974,7 +989,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
match bound {
syn::TypeParamBound::Trait(tb) => {
if tb.paren_token.is_some() || tb.lifetimes.is_some() { unimplemented!(); }
self.write_rust_path(w, &tb.path);
self.write_rust_path(w, generics_resolver, &tb.path);
},
_ => unimplemented!(),
}
Expand All @@ -987,24 +1002,24 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
if had_params { write!(w, ">").unwrap(); }
}

pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics: impl Iterator<Item=&'b syn::GenericArgument>) {
pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator<Item=&'b syn::GenericArgument>) {
write!(w, "<").unwrap();
for (idx, arg) in generics.enumerate() {
if idx != 0 { write!(w, ", ").unwrap(); }
match arg {
syn::GenericArgument::Type(t) => self.write_rust_type(w, t),
syn::GenericArgument::Type(t) => self.write_rust_type(w, generics_resolver, t),
_ => unimplemented!(),
}
}
write!(w, ">").unwrap();
}
pub fn write_rust_type<W: std::io::Write>(&self, w: &mut W, t: &syn::Type) {
pub fn write_rust_type<W: std::io::Write>(&self, w: &mut W, generics: Option<&GenericTypes>, t: &syn::Type) {
match t {
syn::Type::Path(p) => {
if p.qself.is_some() || p.path.leading_colon.is_some() {
unimplemented!();
}
self.write_rust_path(w, &p.path);
self.write_rust_path(w, generics, &p.path);
},
syn::Type::Reference(r) => {
write!(w, "&").unwrap();
Expand All @@ -1014,11 +1029,11 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
if r.mutability.is_some() {
write!(w, "mut ").unwrap();
}
self.write_rust_type(w, &*r.elem);
self.write_rust_type(w, generics, &*r.elem);
},
syn::Type::Array(a) => {
write!(w, "[").unwrap();
self.write_rust_type(w, &a.elem);
self.write_rust_type(w, generics, &a.elem);
if let syn::Expr::Lit(l) = &a.len {
if let syn::Lit::Int(i) = &l.lit {
write!(w, "; {}]", i).unwrap();
Expand All @@ -1027,14 +1042,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
}
syn::Type::Slice(s) => {
write!(w, "[").unwrap();
self.write_rust_type(w, &s.elem);
self.write_rust_type(w, generics, &s.elem);
write!(w, "]").unwrap();
},
syn::Type::Tuple(s) => {
write!(w, "(").unwrap();
for (idx, t) in s.elems.iter().enumerate() {
if idx != 0 { write!(w, ", ").unwrap(); }
self.write_rust_type(w, &t);
self.write_rust_type(w, generics, &t);
}
write!(w, ")").unwrap();
},
Expand Down Expand Up @@ -1743,7 +1758,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
} else if in_crate {
write!(w, "{}", c_type).unwrap();
} else {
self.write_rust_type(w, &t);
self.write_rust_type(w, None, &t);
}
} else {
// If we just write out resolved_generic, it may mostly work, however for
Expand Down
4 changes: 2 additions & 2 deletions genbindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ else
fi

# Now build with LTO on on both C++ and rust, but without cross-language LTO:
cargo rustc -v --release -- -C lto
CARGO_PROFILE_RELEASE_LTO=true cargo rustc -v --release -- -C lto
clang++ -std=c++11 -Wall -flto -O2 -pthread demo.cpp target/release/libldk.a -ldl
echo "C++ Bin size and runtime with only RL (LTO) optimized:"
ls -lha a.out
Expand All @@ -179,7 +179,7 @@ if [ "$HOST_PLATFORM" != "host: x86_64-apple-darwin" -a "$CLANGPP" != "" ]; then
# or Ubuntu packages). This should work fine on Distros which do more involved
# packaging than simply shipping the rustup binaries (eg Debian should Just Work
# here).
cargo rustc -v --release -- -C linker-plugin-lto -C lto -C link-arg=-fuse-ld=lld
CARGO_PROFILE_RELEASE_LTO=true cargo rustc -v --release -- -C linker-plugin-lto -C lto -C link-arg=-fuse-ld=lld
$CLANGPP -Wall -std=c++11 -flto -fuse-ld=lld -O2 -pthread demo.cpp target/release/libldk.a -ldl
echo "C++ Bin size and runtime with cross-language LTO:"
ls -lha a.out
Expand Down
Loading