Skip to content

Commit

Permalink
Merge pull request #749 from TheBlueMatt/2020-11-bindings-clones
Browse files Browse the repository at this point in the history
Bindings Updates for #681 and more Clones
  • Loading branch information
TheBlueMatt committed Nov 23, 2020
2 parents 8f10a1d + 9fe3124 commit 52673d4
Show file tree
Hide file tree
Showing 31 changed files with 1,001 additions and 233 deletions.
72 changes: 48 additions & 24 deletions c-bindings-gen/src/main.rs
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();
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
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
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

0 comments on commit 52673d4

Please sign in to comment.