X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=csharp_strings.py;h=a73ee2035d310be2568fa48f71b94ab9994ea3e4;hb=HEAD;hp=51cdaa97ac6c0098873714bb897fbaa1960b734c;hpb=3b30b992dbf03b6a38320cc16699849840e7b542;p=ldk-java diff --git a/csharp_strings.py b/csharp_strings.py index 51cdaa97..3e5e247d 100644 --- a/csharp_strings.py +++ b/csharp_strings.py @@ -3,7 +3,9 @@ from enum import Enum import sys class Target(Enum): - CSHARP = 1, + WINDOWS = 1, + LINUX = 2, + PTHREAD = 3, def first_to_lower(string: str) -> str: first = string[0] @@ -16,20 +18,23 @@ def arg_name_repl(s, arg_name): return s.replace(arg_name, "_" + arg_name) if arg_name == "lock" or arg_name == "event" or arg_name == "params" else s class Consts: - def __init__(self, DEBUG: bool, target: Target, **kwargs): + def __init__(self, DEBUG: bool, target: Target, outdir: str, **kwargs): + self.outdir = outdir self.target = target self.c_array_class_caches = set() + self.function_ptr_counter = 0 + self.function_ptrs = {} self.c_type_map = dict( - bool = ['bool'], - uint8_t = ['byte'], - uint16_t = ['short'], - uint32_t = ['int'], - uint64_t = ['long'], - int64_t = ['long'], - double = ['double'], + bool = ['bool', 'long', 'bool[]'], + uint8_t = ['byte', 'long', 'byte[]'], + uint16_t = ['short', 'long', 'short[]'], + uint32_t = ['int', 'long', 'int[]'], + uint64_t = ['long', 'long', 'long[]'], + int64_t = ['long', 'long', 'long[]'], + double = ['double', 'long', 'double[]'], ) self.java_type_map = dict( - String = "string" + String = "long" ) self.java_hu_type_map = dict( String = "string" @@ -44,55 +49,20 @@ class Consts: using org.ldk.enums; using org.ldk.impl; using System; +using System.Collections.Generic; using System.Runtime.InteropServices; namespace org { namespace ldk { namespace impl { internal class bindings { - internal class ArrayCoder : ICustomMarshaler { - int size = 0; - GCHandle pinnedArray; - public static ICustomMarshaler GetInstance(string pstrCookie) { - return new ArrayCoder(); - } - - public Object MarshalNativeToManaged(IntPtr pNativeData) { throw new NotImplementedException(); } - public IntPtr MarshalManagedToNative(Object obj) { - if (obj.GetType() == typeof(byte[])) { - byte[] inp = (byte[])obj; - IntPtr data = Marshal.AllocHGlobal(inp.Length + 8); - Marshal.WriteInt64(data, inp.Length); - Marshal.Copy(inp, 0, data + 8, inp.Length); - this.size = inp.Length + 8; - return data; - } else { - throw new NotImplementedException(); - } - } - public void CleanUpNativeData(IntPtr pNativeData) { - Marshal.FreeHGlobal(pNativeData); - } - public void CleanUpManagedData(Object ManagedObj) { } - public int GetNativeDataSize() { - // Blindly guess based on the last allocation, no idea how else to implement this. - return this.size; - } - } + static List js_objs = new List(); - /*static { - init(java.lang.Enum.class, VecOrSliceDef.class); - init_class_cache(); - if (!get_lib_version_string().equals(version.get_ldk_java_bindings_version())) - throw new ArgumentException("Compiled LDK library and LDK class failes do not match"); - // Fetching the LDK versions from C also checks that the header and binaries match - Console.Error.WriteLine("Loaded LDK-Java Bindings " + version.get_ldk_java_bindings_version() + " with LDK " + get_ldk_version() + " and LDK-C-Bindings " + get_ldk_c_bindings_version()); - }*/ - //static extern void init(java.lang.Class c); - //static native void init_class_cache(); """ self.bindings_header += self.native_meth_decl("get_lib_version_string", "string") + "();\n" self.bindings_header += self.native_meth_decl("get_ldk_c_bindings_version", "string") + "();\n" self.bindings_header += self.native_meth_decl("get_ldk_version", "string") + "();\n\n" + self.bindings_header += self.native_meth_decl("allocate_buffer", "long") + "(long buflen);\n\n" + self.bindings_header += self.native_meth_decl("free_buffer", "void") + "(long buf);\n\n" self.bindings_version_file = """ @@ -135,14 +105,14 @@ public class CommonBase { public readonly int previous_vout; internal TxIn(object _dummy, long ptr) : base(ptr) { - this.witness = bindings.TxIn_get_witness(ptr); - this.script_sig = bindings.TxIn_get_script_sig(ptr); + this.witness = InternalUtils.decodeUint8Array(bindings.TxIn_get_witness(ptr)); + this.script_sig = InternalUtils.decodeUint8Array(bindings.TxIn_get_script_sig(ptr)); this.sequence = bindings.TxIn_get_sequence(ptr); - this.previous_txid = bindings.TxIn_get_previous_txid(ptr); + this.previous_txid = InternalUtils.decodeUint8Array(bindings.TxIn_get_previous_txid(ptr)); this.previous_vout = bindings.TxIn_get_previous_vout(ptr); } public TxIn(byte[] witness, byte[] script_sig, int sequence, byte[] previous_txid, int previous_vout) - : this(null, bindings.TxIn_new(witness, script_sig, sequence, previous_txid, previous_vout)) {} + : this(null, bindings.TxIn_new(InternalUtils.encodeUint8Array(witness), InternalUtils.encodeUint8Array(script_sig), sequence, InternalUtils.encodeUint8Array(previous_txid), previous_vout)) {} ~TxIn() { if (ptr != 0) { bindings.TxIn_free(ptr); } @@ -156,10 +126,10 @@ public class CommonBase { public readonly long value; internal TxOut(object _dummy, long ptr) : base(ptr) { - this.script_pubkey = bindings.TxOut_get_script_pubkey(ptr); + this.script_pubkey = InternalUtils.decodeUint8Array(bindings.TxOut_get_script_pubkey(ptr)); this.value = bindings.TxOut_get_value(ptr); } - public TxOut(long value, byte[] script_pubkey) : this(null, bindings.TxOut_new(script_pubkey, value)) {} + public TxOut(long value, byte[] script_pubkey) : this(null, bindings.TxOut_new(InternalUtils.encodeUint8Array(script_pubkey), value)) {} ~TxOut() { if (ptr != 0) { bindings.TxOut_free(ptr); } @@ -171,10 +141,10 @@ public class CommonBase { public readonly byte[] scalar_bytes; internal BigEndianScalar(object _dummy, long ptr) : base(ptr) { - this.scalar_bytes = bindings.BigEndianScalar_get_bytes(ptr); + this.scalar_bytes = InternalUtils.decodeUint8Array(bindings.BigEndianScalar_get_bytes(ptr)); } - public BigEndianScalar(byte[] scalar_bytes) : base(bindings.BigEndianScalar_new(scalar_bytes)) { - this.scalar_bytes = bindings.BigEndianScalar_get_bytes(ptr); + public BigEndianScalar(byte[] scalar_bytes) : base(bindings.BigEndianScalar_new(InternalUtils.encodeUint8Array(scalar_bytes))) { + this.scalar_bytes = InternalUtils.decodeUint8Array(bindings.BigEndianScalar_get_bytes(ptr)); } ~BigEndianScalar() { @@ -182,6 +152,28 @@ public class CommonBase { } }""" + self.witness_program_defn = """public class WitnessProgram : CommonBase { + /** The witness program bytes themselves */ + public readonly byte[] program; + /** The witness version */ + public readonly WitnessVersion version; + + internal WitnessProgram(object _dummy, long ptr) : base(ptr) { + this.program = InternalUtils.decodeUint8Array(bindings.WitnessProgram_get_program(ptr)); + this.version = new WitnessVersion(bindings.WitnessProgram_get_version(ptr)); + } + static private long check_args(byte[] program, WitnessVersion version) { + if (program.Length < 2 || program.Length > 40) throw new ArgumentException(); + if (version.getVal() == 0 && program.Length != 20 && program.Length != 32) throw new ArgumentException(); + return InternalUtils.encodeUint8Array(program); + } + public WitnessProgram(byte[] program, WitnessVersion version) : + this(null, bindings.WitnessProgram_new(version.getVal(), check_args(program, version))) {} + + ~WitnessProgram() { + if (ptr != 0) { bindings.WitnessProgram_free(ptr); } + } +}""" self.c_file_pfx = """ // On OSX jlong (ie long long) is not equivalent to int64_t, so we override here @@ -198,7 +190,25 @@ public class CommonBase { self.c_file_pfx = self.c_file_pfx + "#include \n#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)\n" - if not DEBUG or sys.platform == "darwin": + if self.target == Target.WINDOWS: + self.c_file_pfx = self.c_file_pfx + """#include +static HANDLE process_heap = NULL; +static inline void* init_heap() { + if (UNLIKELY(process_heap == NULL)) { + // Assume pointer writes wont tear, which is true where we need it. + process_heap = GetProcessHeap(); + } +} +static inline void* MALLOC(size_t a, const char* _) { + init_heap(); + return HeapAlloc(process_heap, HEAP_ZERO_MEMORY, a); +} +#define do_MALLOC(a, b, _c) MALLOC(a, b) +#define FREE(p) if ((uint64_t)(p) > 4096) { init_heap(); HeapFree(process_heap, 0, p); } +#define CHECK_ACCESS(p) +#define CHECK_INNER_FIELD_ACCESS_OR_NULL(v) +""" + elif not DEBUG or self.target != Target.LINUX: self.c_file_pfx = self.c_file_pfx + """#define do_MALLOC(a, _b, _c) malloc(a) #define MALLOC(a, _) malloc(a) #define FREE(p) if ((uint64_t)(p) > 4096) { free(p); } @@ -224,7 +234,7 @@ void __attribute__((constructor)) debug_log_version() { } """ - if sys.platform != "darwin": + if self.target == Target.LINUX: self.c_file_pfx += """ // Running a leak check across all the allocations and frees of the JDK is a mess, // so instead we implement our own naive leak checker here, relying on the -wrap @@ -359,7 +369,7 @@ void __attribute__((destructor)) check_leaks() { unsigned long alloc_count = 0; unsigned long alloc_size = 0; DEBUG_PRINT("The following LDK-allocated blocks still remain.\\n"); - DEBUG_PRINT("Note that this is only accurate if System.gc(); System.runFinalization()\\n"); + DEBUG_PRINT("Note that this is only accurate if System.GC.Collect(); GC.WaitForPendingFinalizers();\\n"); DEBUG_PRINT("was called prior to exit after all LDK objects were out of scope.\\n"); for (allocation* a = allocation_ll; a != NULL; a = a->next) { DEBUG_PRINT("%s %p (%lu bytes) remains:\\n", a->struct_name, a->ptr, a->alloc_len); @@ -369,7 +379,7 @@ void __attribute__((destructor)) check_leaks() { alloc_size += a->alloc_len; } DEBUG_PRINT("%lu allocations remained for %lu bytes.\\n", alloc_count, alloc_size); - DEBUG_PRINT("Note that this is only accurate if System.gc(); System.runFinalization()\\n"); + DEBUG_PRINT("Note that this is only accurate if System.GC.Collect(); GC.WaitForPendingFinalizers()\\n"); DEBUG_PRINT("was called prior to exit after all LDK objects were out of scope.\\n"); } """ @@ -382,6 +392,18 @@ _Static_assert(offsetof(LDKCVec_u8Z, datalen) == offsetof(LDKu8slice, datalen), _Static_assert(sizeof(void*) <= 8, "Pointers must fit into 64 bits"); +// Int types across Windows/Linux are different, so make sure we're using the right headers. +_Static_assert(sizeof(void*) == sizeof(uintptr_t), "stdints must be correct"); +_Static_assert(sizeof(void*) == sizeof(intptr_t), "stdints must be correct"); +_Static_assert(sizeof(uint64_t) == 8, "stdints must be correct"); +_Static_assert(sizeof(int64_t) == 8, "stdints must be correct"); +_Static_assert(sizeof(uint32_t) == 4, "stdints must be correct"); +_Static_assert(sizeof(int32_t) == 4, "stdints must be correct"); +_Static_assert(sizeof(uint16_t) == 2, "stdints must be correct"); +_Static_assert(sizeof(int16_t) == 2, "stdints must be correct"); +_Static_assert(sizeof(uint8_t) == 1, "stdints must be correct"); +_Static_assert(sizeof(int8_t) == 1, "stdints must be correct"); + #define DECL_ARR_TYPE(ty, name) \\ struct name##array { \\ uint64_t arr_len; /* uint32_t would suffice but we want to align uint64_ts as well */ \\ @@ -420,6 +442,16 @@ static inline LDKStr str_ref_to_owned_c(const jstring str) { return res; } +typedef bool jboolean; + +int64_t CS_LDK_allocate_buffer(int64_t len) { + return (int64_t)MALLOC(len, "C#-requested buffer"); +} + +void CS_LDK_free_buffer(int64_t buf) { + FREE((void*)buf); +} + jstring CS_LDK_get_ldk_c_bindings_version() { return str_ref_to_cs(check_get_ldk_bindings_version(), strlen(check_get_ldk_bindings_version())); } @@ -457,7 +489,7 @@ namespace org { namespace ldk { namespace structs { self.bindings_footer_wip = "\tstatic bindings() {\n" def bindings_footer(self): - return self.bindings_footer_wip + "\t}\n}\n} } }\n" + return "" def native_meth_decl(self, meth_name, ret_ty_str): return "\t[DllImport (\"ldkcsharp\", EntryPoint=\"CS_LDK_" + meth_name + "\")] public static extern " + ret_ty_str + " " + meth_name @@ -506,31 +538,53 @@ namespace org { namespace ldk { namespace structs { def map_hu_array_elems(self, arr_name, conv_name, arr_ty, elem_ty, is_nullable): if elem_ty.java_hu_ty == "UInt5": - return arr_name + " != null ? InternalUtils.convUInt5Array(" + arr_name + ") : null" + return "InternalUtils.convUInt5Array(" + arr_name + ")" elif elem_ty.java_hu_ty == "WitnessVersion": - return arr_name + " != null ? InternalUtils.convWitnessVersionArray(" + arr_name + ") : null" + return "InternalUtils.convWitnessVersionArray(" + arr_name + ")" else: - return arr_name + " != null ? InternalUtils.mapArray(" + arr_name + ", " + conv_name + " => " + elem_ty.from_hu_conv[0] + ") : null" + return "InternalUtils.mapArray(" + arr_name + ", " + conv_name + " => " + elem_ty.from_hu_conv[0] + ")" def str_ref_to_native_call(self, var_name, str_len): return "str_ref_to_cs(" + var_name + ", " + str_len + ")" def str_ref_to_c_call(self, var_name): return "str_ref_to_owned_c(" + var_name + ")" def str_to_hu_conv(self, var_name): - return None + return "string " + var_name + "_conv = InternalUtils.decodeString(" + var_name + ");" def str_from_hu_conv(self, var_name): - return None + return ("InternalUtils.encodeString(" + var_name + ")", "") def init_str(self): - return "" + ret = "" + for fn_suffix in self.function_ptrs: + cret = self.function_ptrs[fn_suffix]["ret"][1] + cargs = self.function_ptrs[fn_suffix]["args"][1] + ret += f""" +typedef {cret} (*invoker_{fn_suffix})(int obj_ptr, int fn_id{cargs}); +static invoker_{fn_suffix} js_invoke_function_{fn_suffix}; +int CS_LDK_register_{fn_suffix}_invoker(invoker_{fn_suffix} invoker) {{ + js_invoke_function_{fn_suffix} = invoker; + return 0; +}} +""" + + return ret def var_decl_statement(self, ty_string, var_name, statement): return ty_string + " " + var_name + " = " + statement def get_java_arr_len(self, arr_name): - return arr_name + ".Length" + return "InternalUtils.getArrayLength(" + arr_name + ")" + def get_java_arr_elem(self, elem_ty, arr_name, idx): - return arr_name + "[" + idx + "]" + if elem_ty.c_ty == "int64_t" or elem_ty.c_ty == "uint64_t": + return "InternalUtils.getU64ArrayElem(" + arr_name + ", " + idx + ")" + elif elem_ty.c_ty.endswith("Array") or elem_ty.c_ty == "uintptr_t" or elem_ty.rust_obj == "LDKStr": + return "InternalUtils.getU64ArrayElem(" + arr_name + ", " + idx + ")" + elif elem_ty.rust_obj == "LDKU5": + return "InternalUtils.getU8ArrayElem(" + arr_name + ", " + idx + ")" + else: + assert False + def constr_hu_array(self, ty_info, arr_len): base_ty = ty_info.subty.java_hu_ty.split("[")[0].split("<")[0] conv = "new " + base_ty + "[" + arr_len + "]" @@ -539,22 +593,44 @@ namespace org { namespace ldk { namespace structs { conv += "[" + ty_info.subty.java_hu_ty.split("<")[0].split("[")[1] return conv def cleanup_converted_native_array(self, ty_info, arr_name): - return None + return "bindings.free_buffer(" + arr_name + ");" def primitive_arr_from_hu(self, arr_ty, fixed_len, arr_name): mapped_ty = arr_ty.subty + inner = arr_name if arr_ty.rust_obj == "LDKU128": - return ("" + arr_name + ".getLEBytes()", "") + return ("InternalUtils.encodeUint8Array(" + arr_name + ".getLEBytes())", "") if fixed_len is not None: - return ("InternalUtils.check_arr_len(" + arr_name + ", " + fixed_len + ")", "") - return None + inner = "InternalUtils.check_arr_len(" + arr_name + ", " + fixed_len + ")" + if mapped_ty.c_ty.endswith("Array"): + return ("InternalUtils.encodeUint64Array(" + inner + ")", "") + elif mapped_ty.c_ty == "uint8_t" or mapped_ty.c_ty == "int8_t": + return ("InternalUtils.encodeUint8Array(" + inner + ")", "") + elif mapped_ty.c_ty == "uint16_t" or mapped_ty.c_ty == "int16_t": + return ("InternalUtils.encodeUint16Array(" + inner + ")", "") + elif mapped_ty.c_ty == "uint32_t": + return ("InternalUtils.encodeUint32Array(" + inner + ")", "") + elif mapped_ty.c_ty == "int64_t" or mapped_ty.c_ty == "uint64_t" or mapped_ty.rust_obj == "LDKStr": + return ("InternalUtils.encodeUint64Array(" + inner + ")", "") + else: + print(mapped_ty.c_ty) + assert False + def primitive_arr_to_hu(self, arr_ty, fixed_len, arr_name, conv_name): + mapped_ty = arr_ty.subty if arr_ty.rust_obj == "LDKU128": return "org.ldk.util.UInt128 " + conv_name + " = new org.ldk.util.UInt128(" + arr_name + ");" - return None + elif mapped_ty.c_ty == "uint8_t" or mapped_ty.c_ty == "int8_t": + return "byte[] " + conv_name + " = InternalUtils.decodeUint8Array(" + arr_name + ");" + elif mapped_ty.c_ty == "uint16_t" or mapped_ty.c_ty == "int16_t": + return "short[] " + conv_name + " = InternalUtils.decodeUint16Array(" + arr_name + ");" + elif mapped_ty.c_ty == "uint64_t" or mapped_ty.c_ty == "int64_t": + return "long[] " + conv_name + " = InternalUtils.decodeUint64Array(" + arr_name + ");" + else: + assert False def java_arr_ty_str(self, elem_ty_str): - return elem_ty_str + "[]" + return "long" def for_n_in_range(self, n, minimum, maximum): return "for (int " + n + " = " + minimum + "; " + n + " < " + maximum + "; " + n + "++) {" @@ -642,89 +718,69 @@ namespace org { namespace ldk { namespace structs { ret = ret + ", " + param return ret + ")" - def native_c_map_trait(self, struct_name, field_vars, flattened_field_vars, field_fns, trait_doc_comment): - out_java_trait = "" - out_java = "" - - # First generate most of the Java code, note that we need information about java method argument strings for C - out_java_trait += self.hu_struct_file_prefix - if trait_doc_comment is not None: - out_java_trait += "/**\n * " + trait_doc_comment.replace("\n", "\n * ") + "\n */\n" - out_java_trait = out_java_trait + "public class " + struct_name.replace("LDK","") + " : CommonBase {\n" - out_java_trait = out_java_trait + "\tinternal readonly bindings." + struct_name + " bindings_instance;\n" - out_java_trait = out_java_trait + "\tinternal " + struct_name.replace("LDK", "") + "(object _dummy, long ptr) : base(ptr) { bindings_instance = null; }\n" - out_java_trait = out_java_trait + "\tprivate " + struct_name.replace("LDK", "") + "(bindings." + struct_name + " arg" - for var in flattened_field_vars: - if isinstance(var, ConvInfo): - out_java_trait += ", " + var.java_hu_ty + " " + var.arg_name - else: - out_java_trait += ", bindings." + var[0] + " " + var[1] - out_java_trait += ") : base(bindings." + struct_name + "_new(arg" - for var in flattened_field_vars: + def native_c_map_trait(self, struct_name, field_var_conversions, flattened_field_var_conversions, field_function_lines, trait_doc_comment): + out_typescript_bindings = "" + super_instantiator = "" + bindings_instantiator = "" + pointer_to_adder = "" + impl_constructor_arguments = "" + for var in flattened_field_var_conversions: if isinstance(var, ConvInfo): + impl_constructor_arguments += f", {var.java_hu_ty} {var.arg_name}" if var.from_hu_conv is not None: - out_java_trait = out_java_trait + ", " + var.from_hu_conv[0] + bindings_instantiator += ", " + var.from_hu_conv[0] + if var.from_hu_conv[1] != "": + pointer_to_adder += "\t\t\t" + var.from_hu_conv[1] + ";\n" else: - out_java_trait = out_java_trait + ", " + var.arg_name + bindings_instantiator += ", " + first_to_lower(var.arg_name) else: - out_java_trait = out_java_trait + ", " + var[1] - out_java_trait = out_java_trait + ")) {\n" - out_java_trait = out_java_trait + "\t\tthis.ptrs_to.AddLast(arg);\n" - for var in flattened_field_vars: + bindings_instantiator += ", " + first_to_lower(var[1]) + ".instance_idx" + pointer_to_adder += "\t\timpl_holder.held.ptrs_to.AddLast(" + first_to_lower(var[1]) + ");\n" + impl_constructor_arguments += f", {var[0].replace('LDK', '')}Interface {first_to_lower(var[1])}_impl" + + super_constructor_statements = "" + trait_constructor_arguments = "" + for var in field_var_conversions: if isinstance(var, ConvInfo): - if var.from_hu_conv is not None and var.from_hu_conv[1] != "": - out_java_trait = out_java_trait + "\t\t" + var.from_hu_conv[1].replace("\n", "\n\t\t") + ";\n" + trait_constructor_arguments += ", " + var.arg_name else: - out_java_trait = out_java_trait + "\t\tthis.ptrs_to.AddLast(" + var[1] + ");\n" - out_java_trait = out_java_trait + "\t\tthis.bindings_instance = arg;\n" - out_java_trait = out_java_trait + "\t}\n" - out_java_trait = out_java_trait + "\t~" + struct_name.replace("LDK","") + "() {\n" - out_java_trait = out_java_trait + "\t\tif (ptr != 0) { bindings." + struct_name.replace("LDK","") + "_free(ptr); }\n" - out_java_trait = out_java_trait + "\t}\n\n" + super_constructor_statements += "\t\t" + var[1] + " " + first_to_lower(var[1]) + " = " + var[1] + ".new_impl(" + first_to_lower(var[1]) + "_impl" + super_instantiator = "" + for suparg in var[2]: + if isinstance(suparg, ConvInfo): + super_instantiator += ", " + suparg.arg_name + else: + super_instantiator += ", " + first_to_lower(suparg[1]) + "_impl" + super_constructor_statements += super_instantiator + ");\n" + trait_constructor_arguments += ", " + first_to_lower(var[1]) + ".instance_idx" + for suparg in var[2]: + if isinstance(suparg, ConvInfo): + trait_constructor_arguments += ", " + suparg.arg_name + else: + # Blindly assume that we can just strip the first arg to build the args for the supertrait + super_constructor_statements += "\t\t" + suparg[1] + " " + first_to_lower(suparg[1]) + " = " + suparg[1] + ".new_impl(" + super_instantiator.split(", ", 1)[1] + ");\n" + trait_constructor_arguments += ", " + suparg[1] + + # BUILD INTERFACE METHODS java_trait_wrapper = "\tprivate class " + struct_name + "Holder { internal " + struct_name.replace("LDK", "") + " held; }\n" java_trait_wrapper += "\tprivate class " + struct_name + "Impl : bindings." + struct_name + " {\n" java_trait_wrapper += "\t\tinternal " + struct_name + "Impl(" + struct_name.replace("LDK", "") + "Interface arg, " + struct_name + "Holder impl_holder) { this.arg = arg; this.impl_holder = impl_holder; }\n" java_trait_wrapper += "\t\tprivate " + struct_name.replace("LDK", "") + "Interface arg;\n" java_trait_wrapper += "\t\tprivate " + struct_name + "Holder impl_holder;\n" - java_trait_constr = "\tpublic static " + struct_name.replace("LDK", "") + " new_impl(" + struct_name.replace("LDK", "") + "Interface arg" - for var in flattened_field_vars: - if isinstance(var, ConvInfo): - java_trait_constr += ", " + var.java_hu_ty + " " + var.arg_name - else: - # Ideally we'd be able to take any instance of the interface, but our C code can only represent - # Java-implemented version, so we require users pass a Java implementation here :/ - java_trait_constr += ", " + var[0].replace("LDK", "") + "." + var[0].replace("LDK", "") + "Interface " + var[1] + "_impl" - java_trait_constr = java_trait_constr + ") {\n\t\t" + struct_name + "Holder impl_holder = new " + struct_name + "Holder();\n" - java_trait_constr = java_trait_constr + "\t\timpl_holder.held = new " + struct_name.replace("LDK", "") + "(new " + struct_name + "Impl(arg, impl_holder)" - out_java_trait += "\tpublic interface " + struct_name.replace("LDK", "") + "Interface {\n" - out_java += "\tpublic interface " + struct_name + " {\n" - java_meths = [] - for fn_line in field_fns: - java_meth_descr = "(" + + for fn_line in field_function_lines: if fn_line.fn_name != "free" and fn_line.fn_name != "cloned": fn_name = fn_line.fn_name if fn_name == "lock": # reserved symbol fn_name = "do_lock" - out_java += "\t\t" + fn_line.ret_ty_info.java_ty + " " + fn_name + "(" java_trait_wrapper += "\t\tpublic " + fn_line.ret_ty_info.java_ty + " " + fn_name + "(" - out_java_trait += "\t\t/**\n\t\t * " + fn_line.docs.replace("\n", "\n\t\t * ") + "\n\t\t */\n" - out_java_trait += "\t\t" + fn_line.ret_ty_info.java_hu_ty + " " + fn_name + "(" for idx, arg_conv_info in enumerate(fn_line.args_ty): if idx >= 1: - out_java += ", " java_trait_wrapper += ", " - out_java_trait += ", " - out_java += arg_conv_info.java_ty + " _" + arg_conv_info.arg_name - out_java_trait += arg_conv_info.java_hu_ty + " _" + arg_conv_info.arg_name java_trait_wrapper += arg_conv_info.java_ty + " _" + arg_conv_info.arg_name - java_meth_descr = java_meth_descr + arg_conv_info.java_fn_ty_arg - java_meth_descr = java_meth_descr + ")" + fn_line.ret_ty_info.java_fn_ty_arg - java_meths.append((fn_line.fn_name, java_meth_descr)) - out_java += ");\n" - out_java_trait += ");\n" java_trait_wrapper += ") {\n" for arg_info in fn_line.args_ty: @@ -756,66 +812,133 @@ namespace org { namespace ldk { namespace structs { java_trait_wrapper += "\t\t\treturn ret;\n" java_trait_wrapper += "\t\t}\n" java_trait_wrapper += "\t}" - for var in field_vars: - if isinstance(var, ConvInfo): - java_trait_constr = java_trait_constr + ", " + var.arg_name - else: - java_trait_constr += ", " + var[1] + ".new_impl(" + var[1] + "_impl" - suptrait_constr = "" - for suparg in var[2]: - if isinstance(suparg, ConvInfo): - suptrait_constr += ", " + suparg.arg_name - else: - suptrait_constr += ", " + suparg[1] + "_impl" - java_trait_constr += suptrait_constr + ").bindings_instance" - for suparg in var[2]: - if isinstance(suparg, ConvInfo): - java_trait_constr += ", " + suparg.arg_name - else: - java_trait_constr += ", " + suparg[1] + ".new_impl(" - # Blindly assume that we can just strip the first arg to build the args for the supertrait - java_trait_constr += suptrait_constr.split(", ", 1)[1] - java_trait_constr += ").bindings_instance" - out_java_trait += "\t}\n" + java_trait_wrapper + "\n" - out_java_trait += java_trait_constr + ");\n\t\treturn impl_holder.held;\n\t}\n" - out_java += "\t}\n" + out_java_interface = "" + java_methods = [] + for fn_line in field_function_lines: + java_method_descriptor = "" + if fn_line.fn_name != "free" and fn_line.fn_name != "cloned": + out_java_interface += "\t/**" + fn_line.docs.replace("\n", "\n\t * ") + "\n\t */\n" + out_java_interface += "\t" + fn_line.ret_ty_info.java_hu_ty + " " + fn_line.fn_name + "(" + + for idx, arg_conv_info in enumerate(fn_line.args_ty): + if idx >= 1: + out_java_interface += ", " + out_java_interface += f"{arg_conv_info.java_hu_ty} {safe_arg_name(arg_conv_info.arg_name)}" + java_method_descriptor += arg_conv_info.java_fn_ty_arg + out_java_interface += f");\n" + java_method_descriptor += ")" + fn_line.ret_ty_info.java_fn_ty_arg + java_methods.append((fn_line.fn_name, java_method_descriptor)) + + formatted_trait_docs = trait_doc_comment.replace("\n", "\n * ") + out_typescript_human = f""" +{self.hu_struct_file_prefix} + +/** An implementation of {struct_name.replace("LDK","")} */ +public interface {struct_name.replace("LDK", "")}Interface {{ +{out_java_interface}}} + +/** + * {formatted_trait_docs} + */ +public class {struct_name.replace("LDK","")} : CommonBase {{ + internal bindings.{struct_name} bindings_instance; + internal long instance_idx; + + internal {struct_name.replace("LDK","")}(object _dummy, long ptr) : base(ptr) {{ bindings_instance = null; }} + ~{struct_name.replace("LDK","")}() {{ + if (ptr != 0) {{ bindings.{struct_name.replace("LDK","")}_free(ptr); }} + }} + +{java_trait_wrapper} + + /** Creates a new instance of {struct_name.replace("LDK","")} from a given implementation */ + public static {struct_name.replace("LDK", "")} new_impl({struct_name.replace("LDK", "")}Interface arg{impl_constructor_arguments}) {{ + {struct_name}Holder impl_holder = new {struct_name}Holder(); + {struct_name}Impl impl = new {struct_name}Impl(arg, impl_holder); +{super_constructor_statements} long[] ptr_idx = bindings.{struct_name}_new(impl{bindings_instantiator}); + + impl_holder.held = new {struct_name.replace("LDK", "")}(null, ptr_idx[0]); + impl_holder.held.instance_idx = ptr_idx[1]; + impl_holder.held.bindings_instance = impl; +{pointer_to_adder} return impl_holder.held; + }} + +""" + + out_typescript_bindings += "\tpublic interface " + struct_name + " {\n" + java_meths = [] + for fn_line in field_function_lines: + if fn_line.fn_name != "free" and fn_line.fn_name != "cloned": + out_typescript_bindings += f"\t\t{fn_line.ret_ty_info.java_ty} {fn_line.fn_name}(" + + for idx, arg_conv_info in enumerate(fn_line.args_ty): + if idx >= 1: + out_typescript_bindings = out_typescript_bindings + ", " + out_typescript_bindings += f"{arg_conv_info.java_ty} {safe_arg_name(arg_conv_info.arg_name)}" + + out_typescript_bindings += f");\n" - out_java += self.native_meth_decl(struct_name + "_new", "long") + "(" + struct_name + " impl" - for var in flattened_field_vars: + out_typescript_bindings += "\t}\n" + + c_call_extra_args = "" + native_fn_args = "long impl_idx" + for var in flattened_field_var_conversions: + if isinstance(var, ConvInfo): + native_fn_args += ", " + var.java_ty + " " + var.arg_name + else: + native_fn_args += ", long " + var[1] + out_typescript_bindings += self.native_meth_decl(struct_name + "_new", "long") + "_native(" + native_fn_args + ");\n" + out_typescript_bindings += f"\tpublic static long[] {struct_name}_new({struct_name} impl" + for var in flattened_field_var_conversions: if isinstance(var, ConvInfo): - out_java += ", " + var.java_ty + " " + var.arg_name + out_typescript_bindings += f", {var.java_ty} {var.arg_name}" + c_call_extra_args += f", {var.arg_name}" else: - out_java += ", " + var[0] + " " + var[1] - out_java += ");\n" + out_typescript_bindings += f", long {var[1]}" + c_call_extra_args += f", {var[1]}" + + + out_typescript_bindings += f""") {{ + long new_obj_idx = js_objs.Count; + int i = 0; + for (; i < js_objs.Count; i++) {{ + if (js_objs[i] == null || !js_objs[i].IsAlive) {{ new_obj_idx = i; break; }} + }} + if (i == js_objs.Count) {{ + js_objs.Add(new WeakReference(impl)); + }} else {{ + js_objs[i] = new WeakReference(impl); + }} + long[] ret = new long[2]; + ret[0] = {struct_name}_new_native(i{c_call_extra_args}); + ret[1] = i; + return ret; + }} +""" # Now that we've written out our java code (and created java_meths), generate C out_c = "typedef struct " + struct_name + "_JCalls {\n" - out_c = out_c + "\tatomic_size_t refcnt;\n" - out_c = out_c + "\tJavaVM *vm;\n" - out_c = out_c + "\tjweak o;\n" - for var in flattened_field_vars: + out_c += "\tatomic_size_t refcnt;\n" + out_c += "\tuint32_t instance_ptr;\n" + for var in flattened_field_var_conversions: if isinstance(var, ConvInfo): # We're a regular ol' field pass else: # We're a supertrait out_c = out_c + "\t" + var[0] + "_JCalls* " + var[1] + ";\n" - for fn in field_fns: - if fn.fn_name != "free" and fn.fn_name != "cloned": - out_c = out_c + "\tjmethodID " + fn.fn_name + "_meth;\n" out_c = out_c + "} " + struct_name + "_JCalls;\n" - for fn_line in field_fns: + for fn_line in field_function_lines: if fn_line.fn_name == "free": out_c = out_c + "static void " + struct_name + "_JCalls_free(void* this_arg) {\n" out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) this_arg;\n" out_c = out_c + "\tif (atomic_fetch_sub_explicit(&j_calls->refcnt, 1, memory_order_acquire) == 1) {\n" - out_c = out_c + "\t\t(*env)->DeleteWeakGlobalRef(env, j_calls->o);\n" out_c = out_c + "\t\tFREE(j_calls);\n" out_c = out_c + "\t}\n}\n" - for idx, fn_line in enumerate(field_fns): + for idx, fn_line in enumerate(field_function_lines): if fn_line.fn_name != "free" and fn_line.fn_name != "cloned": assert fn_line.ret_ty_info.ty_info.get_full_rust_ty()[1] == "" out_c = out_c + fn_line.ret_ty_info.ty_info.get_full_rust_ty()[0] + " " + fn_line.fn_name + "_" + struct_name + "_jcall(" @@ -836,87 +959,98 @@ namespace org { namespace ldk { namespace structs { out_c = out_c + arg_info.arg_name out_c = out_c + arg_info.ret_conv[1].replace('\n', '\n\t') + "\n" - out_c = out_c + "\tjobject obj = (*env)->NewLocalRef(env, j_calls->o);\n\tCHECK(obj != NULL);\n" + ty_to_c = lambda jty, ty: "b" if jty == "bool" else "c" if jty == "char" else "s" if jty == "short" else "i" if jty == "int" else "l" if jty == "long" else "void" if jty == "void" else jty + + fn_java_callback_args = "" + fn_c_callback_args = "" + fn_callback_call_args = "" + fn_suffix = ty_to_c(fn_line.ret_ty_info.java_ty, fn_line.ret_ty_info) + "_" + idx = 0 + for arg_info in fn_line.args_ty: + fn_suffix += ty_to_c(arg_info.java_ty, arg_info) + fn_java_callback_args += ", " + arg_info.java_ty + " " + chr(ord("a") + idx) + if arg_info.c_ty.endswith("Array") or arg_info.c_ty == "jstring": + fn_c_callback_args += ", int64_t " + chr(ord("a") + idx) + else: + fn_c_callback_args += ", " + arg_info.c_ty + " " + chr(ord("a") + idx) + if idx != 0: + fn_callback_call_args += ", " + fn_callback_call_args += chr(ord("a") + idx) + idx += 1 if fn_line.ret_ty_info.c_ty.endswith("Array"): - out_c = out_c + "\t" + fn_line.ret_ty_info.c_ty + " ret = (*env)->CallObjectMethod(env, obj, j_calls->" + fn_line.fn_name + "_meth" - elif fn_line.ret_ty_info.c_ty == "void": - out_c += "\t(*env)->CallVoidMethod(env, obj, j_calls->" + fn_line.fn_name + "_meth" - elif fn_line.ret_ty_info.java_hu_ty == "string" or "org/ldk/enums" in fn_line.ret_ty_info.java_fn_ty_arg: - # Manually write out string methods as they're just an Object - out_c += "\t" + fn_line.ret_ty_info.c_ty + " ret = (*env)->CallObjectMethod(env, obj, j_calls->" + fn_line.fn_name + "_meth" - elif not fn_line.ret_ty_info.passed_as_ptr: - out_c += "\t" + fn_line.ret_ty_info.c_ty + " ret = (*env)->Call" + fn_line.ret_ty_info.java_ty.title() + "Method(env, obj, j_calls->" + fn_line.fn_name + "_meth" + out_c += "\t" + fn_line.ret_ty_info.c_ty + " ret = (" + fn_line.ret_ty_info.c_ty + ")" + out_c += "js_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + elif fn_line.ret_ty_info.java_ty == "void": + out_c = out_c + "\tjs_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + elif fn_line.ret_ty_info.java_hu_ty == "string": + out_c += "\tjstring ret = (jstring)js_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + elif fn_line.ret_ty_info.arg_conv is None: + out_c += "\treturn js_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) else: - out_c = out_c + "\tuint64_t ret = (*env)->CallLongMethod(env, obj, j_calls->" + fn_line.fn_name + "_meth" + out_c += "\tuint64_t ret = js_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + + if fn_suffix not in self.function_ptrs: + caller_ret_c_ty = fn_line.ret_ty_info.c_ty + if fn_line.ret_ty_info.c_ty.endswith("Array") or fn_line.ret_ty_info.c_ty == "jstring": + caller_ret_c_ty = "int64_t" + self.function_ptrs[fn_suffix] = {"args": [fn_java_callback_args, fn_c_callback_args], "ret": [fn_line.ret_ty_info.java_ty, caller_ret_c_ty]} + self.function_ptrs[fn_suffix][self.function_ptr_counter] = (struct_name, fn_line.fn_name, fn_callback_call_args) + self.function_ptr_counter += 1 for idx, arg_info in enumerate(fn_line.args_ty): if arg_info.ret_conv is not None: - out_c = out_c + ", " + arg_info.ret_conv_name + if arg_info.c_ty.endswith("Array") or arg_info.c_ty == "jstring": + out_c += ", (int64_t)" + arg_info.ret_conv_name + else: + out_c += ", " + arg_info.ret_conv_name else: - out_c = out_c + ", " + arg_info.arg_name + assert False # TODO: Would we need some conversion here? + out_c += ", (int64_t)" + arg_info.arg_name out_c = out_c + ");\n" - - out_c += "\tif (UNLIKELY((*env)->ExceptionCheck(env))) {\n" - out_c += "\t\t(*env)->ExceptionDescribe(env);\n" - out_c += "\t\t(*env)->FatalError(env, \"A call to " + fn_line.fn_name + " in " + struct_name + " from rust threw an exception.\");\n" - out_c += "\t}\n" - if fn_line.ret_ty_info.arg_conv is not None: - out_c += "\t" + fn_line.ret_ty_info.arg_conv.replace("\n", "\n\t") + "\n" - out_c += "\treturn " + fn_line.ret_ty_info.arg_conv_name + ";\n" - else: - if not fn_line.ret_ty_info.passed_as_ptr and fn_line.ret_ty_info.c_ty != "void": - out_c += "\treturn ret;\n" + out_c = out_c + "\t" + fn_line.ret_ty_info.arg_conv.replace("\n", "\n\t") + "\n\treturn " + fn_line.ret_ty_info.arg_conv_name + ";\n" out_c = out_c + "}\n" - # If we can, write out a clone function whether we need one or not, as we use them in moving to rust - can_clone_with_ptr = True - for var in field_vars: - if isinstance(var, ConvInfo): - can_clone_with_ptr = False - if can_clone_with_ptr: - out_c = out_c + "static void " + struct_name + "_JCalls_cloned(" + struct_name + "* new_obj) {\n" - out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) new_obj->this_arg;\n" - out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->refcnt, 1, memory_order_release);\n" - for var in field_vars: - if not isinstance(var, ConvInfo): - out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->" + var[1] + "->refcnt, 1, memory_order_release);\n" - out_c = out_c + "}\n" - - out_c = out_c + "static inline " + struct_name + " " + struct_name + "_init (" + self.c_fn_args_pfx + ", jobject o" - for var in flattened_field_vars: + # Write out a clone function whether we need one or not, as we use them in moving to rust + out_c = out_c + "static void " + struct_name + "_JCalls_cloned(" + struct_name + "* new_obj) {\n" + out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) new_obj->this_arg;\n" + out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->refcnt, 1, memory_order_release);\n" + for var in flattened_field_var_conversions: + if not isinstance(var, ConvInfo): + out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->" + var[2].replace(".", "->") + "->refcnt, 1, memory_order_release);\n" + out_c = out_c + "}\n" + + out_c = out_c + "static inline " + struct_name + " " + struct_name + "_init (int64_t o" + for var in flattened_field_var_conversions: if isinstance(var, ConvInfo): out_c = out_c + ", " + var.c_ty + " " + var.arg_name else: - out_c = out_c + ", jobject " + var[1] + out_c = out_c + ", int64_t " + var[1] out_c = out_c + ") {\n" - out_c = out_c + "\tjclass c = (*env)->GetObjectClass(env, o);\n" - out_c = out_c + "\tCHECK(c != NULL);\n" out_c = out_c + "\t" + struct_name + "_JCalls *calls = MALLOC(sizeof(" + struct_name + "_JCalls), \"" + struct_name + "_JCalls\");\n" out_c = out_c + "\tatomic_init(&calls->refcnt, 1);\n" - out_c = out_c + "\tDO_ASSERT((*env)->GetJavaVM(env, &calls->vm) == 0);\n" - out_c = out_c + "\tcalls->o = (*env)->NewWeakGlobalRef(env, o);\n" + out_c = out_c + "\tcalls->instance_ptr = o;\n" for (fn_name, java_meth_descr) in java_meths: if fn_name != "free" and fn_name != "cloned": out_c = out_c + "\tcalls->" + fn_name + "_meth = (*env)->GetMethodID(env, c, \"" + fn_name + "\", \"" + java_meth_descr + "\");\n" out_c = out_c + "\tCHECK(calls->" + fn_name + "_meth != NULL);\n" - for var in flattened_field_vars: + for var in flattened_field_var_conversions: if isinstance(var, ConvInfo) and var.arg_conv is not None: out_c = out_c + "\n\t" + var.arg_conv.replace("\n", "\n\t") +"\n" out_c = out_c + "\n\t" + struct_name + " ret = {\n" out_c = out_c + "\t\t.this_arg = (void*) calls,\n" - for fn_line in field_fns: + for fn_line in field_function_lines: if fn_line.fn_name != "free" and fn_line.fn_name != "cloned": out_c = out_c + "\t\t." + fn_line.fn_name + " = " + fn_line.fn_name + "_" + struct_name + "_jcall,\n" elif fn_line.fn_name == "free": out_c = out_c + "\t\t.free = " + struct_name + "_JCalls_free,\n" else: out_c = out_c + "\t\t.cloned = " + struct_name + "_JCalls_cloned,\n" - for var in field_vars: + for var in field_var_conversions: if isinstance(var, ConvInfo): if var.arg_conv_name is not None: out_c = out_c + "\t\t." + var.arg_name + " = " + var.arg_conv_name + ",\n" @@ -925,30 +1059,30 @@ namespace org { namespace ldk { namespace structs { out_c = out_c + "\t\t." + var.var_name + " = " + var.var_name + ",\n" out_c = out_c + "\t\t.set_" + var.var_name + " = NULL,\n" else: - out_c += "\t\t." + var[1] + " = " + var[0] + "_init(env, clz, " + var[1] + out_c += "\t\t." + var[1] + " = " + var[0] + "_init(" + var[1] for suparg in var[2]: if isinstance(suparg, ConvInfo): - out_c = out_c + ", " + suparg.arg_name + out_c += ", " + suparg.arg_name else: - out_c = out_c + ", " + suparg[1] + out_c += ", " + suparg[1] out_c += "),\n" out_c = out_c + "\t};\n" - for var in flattened_field_vars: + for var in flattened_field_var_conversions: if not isinstance(var, ConvInfo): - out_c = out_c + "\tcalls->" + var[1] + " = ret." + var[1] + ".this_arg;\n" + out_c = out_c + "\tcalls->" + var[1] + " = ret." + var[2] + ".this_arg;\n" out_c = out_c + "\treturn ret;\n" out_c = out_c + "}\n" - out_c += "int64_t " + self.c_fn_name_define_pfx(struct_name + "_new", True) + "jobject o" - for var in flattened_field_vars: + out_c = out_c + self.c_fn_ty_pfx + "uint64_t " + self.c_fn_name_define_pfx(struct_name + "_new", True) + "int32_t o" + for var in flattened_field_var_conversions: if isinstance(var, ConvInfo): out_c = out_c + ", " + var.c_ty + " " + var.arg_name else: - out_c = out_c + ", jobject " + var[1] + out_c = out_c + ", int32_t " + var[1] out_c = out_c + ") {\n" out_c = out_c + "\t" + struct_name + " *res_ptr = MALLOC(sizeof(" + struct_name + "), \"" + struct_name + "\");\n" - out_c = out_c + "\t*res_ptr = " + struct_name + "_init(env, clz, o" - for var in flattened_field_vars: + out_c = out_c + "\t*res_ptr = " + struct_name + "_init(o" + for var in flattened_field_var_conversions: if isinstance(var, ConvInfo): out_c = out_c + ", " + var.arg_name else: @@ -957,27 +1091,7 @@ namespace org { namespace ldk { namespace structs { out_c = out_c + "\treturn tag_ptr(res_ptr, true);\n" out_c = out_c + "}\n" - for var in flattened_field_vars: - if not isinstance(var, ConvInfo): - out_java_trait += "\n\t/**\n" - out_java_trait += "\t * Gets the underlying " + var[1] + ".\n" - out_java_trait += "\t */\n" - underscore_name = ''.join('_' + c.lower() if c.isupper() else c for c in var[1]).strip('_') - out_java_trait += "\tpublic " + var[1] + " get_" + underscore_name + "() {\n" - out_java_trait += "\t\t" + var[1] + " res = new " + var[1] + "(null, bindings." + struct_name + "_get_" + var[1] + "(this.ptr));\n" - out_java_trait += "\t\tthis.ptrs_to.AddLast(res);\n" - out_java_trait += "\t\treturn res;\n" - out_java_trait += "\t}\n" - out_java_trait += "\n" - - out_java += self.native_meth_decl(struct_name + "_get_" + var[1], "long") + "(long arg);\n" - - out_c += "int64_t " + self.c_fn_name_define_pfx(struct_name + "_get_" + var[1], True) + "int64_t arg) {\n" - out_c += "\t" + struct_name + " *inp = (" + struct_name + " *)untag_ptr(arg);\n" - out_c += "\treturn tag_ptr(&inp->" + var[1] + ", false);\n" - out_c += "}\n" - - return (out_java, out_java_trait, out_c) + return (out_typescript_bindings, out_typescript_human, out_c) def trait_struct_inc_refcnt(self, ty_info): base_conv = "\nif (" + ty_info.var_name + "_conv.free == " + ty_info.rust_obj + "_JCalls_free) {\n" @@ -1041,7 +1155,7 @@ namespace org { namespace ldk { namespace structs { fn_name = f"{struct_name}_{var.var_name}_get_{field_map.arg_name}" out_c += self.c_fn_ty_pfx + field_map.c_ty + self.c_fn_name_define_pfx(fn_name, True) + self.ptr_c_ty + " ptr) {\n" out_c += "\t" + struct_name + " *obj = (" + struct_name + "*)untag_ptr(ptr);\n" - out_c += f"\tassert(obj->tag == {struct_name}_{var.var_name});\n" + out_c += f"\tCHECK(obj->tag == {struct_name}_{var.var_name});\n" if field_map.ret_conv is not None: out_c += ("\t" + field_map.ret_conv[0].replace("\n", "\n\t")) if var.tuple_variant: @@ -1065,7 +1179,7 @@ namespace org { namespace ldk { namespace structs { out_opaque_struct_human = "" out_opaque_struct_human += self.hu_struct_file_prefix out_opaque_struct_human += "\n/**\n * " + struct_doc_comment.replace("\n", "\n * ") + "\n */\n" - hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple").replace("LDK", "") + hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple").replace("LDKC4Tuple", "FourTuple").replace("LDK", "") out_opaque_struct_human += ("public class " + hu_name + " : CommonBase") if struct_name.startswith("LDKLocked") or struct_name.startswith("LDKReadOnly"): out_opaque_struct_human += (", IDisposable") @@ -1150,8 +1264,6 @@ namespace org { namespace ldk { namespace structs { out_c += (", ") if arg_conv_info.c_ty != "void": out_c += (arg_conv_info.c_ty + " " + arg_conv_info.arg_name) - if "[]" in arg_conv_info.java_ty: - out_java += "[MarshalAs(UnmanagedType.CustomMarshaler, MarshalType=\"org.ldk.impl.ArrayCoder\")] " out_java += (arg_conv_info.java_ty + " _" + arg_conv_info.arg_name) # Add a _ to avoid using reserved words out_java_struct = "" @@ -1329,4 +1441,55 @@ namespace org { namespace ldk { namespace structs { return (out_java, out_c, out_java_struct + extra_java_struct_out) def cleanup(self): - pass + with open(self.outdir + "src/org/ldk/impl/bindings.cs", "a") as bindings: + for fn_suffix in self.function_ptrs: + jret = self.function_ptrs[fn_suffix]["ret"][0] + jargs = self.function_ptrs[fn_suffix]["args"][0] + + bindings.write(f""" + static {jret} c_callback_{fn_suffix}(int obj_ptr, int fn_id{jargs}) {{ + if (obj_ptr >= js_objs.Count) {{ + Console.Error.WriteLine("Got function call on unknown/free'd JS object in {fn_suffix}"); + Console.Error.Flush(); + Environment.Exit(42); + }} + object obj = js_objs[obj_ptr].Target; + if (obj == null) {{ + Console.Error.WriteLine("Got function call on GC'd JS object in {fn_suffix}"); + Console.Error.Flush(); + Environment.Exit(43); + }} +""") + bindings.write("\t\tswitch (fn_id) {\n") + for f in self.function_ptrs[fn_suffix]: + if f != "ret" and f != "args" and f != "call": + bindings.write(f"""\t\t\tcase {str(f)}: + if (!(obj is {self.function_ptrs[fn_suffix][f][0]})) {{ + Console.Error.WriteLine("Got function call to object that wasn't a {self.function_ptrs[fn_suffix][f][0]} in {fn_suffix}"); + Console.Error.Flush(); + Environment.Exit(44); + }}\n""") + call = f"(({self.function_ptrs[fn_suffix][f][0]})obj).{self.function_ptrs[fn_suffix][f][1]}({self.function_ptrs[fn_suffix][f][2]});" + if jret != "void": + bindings.write("\t\t\t\treturn " + call) + else: + bindings.write("\t\t\t\t" + call + "\n\t\t\t\treturn;") + bindings.write("\n") + + bindings.write(f"""\t\t\tdefault: + Console.Error.WriteLine("Got unknown function call with id " + fn_id + " from C in {fn_suffix}"); + Console.Error.Flush(); + Environment.Exit(45); + return{" false" if jret == "bool" else " 0" if jret != "void" else ""}; + }} + }} + public delegate {jret} {fn_suffix}_callback(int obj_ptr, int fn_id{jargs}); + static {fn_suffix}_callback {fn_suffix}_callback_inst = c_callback_{fn_suffix}; +""") + bindings.write(self.native_meth_decl(f"register_{fn_suffix}_invoker", "int") + f"({fn_suffix}_callback callee);\n") + # Easiest way to get a static run is just define a variable, even if we dont care + bindings.write(f"\tstatic int _run_{fn_suffix}_registration = register_{fn_suffix}_invoker({fn_suffix}_callback_inst);") + + bindings.write(""" +} +} } }""")