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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
CollaboratorAuthor

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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
CollaboratorAuthor

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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
CollaboratorAuthor

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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
CollaboratorAuthor

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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
CollaboratorAuthor

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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
CollaboratorAuthor

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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
CollaboratorAuthor

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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
CollaboratorAuthor

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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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