use templates for human type conversions
[ldk-java] / typescript_strings.py
1 from bindingstypes import ConvInfo
2
3
4 def first_to_lower(string: str) -> str:
5     first = string[0]
6     return first.lower() + string[1:]
7
8
9 class Consts:
10     def __init__(self, DEBUG):
11
12         self.c_type_map = dict(
13             uint8_t = ['number', 'Uint8Array'],
14             uint16_t = ['number', 'Uint16Array'],
15             uint32_t = ['number', 'Uint32Array'],
16             long = ['number'],
17         )
18
19         self.to_hu_conv_templates = dict(
20             ptr = 'const {var_name}_hu_conv: {human_type} = new {human_type}(null, {var_name});',
21             default = 'const {var_name}_hu_conv: {human_type} = new {human_type}(null, {var_name});',
22         )
23
24         self.common_base = """
25             export default class CommonBase {
26                 ptr: number;
27                 ptrs_to: object[] = []; // new LinkedList(); TODO: build linked list implementation
28                 protected constructor(ptr: number) { this.ptr = ptr; }
29                 public _test_only_get_ptr(): number { return this.ptr; }
30                 protected finalize() {
31                     // TODO: finalize myself
32                 }
33             }
34         """
35
36         self.c_file_pfx = """#include <rust_types.h>
37 #include <stdatomic.h>
38 #include <lightning.h>
39
40 // These should be provided...somehow...
41 void *memset(void *s, int c, size_t n);
42 void *memcpy(void *dest, const void *src, size_t n);
43 int memcmp(const void *s1, const void *s2, size_t n);
44
45 void __attribute__((noreturn)) abort(void);
46 void assert(scalar expression);
47 """
48
49         if not DEBUG:
50             self.c_file_pfx = self.c_file_pfx + """
51 void *malloc(size_t size);
52 void free(void *ptr);
53
54 #define MALLOC(a, _) malloc(a)
55 #define FREE(p) if ((long)(p) > 1024) { free(p); }
56 #define DO_ASSERT(a) (void)(a)
57 #define CHECK(a)
58 """
59         else:
60             self.c_file_pfx = self.c_file_pfx + """
61 // Always run a, then assert it is true:
62 #define DO_ASSERT(a) do { bool _assert_val = (a); assert(_assert_val); } while(0)
63 // Assert a is true or do nothing
64 #define CHECK(a) DO_ASSERT(a)
65
66 // Running a leak check across all the allocations and frees of the JDK is a mess,
67 // so instead we implement our own naive leak checker here, relying on the -wrap
68 // linker option to wrap malloc/calloc/realloc/free, tracking everyhing allocated
69 // and free'd in Rust or C across the generated bindings shared library.
70
71 #define BT_MAX 128
72 typedef struct allocation {
73         struct allocation* next;
74         void* ptr;
75         const char* struct_name;
76 } allocation;
77 static allocation* allocation_ll = NULL;
78
79 void* __real_malloc(size_t len);
80 void* __real_calloc(size_t nmemb, size_t len);
81 static void new_allocation(void* res, const char* struct_name) {
82         allocation* new_alloc = __real_malloc(sizeof(allocation));
83         new_alloc->ptr = res;
84         new_alloc->struct_name = struct_name;
85         new_alloc->next = allocation_ll;
86         allocation_ll = new_alloc;
87 }
88 static void* MALLOC(size_t len, const char* struct_name) {
89         void* res = __real_malloc(len);
90         new_allocation(res, struct_name);
91         return res;
92 }
93 void __real_free(void* ptr);
94 static void alloc_freed(void* ptr) {
95         allocation* p = NULL;
96         allocation* it = allocation_ll;
97         while (it->ptr != ptr) {
98                 p = it; it = it->next;
99                 if (it == NULL) {
100                         //XXX: fprintf(stderr, "Tried to free unknown pointer %p\\n", ptr);
101                         return; // addrsan should catch malloc-unknown and print more info than we have
102                 }
103         }
104         if (p) { p->next = it->next; } else { allocation_ll = it->next; }
105         DO_ASSERT(it->ptr == ptr);
106         __real_free(it);
107 }
108 static void FREE(void* ptr) {
109         if ((long)ptr < 1024) return; // Rust loves to create pointers to the NULL page for dummys
110         alloc_freed(ptr);
111         __real_free(ptr);
112 }
113
114 void* __wrap_malloc(size_t len) {
115         void* res = __real_malloc(len);
116         new_allocation(res, "malloc call");
117         return res;
118 }
119 void* __wrap_calloc(size_t nmemb, size_t len) {
120         void* res = __real_calloc(nmemb, len);
121         new_allocation(res, "calloc call");
122         return res;
123 }
124 void __wrap_free(void* ptr) {
125         if (ptr == NULL) return;
126         alloc_freed(ptr);
127         __real_free(ptr);
128 }
129
130 void* __real_realloc(void* ptr, size_t newlen);
131 void* __wrap_realloc(void* ptr, size_t len) {
132         if (ptr != NULL) alloc_freed(ptr);
133         void* res = __real_realloc(ptr, len);
134         new_allocation(res, "realloc call");
135         return res;
136 }
137 void __wrap_reallocarray(void* ptr, size_t new_sz) {
138         // Rust doesn't seem to use reallocarray currently
139         DO_ASSERT(false);
140 }
141
142 void __attribute__((destructor)) check_leaks() {
143         for (allocation* a = allocation_ll; a != NULL; a = a->next) {
144                 //XXX: fprintf(stderr, "%s %p remains\\n", a->struct_name, a->ptr);
145         }
146         DO_ASSERT(allocation_ll == NULL);
147 }
148 """
149         self.c_file_pfx = self.c_file_pfx + """
150 // We assume that CVec_u8Z and u8slice are the same size and layout (and thus pointers to the two can be mixed)
151 _Static_assert(sizeof(LDKCVec_u8Z) == sizeof(LDKu8slice), "Vec<u8> and [u8] need to have been mapped identically");
152 _Static_assert(offsetof(LDKCVec_u8Z, data) == offsetof(LDKu8slice, data), "Vec<u8> and [u8] need to have been mapped identically");
153 _Static_assert(offsetof(LDKCVec_u8Z, datalen) == offsetof(LDKu8slice, datalen), "Vec<u8> and [u8] need to have been mapped identically");
154
155 _Static_assert(sizeof(void*) == 4, "Pointers mut be 32 bits");
156
157 typedef struct int64_tArray {uint32_t len;int64_t *ptr;} int64_tArray;
158 typedef struct uint32_tArray {uint32_t len;int32_t *ptr;} uint32_tArray;
159 typedef struct int8_tArray {uint32_t len;int8_t *ptr;} int8_tArray;
160
161 typedef bool jboolean;
162
163 """
164
165         self.hu_struct_file_prefix = f"""
166 import CommonBase from './CommonBase';
167 import * as bindings from '../bindings' // TODO: figure out location
168
169 """
170         self.c_fn_ty_pfx = ""
171         self.c_fn_name_pfx = ""
172         self.c_fn_args_pfx = "void* ctx_TODO"
173         self.file_ext = ".ts"
174         self.ptr_c_ty = "uint32_t"
175         self.ptr_native_ty = "uint32_t"
176         self.result_c_ty = "uint32_t"
177         self.ptr_arr = "uint32_tArray"
178         self.get_native_arr_len_call = ("", ".len")
179         self.get_native_arr_ptr_call = ("", ".ptr")
180
181     def release_native_arr_ptr_call(self, arr_var, arr_ptr_var):
182         return None
183     def create_native_arr_call(self, arr_len, ty_info):
184         if ty_info.c_ty == "int8_tArray":
185             return "{ .len = " + arr_len + ", .ptr = MALLOC(" + arr_len + ", \"Native " + ty_info.c_ty + " Bytes\") }"
186         elif ty_info.c_ty == "int64_tArray":
187             return "{ .len = " + arr_len + ", .ptr = MALLOC(" + arr_len + " * sizeof(int64_t), \"Native " + ty_info.c_ty + " Bytes\") }"
188         elif ty_info.c_ty == "uint32_tArray":
189             return "{ .len = " + arr_len + ", .ptr = MALLOC(" + arr_len + " * sizeof(int32_t), \"Native " + ty_info.c_ty + " Bytes\") }"
190         else:
191             print("Need to create arr!", ty_info.c_ty)
192             return ty_info.c_ty
193     def set_native_arr_contents(self, arr_name, arr_len, ty_info):
194         if ty_info.c_ty == "int8_tArray":
195             return ("memcpy(" + arr_name + ".ptr, ", ", " + arr_len + ")")
196         else:
197             assert False
198     def get_native_arr_contents(self, arr_name, dest_name, arr_len, ty_info, copy):
199         if ty_info.c_ty == "int8_tArray":
200             if copy:
201                 return "memcpy(" + dest_name + ", " + arr_name + ".ptr, " + arr_len + ")"
202             else:
203                 return arr_name + ".ptr"
204         else:
205             return "(" + ty_info.subty.c_ty + "*) " + arr_name + ".ptr"
206     def get_native_arr_elem(self, arr_name, idxc, ty_info):
207         assert False # Only called if above is None
208     def cleanup_native_arr_ref_contents(self, arr_name, dest_name, arr_len, ty_info):
209         if ty_info.c_ty == "int8_tArray":
210             return None
211         else:
212             return None
213
214     def init_str(self, c_array_class_caches):
215         return ""
216
217     def native_c_unitary_enum_map(self, struct_name, variants):
218         out_c = "static inline " + struct_name + " " + struct_name + "_from_js(int32_t ord) {\n"
219         out_c = out_c + "\tswitch (ord) {\n"
220         ord_v = 0
221
222         out_typescript_enum_fields = ""
223
224         for var in variants:
225             out_c = out_c + "\t\tcase %d: return %s;\n" % (ord_v, var)
226             ord_v = ord_v + 1
227             out_typescript_enum_fields += f"{var},\n\t\t\t\t"
228         out_c = out_c + "\t}\n"
229         out_c = out_c + "\tabort();\n"
230         out_c = out_c + "}\n"
231
232         out_c = out_c + "static inline int32_t " + struct_name + "_to_js(" + struct_name + " val) {\n"
233         out_c = out_c + "\tswitch (val) {\n"
234         ord_v = 0
235         for var in variants:
236             out_c = out_c + "\t\tcase " + var + ": return %d;\n" % ord_v
237             ord_v = ord_v + 1
238         out_c = out_c + "\t\tdefault: abort();\n"
239         out_c = out_c + "\t}\n"
240         out_c = out_c + "}\n"
241
242         out_typescript_enum = f"""
243             export enum {struct_name} {{
244                 {out_typescript_enum_fields}
245             }}
246         """
247
248         return (out_c, out_typescript_enum, "")
249
250     def c_unitary_enum_to_native_call(self, ty_info):
251         return (ty_info.rust_obj + "_to_js(", ")")
252     def native_unitary_enum_to_c_call(self, ty_info):
253         return (ty_info.rust_obj + "_from_js(", ")")
254
255     def c_complex_enum_pass_ty(self, struct_name):
256         return "uint32_t"
257
258     def c_constr_native_complex_enum(self, struct_name, variant, c_params):
259         ret = "0 /* " + struct_name + " - " + variant + " */"
260         for param in c_params:
261             ret = ret + "; (void) " + param
262         return ret
263
264     def native_c_map_trait(self, struct_name, field_var_conversions, field_function_lines):
265         out_java = "out_java:native_c_map_trait"
266         out_java_trait = "out_java_trait:native_c_map_trait"
267         out_c = "out_c:native_c_map_trait"
268
269
270         out_java_trait = ""
271         out_java = ""
272
273         constructor_arguments = ""
274         super_instantiator = ""
275         pointer_to_adder = ""
276         impl_constructor_arguments = ""
277         for var in field_var_conversions:
278             if isinstance(var, ConvInfo):
279                 constructor_arguments += f", {first_to_lower(var.arg_name)}?: {var.java_hu_ty}"
280                 impl_constructor_arguments += f", {var.arg_name}: {var.java_hu_ty}"
281                 if var.from_hu_conv is not None:
282                     super_instantiator += ", " + var.from_hu_conv[0]
283                     if var.from_hu_conv[1] != "":
284                         pointer_to_adder += var.from_hu_conv[1] + ";\n"
285                 else:
286                     super_instantiator += ", " + first_to_lower(var.arg_name)
287             else:
288                 constructor_arguments += f", {first_to_lower(var[1])}?: bindings.{var[0]}"
289                 super_instantiator += ", " + first_to_lower(var[1])
290                 pointer_to_adder += "this.ptrs_to.push(" + first_to_lower(var[1]) + ");\n"
291                 impl_constructor_arguments += f", {first_to_lower(var[1])}_impl: {var[0].replace('LDK', '')}.{var[0].replace('LDK', '')}Interface"
292
293         # BUILD INTERFACE METHODS
294         out_java_interface = ""
295         out_interface_implementation_overrides = ""
296         java_methods = []
297         for fn_line in field_function_lines:
298             java_method_descriptor = ""
299             if fn_line.fn_name != "free" and fn_line.fn_name != "clone":
300                 out_java_interface += fn_line.fn_name + "("
301                 out_interface_implementation_overrides += f"{fn_line.fn_name} ("
302
303                 for idx, arg_conv_info in enumerate(fn_line.args_ty):
304                     if idx >= 1:
305                         out_java_interface += ", "
306                         out_interface_implementation_overrides += ", "
307                     out_java_interface += f"{arg_conv_info.arg_name}: {arg_conv_info.java_hu_ty}"
308                     out_interface_implementation_overrides += f"{arg_conv_info.arg_name}: {arg_conv_info.java_ty}"
309                     java_method_descriptor += arg_conv_info.java_fn_ty_arg
310                 out_java_interface += f"): {fn_line.ret_ty_info.java_hu_ty};\n\t\t\t\t"
311                 java_method_descriptor += ")" + fn_line.ret_ty_info.java_fn_ty_arg
312                 java_methods.append((fn_line.fn_name, java_method_descriptor))
313
314                 out_interface_implementation_overrides += f"): {fn_line.ret_ty_info.java_ty} {{\n"
315
316                 interface_method_override_inset = "\t\t\t\t\t\t"
317                 interface_implementation_inset = "\t\t\t\t\t\t\t"
318                 for arg_info in fn_line.args_ty:
319                     if arg_info.to_hu_conv is not None:
320                         out_interface_implementation_overrides += interface_implementation_inset + arg_info.to_hu_conv.replace("\n", "\n\t\t\t\t") + "\n"
321
322                 if fn_line.ret_ty_info.java_ty != "void":
323                     out_interface_implementation_overrides += interface_implementation_inset + fn_line.ret_ty_info.java_hu_ty + " ret = arg." + fn_line.fn_name + "("
324                 else:
325                     out_interface_implementation_overrides += f"{interface_implementation_inset}arg." + fn_line.fn_name + "("
326
327                 for idx, arg_info in enumerate(fn_line.args_ty):
328                     if idx != 0:
329                         out_interface_implementation_overrides += ", "
330                     if arg_info.to_hu_conv_name is not None:
331                         out_interface_implementation_overrides += arg_info.to_hu_conv_name
332                     else:
333                         out_interface_implementation_overrides += arg_info.arg_name
334
335                 out_interface_implementation_overrides += ");\n"
336                 if fn_line.ret_ty_info.java_ty != "void":
337                     if fn_line.ret_ty_info.from_hu_conv is not None:
338                         out_interface_implementation_overrides = out_interface_implementation_overrides + "\t\t\t\t" + f"result: {fn_line.ret_ty_info.java_ty} = " + fn_line.ret_ty_info.from_hu_conv[0] + ";\n"
339                         if fn_line.ret_ty_info.from_hu_conv[1] != "":
340                             out_interface_implementation_overrides = out_interface_implementation_overrides + "\t\t\t\t" + fn_line.ret_ty_info.from_hu_conv[1].replace("this", "impl_holder.held") + ";\n"
341                         #if fn_line.ret_ty_info.rust_obj in result_types:
342                         # XXX: We need to handle this in conversion logic so that its cross-language!
343                         # Avoid double-free by breaking the result - we should learn to clone these and then we can be safe instead
344                         #    out_interface_implementation_overrides = out_interface_implementation_overrides + "\t\t\t\tret.ptr = 0;\n"
345                         out_interface_implementation_overrides = out_interface_implementation_overrides + "\t\t\t\treturn result;\n"
346                     else:
347                         out_interface_implementation_overrides = out_interface_implementation_overrides + "\t\t\t\treturn ret;\n"
348                 out_interface_implementation_overrides += f"{interface_method_override_inset}}},\n\n{interface_method_override_inset}"
349
350         trait_constructor_arguments = ""
351         for var in field_var_conversions:
352             if isinstance(var, ConvInfo):
353                 trait_constructor_arguments += ", " + var.arg_name
354             else:
355                 trait_constructor_arguments += ", " + var[1] + ".new_impl(" + var[1] + "_impl).bindings_instance"
356
357
358
359         out_java_trait = f"""
360             {self.hu_struct_file_prefix}
361             
362             export class {struct_name.replace("LDK","")} extends CommonBase {{
363             
364                 bindings_instance?: bindings.{struct_name};
365                 
366                 constructor(ptr?: number, arg?: bindings.{struct_name}{constructor_arguments}) {{
367                     if (Number.isFinite(ptr)) {{
368                                         super(ptr);
369                                         this.bindings_instance = null;
370                                     }} else {{
371                                         // TODO: private constructor instantiation
372                                         super(bindings.{struct_name}_new(arg{super_instantiator}));
373                                         this.ptrs_to.push(arg);
374                                         {pointer_to_adder}
375                                     }}
376                 }}
377                 
378                 protected finalize() {{
379                     if (this.ptr != 0) {{ 
380                         bindings.{struct_name.replace("LDK","")}_free(this.ptr); 
381                     }} 
382                     super.finalize();
383                 }}
384                 
385                 static new_impl(arg: {struct_name.replace("LDK", "")}Interface{impl_constructor_arguments}): {struct_name.replace("LDK", "")} {{
386                     const impl_holder: {struct_name}Holder = new {struct_name}Holder();
387                     let structImplementation = <bindings.{struct_name}>{{
388                     
389                         // todo: in-line interface filling
390                         
391                         {out_interface_implementation_overrides}
392                     }};
393                     impl_holder.held = new {struct_name.replace("LDK", "")} (null, structImplementation{trait_constructor_arguments});
394                 }}
395                 
396             }}
397             
398             export interface {struct_name.replace("LDK", "")}Interface {{
399                 {out_java_interface}
400             }}
401             
402             class {struct_name}Holder {{
403                 held: {struct_name.replace("LDK", "")};
404             }}
405             
406         """
407
408
409
410         out_java = out_java + "\tpublic interface " + struct_name + " {\n"
411         java_meths = []
412         for fn_line in field_function_lines:
413             java_meth_descr = "("
414             if fn_line.fn_name != "free" and fn_line.fn_name != "clone":
415                 out_java = out_java + "\t\t " + fn_line.ret_ty_info.java_ty + " " + fn_line.fn_name + "("
416
417                 for idx, arg_conv_info in enumerate(fn_line.args_ty):
418                     if idx >= 1:
419                         out_java = out_java + ", "
420                         # out_java_trait = out_java_trait + ", "
421                     out_java = out_java + arg_conv_info.java_ty + " " + arg_conv_info.arg_name
422                     # out_java_trait = out_java_trait + arg_conv_info.java_hu_ty + " " + arg_conv_info.arg_name
423
424                 out_java = out_java + ");\n"
425                 # out_java_trait = out_java_trait + ");\n"
426
427
428         out_java = out_java + "\t}\n"
429
430         out_java = out_java + "\tpublic static native long " + struct_name + "_new(" + struct_name + " impl"
431         for var in field_var_conversions:
432             if isinstance(var, ConvInfo):
433                 out_java = out_java + ", " + var.java_ty + " " + var.arg_name
434             else:
435                 out_java = out_java + ", " + var[0] + " " + var[1]
436         out_java = out_java + ");\n"
437         out_java = out_java + "\tpublic static native " + struct_name + " " + struct_name + "_get_obj_from_jcalls(long val);\n"
438
439         # Now that we've written out our java code (and created java_meths), generate C
440         out_c = "typedef struct " + struct_name + "_JCalls {\n"
441         out_c = out_c + "\tatomic_size_t refcnt;\n"
442         out_c = out_c + "\tJavaVM *vm;\n"
443         out_c = out_c + "\tjweak o;\n"
444         for var in field_var_conversions:
445             if isinstance(var, ConvInfo):
446                 # We're a regular ol' field
447                 pass
448             else:
449                 # We're a supertrait
450                 out_c = out_c + "\t" + var[0] + "_JCalls* " + var[1] + ";\n"
451         for fn in field_function_lines:
452             if fn.fn_name != "free" and fn.fn_name != "clone":
453                 out_c = out_c + "\tjmethodID " + fn.fn_name + "_meth;\n"
454         out_c = out_c + "} " + struct_name + "_JCalls;\n"
455
456         for fn_line in field_function_lines:
457             if fn_line.fn_name == "free":
458                 out_c = out_c + "static void " + struct_name + "_JCalls_free(void* this_arg) {\n"
459                 out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) this_arg;\n"
460                 out_c = out_c + "\tif (atomic_fetch_sub_explicit(&j_calls->refcnt, 1, memory_order_acquire) == 1) {\n"
461                 out_c = out_c + "\t\tJNIEnv *env;\n"
462                 out_c = out_c + "\t\tDO_ASSERT((*j_calls->vm)->GetEnv(j_calls->vm, (void**)&env, JNI_VERSION_1_8) == JNI_OK);\n"
463                 out_c = out_c + "\t\t(*env)->DeleteWeakGlobalRef(env, j_calls->o);\n"
464                 out_c = out_c + "\t\tFREE(j_calls);\n"
465                 out_c = out_c + "\t}\n}\n"
466
467         for idx, fn_line in enumerate(field_function_lines):
468             if fn_line.fn_name != "free" and fn_line.fn_name != "clone":
469                 assert fn_line.ret_ty_info.ty_info.get_full_rust_ty()[1] == ""
470                 out_c = out_c + fn_line.ret_ty_info.ty_info.get_full_rust_ty()[0] + " " + fn_line.fn_name + "_jcall("
471                 if fn_line.self_is_const:
472                     out_c = out_c + "const void* this_arg"
473                 else:
474                     out_c = out_c + "void* this_arg"
475
476                 for idx, arg in enumerate(fn_line.args_ty):
477                     out_c = out_c + ", " + arg.ty_info.get_full_rust_ty()[0] + " " + arg.arg_name + arg.ty_info.get_full_rust_ty()[1]
478
479                 out_c = out_c + ") {\n"
480                 out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) this_arg;\n"
481                 out_c = out_c + "\tJNIEnv *env;\n"
482                 out_c = out_c + "\tDO_ASSERT((*j_calls->vm)->GetEnv(j_calls->vm, (void**)&env, JNI_VERSION_1_8) == JNI_OK);\n"
483
484                 for arg_info in fn_line.args_ty:
485                     if arg_info.ret_conv is not None:
486                         out_c = out_c + "\t" + arg_info.ret_conv[0].replace('\n', '\n\t')
487                         out_c = out_c + arg_info.arg_name
488                         out_c = out_c + arg_info.ret_conv[1].replace('\n', '\n\t') + "\n"
489
490                 out_c = out_c + "\tjobject obj = (*env)->NewLocalRef(env, j_calls->o);\n\tCHECK(obj != NULL);\n"
491                 if fn_line.ret_ty_info.c_ty.endswith("Array"):
492                     out_c = out_c + "\t" + fn_line.ret_ty_info.c_ty + " arg = (*env)->CallObjectMethod(env, obj, j_calls->" + fn_line.fn_name + "_meth"
493                 elif not fn_line.ret_ty_info.passed_as_ptr:
494                     out_c = out_c + "\treturn (*env)->Call" + fn_line.ret_ty_info.java_ty.title() + "Method(env, obj, j_calls->" + fn_line.fn_name + "_meth"
495                 else:
496                     out_c = out_c + "\t" + fn_line.ret_ty_info.rust_obj + "* ret = (" + fn_line.ret_ty_info.rust_obj + "*)(*env)->CallLongMethod(env, obj, j_calls->" + fn_line.fn_name + "_meth"
497
498                 for idx, arg_info in enumerate(fn_line.args_ty):
499                     if arg_info.ret_conv is not None:
500                         out_c = out_c + ", " + arg_info.ret_conv_name
501                     else:
502                         out_c = out_c + ", " + arg_info.arg_name
503                 out_c = out_c + ");\n"
504                 if fn_line.ret_ty_info.arg_conv is not None:
505                     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"
506
507                 out_c = out_c + "}\n"
508
509         # Write out a clone function whether we need one or not, as we use them in moving to rust
510         out_c = out_c + "static void* " + struct_name + "_JCalls_clone(const void* this_arg) {\n"
511         out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) this_arg;\n"
512         out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->refcnt, 1, memory_order_release);\n"
513         for var in field_var_conversions:
514             if not isinstance(var, ConvInfo):
515                 out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->" + var[1] + "->refcnt, 1, memory_order_release);\n"
516         out_c = out_c + "\treturn (void*) this_arg;\n"
517         out_c = out_c + "}\n"
518
519         out_c = out_c + "static inline " + struct_name + " " + struct_name + "_init (" + self.c_fn_args_pfx + ", jobject o"
520         for var in field_var_conversions:
521             if isinstance(var, ConvInfo):
522                 out_c = out_c + ", " + var.c_ty + " " + var.arg_name
523             else:
524                 out_c = out_c + ", jobject " + var[1]
525         out_c = out_c + ") {\n"
526
527         out_c = out_c + "\tjclass c = (*env)->GetObjectClass(env, o);\n"
528         out_c = out_c + "\tCHECK(c != NULL);\n"
529         out_c = out_c + "\t" + struct_name + "_JCalls *calls = MALLOC(sizeof(" + struct_name + "_JCalls), \"" + struct_name + "_JCalls\");\n"
530         out_c = out_c + "\tatomic_init(&calls->refcnt, 1);\n"
531         out_c = out_c + "\tDO_ASSERT((*env)->GetJavaVM(env, &calls->vm) == 0);\n"
532         out_c = out_c + "\tcalls->o = (*env)->NewWeakGlobalRef(env, o);\n"
533
534         for (fn_name, java_meth_descr) in java_meths:
535             if fn_name != "free" and fn_name != "clone":
536                 out_c = out_c + "\tcalls->" + fn_name + "_meth = (*env)->GetMethodID(env, c, \"" + fn_name + "\", \"" + java_meth_descr + "\");\n"
537                 out_c = out_c + "\tCHECK(calls->" + fn_name + "_meth != NULL);\n"
538
539         for var in field_var_conversions:
540             if isinstance(var, ConvInfo) and var.arg_conv is not None:
541                 out_c = out_c + "\n\t" + var.arg_conv.replace("\n", "\n\t") +"\n"
542         out_c = out_c + "\n\t" + struct_name + " ret = {\n"
543         out_c = out_c + "\t\t.this_arg = (void*) calls,\n"
544         for fn_line in field_function_lines:
545             if fn_line.fn_name != "free" and fn_line.fn_name != "clone":
546                 out_c = out_c + "\t\t." + fn_line.fn_name + " = " + fn_line.fn_name + "_jcall,\n"
547             elif fn_line.fn_name == "free":
548                 out_c = out_c + "\t\t.free = " + struct_name + "_JCalls_free,\n"
549             else:
550                 out_c = out_c + "\t\t.clone = " + struct_name + "_JCalls_clone,\n"
551         for var in field_var_conversions:
552             if isinstance(var, ConvInfo):
553                 if var.arg_conv_name is not None:
554                     out_c = out_c + "\t\t." + var.arg_name + " = " + var.arg_conv_name + ",\n"
555                     out_c = out_c + "\t\t.set_" + var.arg_name + " = NULL,\n"
556                 else:
557                     out_c = out_c + "\t\t." + var.var_name + " = " + var.var_name + ",\n"
558                     out_c = out_c + "\t\t.set_" + var.var_name + " = NULL,\n"
559             else:
560                 out_c = out_c + "\t\t." + var[1] + " = " + var[0] + "_init(env, clz, " + var[1] + "),\n"
561         out_c = out_c + "\t};\n"
562         for var in field_var_conversions:
563             if not isinstance(var, ConvInfo):
564                 out_c = out_c + "\tcalls->" + var[1] + " = ret." + var[1] + ".this_arg;\n"
565         out_c = out_c + "\treturn ret;\n"
566         out_c = out_c + "}\n"
567
568         out_c = out_c + self.c_fn_ty_pfx + "long " + self.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1new (" + self.c_fn_args_pfx + ", jobject o"
569         for var in field_var_conversions:
570             if isinstance(var, ConvInfo):
571                 out_c = out_c + ", " + var.c_ty + " " + var.arg_name
572             else:
573                 out_c = out_c + ", jobject " + var[1]
574         out_c = out_c + ") {\n"
575         out_c = out_c + "\t" + struct_name + " *res_ptr = MALLOC(sizeof(" + struct_name + "), \"" + struct_name + "\");\n"
576         out_c = out_c + "\t*res_ptr = " + struct_name + "_init(env, clz, o"
577         for var in field_var_conversions:
578             if isinstance(var, ConvInfo):
579                 out_c = out_c + ", " + var.arg_name
580             else:
581                 out_c = out_c + ", " + var[1]
582         out_c = out_c + ");\n"
583         out_c = out_c + "\treturn (long)res_ptr;\n"
584         out_c = out_c + "}\n"
585
586         out_c = out_c + self.c_fn_ty_pfx + "jobject " + self.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1get_1obj_1from_1jcalls (" + self.c_fn_args_pfx + ", " + self.ptr_c_ty + " val) {\n"
587         out_c = out_c + "\tjobject ret = (*env)->NewLocalRef(env, ((" + struct_name + "_JCalls*)val)->o);\n"
588         out_c = out_c + "\tCHECK(ret != NULL);\n"
589         out_c = out_c + "\treturn ret;\n"
590         out_c = out_c + "}\n"
591
592
593         return (out_java, out_java_trait, out_c)
594
595     def map_complex_enum(self, struct_name, variant_list, camel_to_snake):
596         java_hu_type = struct_name.replace("LDK", "")
597
598         out_java_enum = ""
599         out_java = ""
600         out_c = ""
601
602         out_java_enum += (self.hu_struct_file_prefix)
603         out_java_enum += ("export default class " + java_hu_type + " extends CommonBase {\n")
604         out_java_enum += ("\tprotected constructor(_dummy: object, ptr: number) { super(ptr); }\n")
605         out_java_enum += ("\tprotected finalize() {\n")
606         out_java_enum += ("\t\tsuper.finalize();\n")
607         out_java_enum += ("\t\tif (this.ptr != 0) { bindings." + java_hu_type + "_free(this.ptr); }\n")
608         out_java_enum += ("\t}\n")
609         out_java_enum += f"\tstatic constr_from_ptr(ptr: number): {java_hu_type} {{\n"
610         out_java_enum += (f"\t\tconst raw_val: bindings.{struct_name} = bindings." + struct_name + "_ref_from_ptr(ptr);\n")
611         java_hu_subclasses = ""
612
613         out_java +=  ("\tpublic static class " + struct_name + " {\n")
614         out_java +=  ("\t\tprivate " + struct_name + "() {}\n")
615         for var in variant_list:
616             out_java +=  ("\t\texport class " + var.var_name + " extends " + struct_name + " {\n")
617             java_hu_subclasses = java_hu_subclasses + "export class " + var.var_name + " extends " + java_hu_type + " {\n"
618             out_java_enum += ("\t\tif (raw_val instanceof bindings." + struct_name + "." + var.var_name + ") {\n")
619             out_java_enum += ("\t\t\treturn new " + var.var_name + "(this.ptr, raw_val);\n")
620             init_meth_params = ""
621             init_meth_body = ""
622             hu_conv_body = ""
623             for idx, field_ty in enumerate(var.fields):
624                 out_java += ("\t\t\tpublic " + field_ty.java_ty + " " + field_ty.arg_name + ";\n")
625                 java_hu_subclasses = java_hu_subclasses + "\tpublic " + field_ty.arg_name + f": {field_ty.java_hu_ty};\n"
626                 if field_ty.to_hu_conv is not None:
627                     hu_conv_body = hu_conv_body + "\t\tconst " + field_ty.arg_name + f": {field_ty.java_ty} = obj." + field_ty.arg_name + ";\n"
628                     hu_conv_body = hu_conv_body + "\t\t" + field_ty.to_hu_conv.replace("\n", "\n\t\t\t") + "\n"
629                     hu_conv_body = hu_conv_body + "\t\tthis." + field_ty.arg_name + " = " + field_ty.to_hu_conv_name + ";\n"
630                 else:
631                     hu_conv_body = hu_conv_body + "\t\tthis." + field_ty.arg_name + " = obj." + field_ty.arg_name + ";\n"
632                 if idx > 0:
633                     init_meth_params = init_meth_params + ", "
634                 init_meth_params = init_meth_params + field_ty.java_ty + " " + field_ty.arg_name
635                 init_meth_body = init_meth_body + "this." + field_ty.arg_name + " = " + field_ty.arg_name + "; "
636             out_java +=  ("\t\t\t" + var.var_name + "(" + init_meth_params + ") { ")
637             out_java +=  (init_meth_body)
638             out_java +=  ("}\n")
639             out_java += ("\t\t}\n")
640             out_java_enum += ("\t\t}\n")
641             java_hu_subclasses = java_hu_subclasses + "\tprivate constructor(ptr: number, obj: bindings." + struct_name + "." + var.var_name + ") {\n\t\tsuper(null, ptr);\n"
642             java_hu_subclasses = java_hu_subclasses + hu_conv_body
643             java_hu_subclasses = java_hu_subclasses + "\t}\n}\n"
644         out_java += ("\t\tstatic native void init();\n")
645         out_java += ("\t}\n")
646         out_java_enum += ("\t\tthrow new Error('oops, this should be unreachable'); // Unreachable without extending the (internal) bindings interface\n\t}\n\n")
647         out_java += ("\tstatic { " + struct_name + ".init(); }\n")
648         out_java += ("\tpublic static native " + struct_name + " " + struct_name + "_ref_from_ptr(long ptr);\n");
649
650         out_c += (self.c_fn_ty_pfx + self.c_complex_enum_pass_ty(struct_name) + " " + self.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1ref_1from_1ptr (" + self.c_fn_args_pfx + ", " + self.ptr_c_ty + " ptr) {\n")
651         out_c += ("\t" + struct_name + " *obj = (" + struct_name + "*)ptr;\n")
652         out_c += ("\tswitch(obj->tag) {\n")
653         for var in variant_list:
654             out_c += ("\t\tcase " + struct_name + "_" + var.var_name + ": {\n")
655             c_params = []
656             for idx, field_map in enumerate(var.fields):
657                 if field_map.ret_conv is not None:
658                     out_c += ("\t\t\t" + field_map.ret_conv[0].replace("\n", "\n\t\t\t"))
659                     out_c += ("obj->" + camel_to_snake(var.var_name) + "." + field_map.arg_name)
660                     out_c += (field_map.ret_conv[1].replace("\n", "\n\t\t\t") + "\n")
661                     c_params.append(field_map.ret_conv_name)
662                 else:
663                     c_params.append("obj->" + camel_to_snake(var.var_name) + "." + field_map.arg_name)
664             out_c += ("\t\t\treturn " + self.c_constr_native_complex_enum(struct_name, var.var_name, c_params) + ";\n")
665             out_c += ("\t\t}\n")
666         out_c += ("\t\tdefault: abort();\n")
667         out_c += ("\t}\n}\n")
668         out_java_enum += ("}\n")
669         out_java_enum += (java_hu_subclasses)
670         return (out_java, out_java_enum, out_c)