Skip to content

Bindings Updates for #681 and more Clones #749

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from 7 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
51 changes: 35 additions & 16 deletions c-bindings-gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
}
writeln_docs(w, &t.attrs, "");

let mut gen_types = GenericTypes::new();
assert!(gen_types.learn_generics(&t.generics, types));

writeln!(w, "#[repr(C)]\npub struct {} {{", trait_name).unwrap();
writeln!(w, "\tpub this_arg: *mut c_void,").unwrap();
let associated_types = learn_associated_types(t);
Expand All @@ -158,15 +161,17 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
match export_status(&m.attrs) {
ExportStatus::NoExport => {
// NoExport in this context means we'll hit an unimplemented!() at runtime,
// so add a comment noting that this needs to change in the output.
writeln!(w, "\t//XXX: Need to export {}", m.sig.ident).unwrap();
continue;
// so bail out.
unimplemented!();
},
ExportStatus::Export => {},
ExportStatus::TestOnly => continue,
}
if m.default.is_some() { unimplemented!(); }

gen_types.push_ctx();
assert!(gen_types.learn_generics(&m.sig.generics, types));

writeln_docs(w, &m.attrs, "\t");

if let syn::ReturnType::Type(_, rtype) = &m.sig.output {
Expand All @@ -183,7 +188,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
// called when the trait method is called which allows updating on the fly.
write!(w, "\tpub {}: ", m.sig.ident).unwrap();
generated_fields.push(format!("{}", m.sig.ident));
types.write_c_type(w, &*r.elem, None, false);
types.write_c_type(w, &*r.elem, Some(&gen_types), false);
writeln!(w, ",").unwrap();
writeln!(w, "\t/// Fill in the {} field as a reference to it will be given to Rust after this returns", m.sig.ident).unwrap();
writeln!(w, "\t/// Note that this takes a pointer to this object, not the this_ptr like other methods do").unwrap();
Expand All @@ -195,6 +200,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
// which does not compile since Thing is not defined before it is used.
writeln!(extra_headers, "struct LDK{};", trait_name).unwrap();
writeln!(extra_headers, "typedef struct LDK{} LDK{};", trait_name, trait_name).unwrap();
gen_types.pop_ctx();
continue;
}
// Sadly, this currently doesn't do what we want, but it should be easy to get
Expand All @@ -204,8 +210,10 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty

write!(w, "\tpub {}: extern \"C\" fn (", m.sig.ident).unwrap();
generated_fields.push(format!("{}", m.sig.ident));
write_method_params(w, &m.sig, &associated_types, "c_void", types, None, true, false);
write_method_params(w, &m.sig, &associated_types, "c_void", types, Some(&gen_types), true, false);
writeln!(w, ",").unwrap();

gen_types.pop_ctx();
},
&syn::TraitItem::Type(_) => {},
_ => unimplemented!(),
Expand Down Expand Up @@ -284,8 +292,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 +319,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 +348,27 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
writeln!(w, "if let Some(f) = self.set_{} {{", m.sig.ident).unwrap();
writeln!(w, "\t\t\t(f)(self);").unwrap();
write!(w, "\t\t}}\n\t\t").unwrap();
types.write_from_c_conversion_to_ref_prefix(w, &*r.elem, None);
types.write_from_c_conversion_to_ref_prefix(w, &*r.elem, Some(&gen_types));
write!(w, "self.{}", m.sig.ident).unwrap();
types.write_from_c_conversion_to_ref_suffix(w, &*r.elem, None);
types.write_from_c_conversion_to_ref_suffix(w, &*r.elem, Some(&gen_types));
writeln!(w, "\n\t}}").unwrap();
gen_types.pop_ctx();
continue;
}
}
write_method_var_decl_body(w, &m.sig, "\t", types, None, true);
write_method_var_decl_body(w, &m.sig, "\t", types, Some(&gen_types), true);
write!(w, "(self.{})(", m.sig.ident).unwrap();
write_method_call_params(w, &m.sig, &associated_types, "\t", types, None, "", true);
write_method_call_params(w, &m.sig, &associated_types, "\t", types, Some(&gen_types), "", true);

writeln!(w, "\n\t}}").unwrap();
gen_types.pop_ctx();
},
&syn::TraitItem::Type(ref t) => {
if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); }
let mut bounds_iter = t.bounds.iter();
match bounds_iter.next().unwrap() {
syn::TypeParamBound::Trait(tr) => {
writeln!(w, "\ttype {} = crate::{};", t.ident, types.resolve_path(&tr.path, None)).unwrap();
writeln!(w, "\ttype {} = crate::{};", t.ident, types.resolve_path(&tr.path, Some(&gen_types))).unwrap();
},
_ => unimplemented!(),
}
Expand Down Expand Up @@ -439,6 +450,10 @@ fn writeln_opaque<W: std::io::Write>(w: &mut W, ident: &syn::Ident, struct_name:
writeln!(w, "pub(crate) extern \"C\" fn {}_clone_void(this_ptr: *const c_void) -> *mut c_void {{", struct_name).unwrap();
writeln!(w, "\tBox::into_raw(Box::new(unsafe {{ (*(this_ptr as *mut native{})).clone() }})) as *mut c_void", struct_name).unwrap();
writeln!(w, "}}").unwrap();
writeln!(w, "#[no_mangle]").unwrap();
writeln!(w, "pub extern \"C\" fn {}_clone(orig: &{}) -> {} {{", struct_name, struct_name, struct_name).unwrap();
writeln!(w, "\t{} {{ inner: Box::into_raw(Box::new(unsafe {{ &*orig.inner }}.clone())), is_owned: true }}", struct_name).unwrap();
writeln!(w, "}}").unwrap();
break 'attr_loop;
}
}
Expand Down Expand Up @@ -980,6 +995,10 @@ fn writeln_enum<'a, 'b, W: std::io::Write>(w: &mut W, e: &'a syn::ItemEnum, type
if needs_free {
writeln!(w, "#[no_mangle]\npub extern \"C\" fn {}_free(this_ptr: {}) {{ }}", e.ident, e.ident).unwrap();
}
writeln!(w, "#[no_mangle]").unwrap();
writeln!(w, "pub extern \"C\" fn {}_clone(orig: &{}) -> {} {{", e.ident, e.ident, e.ident).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

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

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

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

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

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

pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics: impl Iterator<Item=&'b syn::GenericArgument>) {
pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator<Item=&'b syn::GenericArgument>) {
write!(w, "<").unwrap();
for (idx, arg) in generics.enumerate() {
if idx != 0 { write!(w, ", ").unwrap(); }
match arg {
syn::GenericArgument::Type(t) => self.write_rust_type(w, t),
syn::GenericArgument::Type(t) => self.write_rust_type(w, generics_resolver, t),
_ => unimplemented!(),
}
}
write!(w, ">").unwrap();
}
pub fn write_rust_type<W: std::io::Write>(&self, w: &mut W, t: &syn::Type) {
pub fn write_rust_type<W: std::io::Write>(&self, w: &mut W, generics: Option<&GenericTypes>, t: &syn::Type) {
match t {
syn::Type::Path(p) => {
if p.qself.is_some() || p.path.leading_colon.is_some() {
unimplemented!();
}
self.write_rust_path(w, &p.path);
self.write_rust_path(w, generics, &p.path);
},
syn::Type::Reference(r) => {
write!(w, "&").unwrap();
Expand All @@ -1014,11 +1029,11 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
if r.mutability.is_some() {
write!(w, "mut ").unwrap();
}
self.write_rust_type(w, &*r.elem);
self.write_rust_type(w, generics, &*r.elem);
},
syn::Type::Array(a) => {
write!(w, "[").unwrap();
self.write_rust_type(w, &a.elem);
self.write_rust_type(w, generics, &a.elem);
if let syn::Expr::Lit(l) = &a.len {
if let syn::Lit::Int(i) = &l.lit {
write!(w, "; {}]", i).unwrap();
Expand All @@ -1027,14 +1042,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
}
syn::Type::Slice(s) => {
write!(w, "[").unwrap();
self.write_rust_type(w, &s.elem);
self.write_rust_type(w, generics, &s.elem);
write!(w, "]").unwrap();
},
syn::Type::Tuple(s) => {
write!(w, "(").unwrap();
for (idx, t) in s.elems.iter().enumerate() {
if idx != 0 { write!(w, ", ").unwrap(); }
self.write_rust_type(w, &t);
self.write_rust_type(w, generics, &t);
}
write!(w, ")").unwrap();
},
Expand Down Expand Up @@ -1743,7 +1758,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
} else if in_crate {
write!(w, "{}", c_type).unwrap();
} else {
self.write_rust_type(w, &t);
self.write_rust_type(w, None, &t);
}
} else {
// If we just write out resolved_generic, it may mostly work, however for
Expand Down
12 changes: 6 additions & 6 deletions lightning-c-bindings/demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ struct NodeMonitors {
void ConnectBlock(const uint8_t (*header)[80], uint32_t height, LDKCVec_C2Tuple_usizeTransactionZZ tx_data, LDKBroadcasterInterface broadcast, LDKFeeEstimator fee_est) {
std::unique_lock<std::mutex> l(mut);
for (auto& mon : mons) {
LDK::CVec_C2Tuple_TxidCVec_TxOutZZZ res = ChannelMonitor_block_connected(&mon.second, &header_2, tx_data, height, broadcast, fee_est, *logger);
LDK::CVec_C2Tuple_TxidCVec_C2Tuple_u32TxOutZZZZ res = ChannelMonitor_block_connected(&mon.second, &header_2, tx_data, height, broadcast, fee_est, *logger);
}
}
};
Expand Down Expand Up @@ -159,7 +159,7 @@ LDKCResult_NoneChannelMonitorUpdateErrZ update_channel_monitor(const void *this_
LDKBroadcasterInterface broadcaster = {
.broadcast_transaction = broadcast_tx,
};
LDK::CResult_NoneMonitorUpdateErrorZ res = ChannelMonitor_update_monitor(&mon.second, update, &broadcaster, arg->logger);
LDK::CResult_NoneMonitorUpdateErrorZ res = ChannelMonitor_update_monitor(&mon.second, &update, &broadcaster, arg->logger);
assert(res->result_ok);
}
}
Expand Down Expand Up @@ -420,19 +420,19 @@ int main() {
}

LDKCVec_C2Tuple_usizeTransactionZZ txdata { .data = (LDKC2TupleTempl_usize__Transaction*)malloc(sizeof(LDKC2Tuple_usizeTransactionZ)), .datalen = 1 };
*txdata.data = C2Tuple_usizeTransactionZ_new(0, LDKTransaction { .data = channel_open_tx, .datalen = sizeof(channel_open_tx), .data_is_owned = false });
*txdata.data = C2Tuple_usizeTransactionZ_new(0, LDKTransaction { .data = (uint8_t*)channel_open_tx, .datalen = sizeof(channel_open_tx), .data_is_owned = false });
ChannelManager_block_connected(&cm1, &channel_open_header, txdata, 1);

txdata = LDKCVec_C2Tuple_usizeTransactionZZ { .data = (LDKC2TupleTempl_usize__Transaction*)malloc(sizeof(LDKC2Tuple_usizeTransactionZ)), .datalen = 1 };
*txdata.data = C2Tuple_usizeTransactionZ_new(0, LDKTransaction { .data = channel_open_tx, .datalen = sizeof(channel_open_tx), .data_is_owned = false });
*txdata.data = C2Tuple_usizeTransactionZ_new(0, LDKTransaction { .data = (uint8_t*)channel_open_tx, .datalen = sizeof(channel_open_tx), .data_is_owned = false });
ChannelManager_block_connected(&cm2, &channel_open_header, txdata, 1);

txdata = LDKCVec_C2Tuple_usizeTransactionZZ { .data = (LDKC2TupleTempl_usize__Transaction*)malloc(sizeof(LDKC2Tuple_usizeTransactionZ)), .datalen = 1 };
*txdata.data = C2Tuple_usizeTransactionZ_new(0, LDKTransaction { .data = channel_open_tx, .datalen = sizeof(channel_open_tx), .data_is_owned = false });
*txdata.data = C2Tuple_usizeTransactionZ_new(0, LDKTransaction { .data = (uint8_t*)channel_open_tx, .datalen = sizeof(channel_open_tx), .data_is_owned = false });
mons1.ConnectBlock(&channel_open_header, 1, txdata, broadcast, fee_est);

txdata = LDKCVec_C2Tuple_usizeTransactionZZ { .data = (LDKC2TupleTempl_usize__Transaction*)malloc(sizeof(LDKC2Tuple_usizeTransactionZ)), .datalen = 1 };
*txdata.data = C2Tuple_usizeTransactionZ_new(0, LDKTransaction { .data = channel_open_tx, .datalen = sizeof(channel_open_tx), .data_is_owned = false });
*txdata.data = C2Tuple_usizeTransactionZ_new(0, LDKTransaction { .data = (uint8_t*)channel_open_tx, .datalen = sizeof(channel_open_tx), .data_is_owned = false });
mons2.ConnectBlock(&channel_open_header, 1, txdata, broadcast, fee_est);

ChannelManager_block_connected(&cm1, &header_1, LDKCVec_C2Tuple_usizeTransactionZZ { .data = NULL, .datalen = 0 }, 2);
Expand Down
5 changes: 3 additions & 2 deletions lightning-c-bindings/src/c_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ impl Secp256k1Error {
/// set. Similarly, while it may change in the future, all `Transaction`s you pass to Rust may have
/// `data_is_owned` either set or unset at your discretion.
pub struct Transaction {
pub data: *const u8,
/// This is non-const for your convenience, an object passed to Rust is never written to.
pub data: *mut u8,
pub datalen: usize,
pub data_is_owned: bool,
}
Expand Down Expand Up @@ -319,7 +320,7 @@ impl<T: Clone> Clone for CVecTempl<T> {
fn clone(&self) -> Self {
let mut res = Vec::new();
if self.datalen == 0 { return Self::from(res); }
res.clone_from_slice(unsafe { std::slice::from_raw_parts_mut(self.data, self.datalen) });
res.extend_from_slice(unsafe { std::slice::from_raw_parts_mut(self.data, self.datalen) });
Self::from(res)
}
}
Expand Down