Add deserialization/init helper class that overtorment asked for
[ldk-java] / genbindings.py
1 #!/usr/bin/env python3
2 import sys, re
3
4 if len(sys.argv) < 7:
5     print("USAGE: /path/to/lightning.h /path/to/bindings/output /path/to/bindings/ /path/to/bindings/output.c debug lang")
6     sys.exit(1)
7
8 if sys.argv[5] == "false":
9     DEBUG = False
10 elif sys.argv[5] == "true":
11     DEBUG = True
12 else:
13     print("debug should be true or false and indicates whether to track allocations and ensure we don't leak")
14     sys.exit(1)
15
16 target = None
17 if sys.argv[6] == "java" or sys.argv[6] == "android":
18     import java_strings
19     from java_strings import Consts
20     target = java_strings.Target.JAVA
21     if sys.argv[6] == "android":
22         target = java_strings.Target.ANDROID
23 elif sys.argv[6] == "typescript":
24     import typescript_strings
25     from typescript_strings import Consts
26     target = typescript_strings.Target.NODEJS
27     if len(sys.argv) == 8 and sys.argv[7] == 'browser':
28         target = typescript_strings.Target.BROWSER
29 else:
30     print("Only java or typescript can be set for lang")
31     sys.exit(1)
32
33
34 consts = Consts(DEBUG, target=target)
35
36 from bindingstypes import *
37
38 c_file = ""
39 def write_c(s):
40     global c_file
41     c_file += s
42
43 def camel_to_snake(s):
44     # Convert camel case to snake case, in a way that appears to match cbindgen
45     con = "_"
46     ret = ""
47     lastchar = ""
48     lastund = False
49     for char in s:
50         if lastchar.isupper():
51             if not char.isupper() and not lastund:
52                 ret = ret + "_"
53                 lastund = True
54             else:
55                 lastund = False
56             ret = ret + lastchar.lower()
57         else:
58             ret = ret + lastchar
59             if char.isupper() and not lastund:
60                 ret = ret + "_"
61                 lastund = True
62             else:
63                 lastund = False
64         lastchar = char
65         if char.isnumeric():
66             lastund = True
67     return (ret + lastchar.lower()).strip("_")
68
69 unitary_enums = set()
70 complex_enums = set()
71 opaque_structs = set()
72 trait_structs = set()
73 result_types = set()
74 tuple_types = {}
75
76 var_is_arr_regex = re.compile("\(\*([A-za-z0-9_]*)\)\[([a-z0-9]*)\]")
77 var_ty_regex = re.compile("([A-za-z_0-9]*)(.*)")
78 java_c_types_none_allowed = True # Unset when we do the real pass that populates the above sets
79 def java_c_types(fn_arg, ret_arr_len):
80     fn_arg = fn_arg.strip()
81     if fn_arg.startswith("MUST_USE_RES "):
82         fn_arg = fn_arg[13:]
83     is_const = False
84     if fn_arg.startswith("const "):
85         fn_arg = fn_arg[6:]
86         is_const = True
87     if fn_arg.startswith("struct "):
88         fn_arg = fn_arg[7:]
89     if fn_arg.startswith("enum "):
90         fn_arg = fn_arg[5:]
91     fn_arg = fn_arg.replace("NONNULL_PTR", "")
92
93     is_ptr = False
94     take_by_ptr = False
95     rust_obj = None
96     arr_access = None
97     java_hu_ty = None
98     if fn_arg.startswith("LDKThirtyTwoBytes"):
99         fn_arg = "uint8_t (*" + fn_arg[18:] + ")[32]"
100         assert var_is_arr_regex.match(fn_arg[8:])
101         rust_obj = "LDKThirtyTwoBytes"
102         arr_access = "data"
103     elif fn_arg.startswith("LDKPublicKey"):
104         fn_arg = "uint8_t (*" + fn_arg[13:] + ")[33]"
105         assert var_is_arr_regex.match(fn_arg[8:])
106         rust_obj = "LDKPublicKey"
107         arr_access = "compressed_form"
108     elif fn_arg.startswith("LDKSecretKey"):
109         fn_arg = "uint8_t (*" + fn_arg[13:] + ")[32]"
110         assert var_is_arr_regex.match(fn_arg[8:])
111         rust_obj = "LDKSecretKey"
112         arr_access = "bytes"
113     elif fn_arg.startswith("LDKSignature"):
114         fn_arg = "uint8_t (*" + fn_arg[13:] + ")[64]"
115         assert var_is_arr_regex.match(fn_arg[8:])
116         rust_obj = "LDKSignature"
117         arr_access = "compact_form"
118     elif fn_arg.startswith("LDKThreeBytes"):
119         fn_arg = "uint8_t (*" + fn_arg[14:] + ")[3]"
120         assert var_is_arr_regex.match(fn_arg[8:])
121         rust_obj = "LDKThreeBytes"
122         arr_access = "data"
123     elif fn_arg.startswith("LDKFourBytes"):
124         fn_arg = "uint8_t (*" + fn_arg[13:] + ")[4]"
125         assert var_is_arr_regex.match(fn_arg[8:])
126         rust_obj = "LDKFourBytes"
127         arr_access = "data"
128     elif fn_arg.startswith("LDKSixteenBytes"):
129         fn_arg = "uint8_t (*" + fn_arg[16:] + ")[16]"
130         assert var_is_arr_regex.match(fn_arg[8:])
131         rust_obj = "LDKSixteenBytes"
132         arr_access = "data"
133     elif fn_arg.startswith("LDKTenBytes"):
134         fn_arg = "uint8_t (*" + fn_arg[12:] + ")[10]"
135         assert var_is_arr_regex.match(fn_arg[8:])
136         rust_obj = "LDKTenBytes"
137         arr_access = "data"
138     elif fn_arg.startswith("LDKu8slice"):
139         fn_arg = "uint8_t (*" + fn_arg[11:] + ")[datalen]"
140         assert var_is_arr_regex.match(fn_arg[8:])
141         rust_obj = "LDKu8slice"
142         arr_access = "data"
143     elif fn_arg.startswith("LDKCVec_u8Z"):
144         fn_arg = "uint8_t (*" + fn_arg[12:] + ")[datalen]"
145         rust_obj = "LDKCVec_u8Z"
146         assert var_is_arr_regex.match(fn_arg[8:])
147         arr_access = "data"
148     elif fn_arg.startswith("LDKTransaction"):
149         fn_arg = "uint8_t (*" + fn_arg[15:] + ")[datalen]"
150         rust_obj = "LDKTransaction"
151         assert var_is_arr_regex.match(fn_arg[8:])
152         arr_access = "data"
153     elif fn_arg.startswith("LDKCVec_"):
154         is_ptr = False
155         if "*" in fn_arg:
156             fn_arg = fn_arg.replace("*", "")
157             is_ptr = True
158
159         tyn = fn_arg[8:].split(" ")
160         assert tyn[0].endswith("Z")
161         if tyn[0] == "u64Z":
162             new_arg = "uint64_t"
163         else:
164             new_arg = "LDK" + tyn[0][:-1]
165         for a in tyn[1:]:
166             new_arg = new_arg + " " + a
167         res = java_c_types(new_arg, ret_arr_len)
168         if res is None:
169             assert java_c_types_none_allowed
170             return None
171         if is_ptr:
172             res.pass_by_ref = True
173         if res.is_native_primitive or res.passed_as_ptr:
174             return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=res.java_ty + "[]", java_hu_ty=res.java_hu_ty + "[]",
175                 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=res.c_ty + "Array", passed_as_ptr=False, is_ptr=is_ptr, is_const=is_const,
176                 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
177         else:
178             return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=res.java_ty + "[]", java_hu_ty=res.java_hu_ty + "[]",
179                 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=consts.ptr_arr, passed_as_ptr=False, is_ptr=is_ptr, is_const=is_const,
180                 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
181
182     is_primitive = False
183     arr_len = None
184     mapped_type = []
185     java_type_plural = None
186     if fn_arg.startswith("void"):
187         java_ty = "void"
188         c_ty = "void"
189         fn_ty_arg = "V"
190         fn_arg = fn_arg[4:].strip()
191         is_primitive = True
192     elif fn_arg.startswith("bool"):
193         java_ty = "boolean"
194         c_ty = "jboolean"
195         fn_ty_arg = "Z"
196         fn_arg = fn_arg[4:].strip()
197         is_primitive = True
198     elif fn_arg.startswith("uint8_t"):
199         mapped_type = consts.c_type_map['uint8_t']
200         java_ty = mapped_type[0]
201         c_ty = "int8_t"
202         fn_ty_arg = "B"
203         fn_arg = fn_arg[7:].strip()
204         is_primitive = True
205     elif fn_arg.startswith("uint16_t"):
206         mapped_type = consts.c_type_map['uint16_t']
207         java_ty = mapped_type[0]
208         c_ty = "int16_t"
209         fn_ty_arg = "S"
210         fn_arg = fn_arg[8:].strip()
211         is_primitive = True
212     elif fn_arg.startswith("uint32_t"):
213         mapped_type = consts.c_type_map['uint32_t']
214         java_ty = mapped_type[0]
215         c_ty = "int32_t"
216         fn_ty_arg = "I"
217         fn_arg = fn_arg[8:].strip()
218         is_primitive = True
219     elif fn_arg.startswith("uint64_t") or fn_arg.startswith("uintptr_t"):
220         # TODO: uintptr_t is arch-dependent :(
221         mapped_type = consts.c_type_map['uint64_t']
222         java_ty = mapped_type[0]
223         fn_ty_arg = "J"
224         if fn_arg.startswith("uint64_t"):
225             c_ty = "int64_t"
226             fn_arg = fn_arg[8:].strip()
227         else:
228             c_ty = "int64_t"
229             rust_obj = "uintptr_t"
230             fn_arg = fn_arg[9:].strip()
231         is_primitive = True
232     elif is_const and fn_arg.startswith("char *"):
233         java_ty = "String"
234         c_ty = "const char*"
235         fn_ty_arg = "Ljava/lang/String;"
236         fn_arg = fn_arg[6:].strip()
237     elif fn_arg.startswith("LDKStr"):
238         java_ty = "String"
239         c_ty = "jstring"
240         fn_ty_arg = "Ljava/lang/String;"
241         fn_arg = fn_arg[6:].strip()
242         arr_access = "chars"
243         arr_len = "len"
244     else:
245         ma = var_ty_regex.match(fn_arg)
246         if ma.group(1).strip() in unitary_enums:
247             java_ty = ma.group(1).strip()
248             c_ty = consts.result_c_ty
249             fn_ty_arg = "Lorg/ldk/enums/" + ma.group(1).strip() + ";"
250             fn_arg = ma.group(2).strip()
251             rust_obj = ma.group(1).strip()
252         elif ma.group(1).strip().startswith("LDKC2Tuple"):
253             c_ty = consts.ptr_c_ty
254             java_ty = consts.ptr_native_ty
255             java_hu_ty = "TwoTuple<"
256             if not ma.group(1).strip() in tuple_types:
257                 assert java_c_types_none_allowed
258                 return None
259             for idx, ty_info in enumerate(tuple_types[ma.group(1).strip()][0]):
260                 if idx != 0:
261                     java_hu_ty = java_hu_ty + ", "
262                 if ty_info.is_native_primitive:
263                     if ty_info.java_hu_ty == "int":
264                         java_hu_ty = java_hu_ty + "Integer" # Java concrete integer type is Integer, not Int
265                     else:
266                         java_hu_ty = java_hu_ty + ty_info.java_hu_ty.title() # If we're a primitive, capitalize the first letter
267                 else:
268                     java_hu_ty = java_hu_ty + ty_info.java_hu_ty
269             java_hu_ty = java_hu_ty + ">"
270             fn_ty_arg = "J"
271             fn_arg = ma.group(2).strip()
272             rust_obj = ma.group(1).strip()
273             take_by_ptr = True
274         elif ma.group(1).strip().startswith("LDKC3Tuple"):
275             c_ty = consts.ptr_c_ty
276             java_ty = consts.ptr_native_ty
277             java_hu_ty = "ThreeTuple<"
278             if not ma.group(1).strip() in tuple_types:
279                 assert java_c_types_none_allowed
280                 return None
281             for idx, ty_info in enumerate(tuple_types[ma.group(1).strip()][0]):
282                 if idx != 0:
283                     java_hu_ty = java_hu_ty + ", "
284                 if ty_info.is_native_primitive:
285                     if ty_info.java_hu_ty == "int":
286                         java_hu_ty = java_hu_ty + "Integer" # Java concrete integer type is Integer, not Int
287                     else:
288                         java_hu_ty = java_hu_ty + ty_info.java_hu_ty.title() # If we're a primitive, capitalize the first letter
289                 else:
290                     java_hu_ty = java_hu_ty + ty_info.java_hu_ty
291             java_hu_ty = java_hu_ty + ">"
292             fn_ty_arg = "J"
293             fn_arg = ma.group(2).strip()
294             rust_obj = ma.group(1).strip()
295             take_by_ptr = True
296         else:
297             c_ty = consts.ptr_c_ty
298             java_ty = consts.ptr_native_ty
299             java_hu_ty = ma.group(1).strip().replace("LDKCResult", "Result").replace("LDK", "")
300             fn_ty_arg = "J"
301             fn_arg = ma.group(2).strip()
302             rust_obj = ma.group(1).strip()
303             take_by_ptr = True
304
305     if fn_arg.startswith(" *") or fn_arg.startswith("*"):
306         fn_arg = fn_arg.replace("*", "").strip()
307         is_ptr = True
308         c_ty = consts.ptr_c_ty
309         java_ty = consts.ptr_native_ty
310         fn_ty_arg = "J"
311
312     var_is_arr = var_is_arr_regex.match(fn_arg)
313     if var_is_arr is not None or ret_arr_len is not None:
314         assert(not take_by_ptr)
315         assert(not is_ptr)
316         # is there a special case for plurals?
317         if len(mapped_type) == 2:
318             java_ty = mapped_type[1]
319         else:
320             java_ty = java_ty + "[]"
321         c_ty = c_ty + "Array"
322         if var_is_arr is not None:
323             if var_is_arr.group(1) == "":
324                 return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty, is_const=is_const,
325                     passed_as_ptr=False, is_ptr=False, var_name="arg", arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False)
326             return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty, is_const=is_const,
327                 passed_as_ptr=False, is_ptr=False, var_name=var_is_arr.group(1), arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False)
328
329     if java_hu_ty is None:
330         java_hu_ty = java_ty
331     return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_hu_ty, java_fn_ty_arg=fn_ty_arg, c_ty=c_ty, passed_as_ptr=is_ptr or take_by_ptr,
332         is_const=is_const, is_ptr=is_ptr, var_name=fn_arg, arr_len=arr_len, arr_access=arr_access, is_native_primitive=is_primitive)
333
334 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
335 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
336 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
337 clone_fns = set()
338 constructor_fns = {}
339
340 from gen_type_mapping import TypeMappingGenerator
341 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
342
343 with open(sys.argv[1]) as in_h:
344     for line in in_h:
345         reg_fn = reg_fn_regex.match(line)
346         if reg_fn is not None:
347             if reg_fn.group(2).endswith("_clone"):
348                 clone_fns.add(reg_fn.group(2))
349             else:
350                 rty = java_c_types(reg_fn.group(1), None)
351                 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
352                     constructor_fns[rty.rust_obj] = reg_fn.group(3)
353             continue
354         arr_fn = fn_ret_arr_regex.match(line)
355         if arr_fn is not None:
356             if arr_fn.group(2).endswith("_clone"):
357                 clone_fns.add(arr_fn.group(2))
358             # No object constructors return arrays, as then they wouldn't be an object constructor
359             continue
360
361 # Define some manual clones...
362 clone_fns.add("ThirtyTwoBytes_clone")
363 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
364
365 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
366
367 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
368     util.write(consts.util_fn_pfx)
369
370 with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
371     # Map a top-level function
372     def map_fn(line, re_match, ret_arr_len, c_call_string):
373         method_return_type = re_match.group(1)
374         method_name = re_match.group(2)
375         method_comma_separated_arguments = re_match.group(3)
376         method_arguments = method_comma_separated_arguments.split(',')
377
378         is_free = method_name.endswith("_free")
379         struct_meth = method_name.split("_")[0]
380
381         return_type_info = type_mapping_generator.map_type(method_return_type, True, ret_arr_len, False, False)
382
383         argument_types = []
384         default_constructor_args = {}
385         takes_self = False
386         args_known = True
387
388         for argument_index, argument in enumerate(method_arguments):
389             argument_conversion_info = type_mapping_generator.map_type(argument, False, None, is_free, True)
390             if argument_index == 0 and argument_conversion_info.java_hu_ty == struct_meth:
391                 takes_self = True
392             if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
393                 if argument_conversion_info.rust_obj in constructor_fns:
394                     assert not is_free
395                     for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
396                         explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
397                         if explode_arg_conv.c_ty == "void":
398                             # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
399                             # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
400                             args_known = False
401                             pass
402                         if not argument_conversion_info.arg_name in default_constructor_args:
403                             default_constructor_args[argument_conversion_info.arg_name] = []
404                         default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
405             argument_types.append(argument_conversion_info)
406
407         out_java.write("\t// " + line)
408         (out_java_delta, out_c_delta, out_java_struct_delta) = consts.map_function(argument_types, c_call_string, method_name, return_type_info, struct_meth, default_constructor_args, takes_self, args_known, type_mapping_generator)
409         out_java.write(out_java_delta)
410
411         if is_free:
412             assert len(argument_types) == 1
413             assert return_type_info.c_ty == "void"
414             write_c(consts.c_fn_ty_pfx + "void " + consts.c_fn_name_define_pfx(method_name, True) + argument_types[0].c_ty + " " + argument_types[0].ty_info.var_name + ") {\n")
415             if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
416                 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
417
418             for info in argument_types:
419                 if info.arg_conv is not None:
420                     write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
421             assert c_call_string is None
422             write_c("\t" + method_name + "(")
423             if argument_types[0].arg_conv_name is not None:
424                 write_c(argument_types[0].arg_conv_name)
425             write_c(");")
426             for info in argument_types:
427                 if info.arg_conv_cleanup is not None:
428                     write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
429             write_c("\n}\n\n")
430         else:
431             write_c(out_c_delta)
432
433         out_java_struct = None
434         if ("LDK" + struct_meth in opaque_structs or "LDK" + struct_meth in trait_structs) and not is_free:
435             out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
436         elif method_name.startswith("C2Tuple_") and method_name.endswith("_read"):
437             struct_meth = method_name.rsplit("_", 1)[0]
438             out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
439         if out_java_struct is not None:
440             out_java_struct.write(out_java_struct_delta)
441
442     def map_unitary_enum(struct_name, field_lines):
443         with open(f"{sys.argv[3]}/enums/{struct_name}{consts.file_ext}", "w") as out_java_enum:
444             unitary_enums.add(struct_name)
445             for idx, struct_line in enumerate(field_lines):
446                 if idx == 0:
447                     assert(struct_line == "typedef enum %s {" % struct_name)
448                 elif idx == len(field_lines) - 3:
449                     assert(struct_line.endswith("_Sentinel,"))
450                 elif idx == len(field_lines) - 2:
451                     assert(struct_line == "} %s;" % struct_name)
452                 elif idx == len(field_lines) - 1:
453                     assert(struct_line == "")
454             (c_out, native_file_out, native_out) = consts.native_c_unitary_enum_map(struct_name, [x.strip().strip(",") for x in field_lines[1:-3]])
455             write_c(c_out)
456             out_java_enum.write(native_file_out)
457             out_java.write(native_out)
458
459     def map_complex_enum(struct_name, union_enum_items):
460         java_hu_type = struct_name.replace("LDK", "")
461         complex_enums.add(struct_name)
462
463         enum_variants = []
464         tag_field_lines = union_enum_items["field_lines"]
465         for idx, struct_line in enumerate(tag_field_lines):
466             if idx == 0:
467                 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
468             elif idx == len(tag_field_lines) - 3:
469                 assert(struct_line.endswith("_Sentinel,"))
470             elif idx == len(tag_field_lines) - 2:
471                 assert(struct_line == "} %s_Tag;" % struct_name)
472             elif idx == len(tag_field_lines) - 1:
473                 assert(struct_line == "")
474             else:
475                 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
476                 fields = []
477                 if "LDK" + variant_name in union_enum_items:
478                     enum_var_lines = union_enum_items["LDK" + variant_name]
479                     for idx, field in enumerate(enum_var_lines):
480                         if idx != 0 and idx < len(enum_var_lines) - 2:
481                             fields.append(type_mapping_generator.map_type(field.strip(' ;'), False, None, False, True))
482                         else:
483                             # TODO: Assert line format
484                             pass
485                 else:
486                     # TODO: Assert line format
487                     pass
488                 enum_variants.append(ComplexEnumVariantInfo(variant_name, fields))
489
490         with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
491             (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake)
492
493             out_java_enum.write(out_java_enum_addendum)
494             out_java.write(out_java_addendum)
495             write_c(out_c_addendum)
496
497     def map_trait(struct_name, field_var_lines, trait_fn_lines):
498         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
499             field_var_convs = []
500             for var_line in field_var_lines:
501                 if var_line.group(1) in trait_structs:
502                     field_var_convs.append((var_line.group(1), var_line.group(2)))
503                 else:
504                     field_var_convs.append(
505                         type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False))
506
507             field_fns = []
508             for fn_line in trait_fn_lines:
509                 ret_ty_info = type_mapping_generator.map_type(fn_line.group(2), True, None, False, False)
510                 is_const = fn_line.group(4) is not None
511
512                 arg_tys = []
513                 for idx, arg in enumerate(fn_line.group(5).split(',')):
514                     if arg == "":
515                         continue
516                     arg_conv_info = type_mapping_generator.map_type(arg, True, None, False, False)
517                     arg_tys.append(arg_conv_info)
518                 field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys))
519
520             (out_java_addendum, out_java_trait_addendum, out_c_addendum) = consts.native_c_map_trait(struct_name, field_var_convs, field_fns)
521             write_c(out_c_addendum)
522             out_java_trait.write(out_java_trait_addendum)
523             out_java.write(out_java_addendum)
524
525         for fn_line in trait_fn_lines:
526             # For now, just disable enabling the _call_log - we don't know how to inverse-map String
527             is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
528             if fn_line.group(3) != "free" and fn_line.group(3) != "clone" and fn_line.group(3) != "eq" and not is_log:
529                 dummy_line = fn_line.group(2) + struct_name.replace("LDK", "") + "_" + fn_line.group(3) + " " + struct_name + "* this_arg" + fn_line.group(5) + "\n"
530                 map_fn(dummy_line, re.compile("([A-Za-z_0-9]*) *([A-Za-z_0-9]*) *(.*)").match(dummy_line), None, "(this_arg_conv->" + fn_line.group(3) + ")(this_arg_conv->this_arg")
531         for idx, var_line in enumerate(field_var_lines):
532             if var_line.group(1) not in trait_structs:
533                 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
534                 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
535                 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
536                 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
537                 write_c("}\n")
538                 dummy_line = var_line.group(1) + " " + struct_name.replace("LDK", "") + "_get_" + var_line.group(2) + " " + struct_name + "* this_arg" + fn_line.group(5) + "\n"
539                 map_fn(dummy_line, re.compile("([A-Za-z_0-9]*) *([A-Za-z_0-9]*) *(.*)").match(dummy_line), None, struct_name + "_set_get_" + var_line.group(2) + "(this_arg_conv")
540
541     def map_result(struct_name, res_ty, err_ty):
542         result_types.add(struct_name)
543         human_ty = struct_name.replace("LDKCResult", "Result")
544         with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
545             out_java_struct.write(consts.hu_struct_file_prefix)
546             out_java_struct.write("public class " + human_ty + " extends CommonBase {\n")
547             out_java_struct.write("\tprivate " + human_ty + "(Object _dummy, long ptr) { super(ptr); }\n")
548             out_java_struct.write("\tprotected void finalize() throws Throwable {\n")
549             out_java_struct.write("\t\tif (ptr != 0) { bindings." + struct_name.replace("LDK","") + "_free(ptr); } super.finalize();\n")
550             out_java_struct.write("\t}\n\n")
551             out_java_struct.write("\tstatic " + human_ty + " constr_from_ptr(long ptr) {\n")
552             out_java_struct.write("\t\tif (bindings." + struct_name + "_result_ok(ptr)) {\n")
553             out_java_struct.write("\t\t\treturn new " + human_ty + "_OK(null, ptr);\n")
554             out_java_struct.write("\t\t} else {\n")
555             out_java_struct.write("\t\t\treturn new " + human_ty + "_Err(null, ptr);\n")
556             out_java_struct.write("\t\t}\n")
557             out_java_struct.write("\t}\n")
558
559             res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
560             err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
561             can_clone = True
562             if not res_map.is_native_primitive and (res_map.rust_obj.replace("LDK", "") + "_clone" not in clone_fns):
563                 can_clone = False
564             if not err_map.is_native_primitive and (err_map.rust_obj.replace("LDK", "") + "_clone" not in clone_fns):
565                 can_clone = False
566
567             out_java.write("\tpublic static native boolean " + struct_name + "_result_ok(long arg);\n")
568             write_c(consts.c_fn_ty_pfx + "jboolean " + consts.c_fn_name_define_pfx(struct_name + "_result_ok", True) + consts.ptr_c_ty + " arg) {\n")
569             write_c("\treturn ((" + struct_name + "*)arg)->result_ok;\n")
570             write_c("}\n")
571
572             out_java.write("\tpublic static native " + res_map.java_ty + " " + struct_name + "_get_ok(long arg);\n")
573             write_c(consts.c_fn_ty_pfx + res_map.c_ty + " " + consts.c_fn_name_define_pfx(struct_name + "_get_ok", True) + consts.ptr_c_ty + " arg) {\n")
574             write_c("\t" + struct_name + " *val = (" + struct_name + "*)(arg & ~1);\n")
575             write_c("\tCHECK(val->result_ok);\n\t")
576             out_java_struct.write("\tpublic static final class " + human_ty + "_OK extends " + human_ty + " {\n")
577             if res_map.ret_conv is not None:
578                 write_c(res_map.ret_conv[0].replace("\n", "\n\t") + "(*val->contents.result)")
579                 write_c(res_map.ret_conv[1].replace("\n", "\n\t") + "\n\treturn " + res_map.ret_conv_name)
580             else:
581                 write_c("return *val->contents.result")
582             write_c(";\n}\n")
583
584             if res_map.java_hu_ty != "void":
585                 out_java_struct.write("\t\tpublic final " + res_map.java_hu_ty + " res;\n")
586             out_java_struct.write("\t\tprivate " + human_ty + "_OK(Object _dummy, long ptr) {\n")
587             out_java_struct.write("\t\t\tsuper(_dummy, ptr);\n")
588             if res_map.java_hu_ty == "void":
589                 pass
590             elif res_map.to_hu_conv is not None:
591                 out_java_struct.write("\t\t\t" + res_map.java_ty + " res = bindings." + struct_name + "_get_ok(ptr);\n")
592                 out_java_struct.write("\t\t\t" + res_map.to_hu_conv.replace("\n", "\n\t\t\t"))
593                 out_java_struct.write("\n\t\t\tthis.res = " + res_map.to_hu_conv_name + ";\n")
594             else:
595                 out_java_struct.write("\t\t\tthis.res = bindings." + struct_name + "_get_ok(ptr);\n")
596             out_java_struct.write("\t\t}\n")
597             if struct_name.startswith("LDKCResult_None"):
598                 out_java_struct.write("\t\tpublic " + human_ty + "_OK() {\n\t\t\tthis(null, bindings.C" + human_ty + "_ok());\n")
599             else:
600                 out_java_struct.write("\t\tpublic " + human_ty + "_OK(" + res_map.java_hu_ty + " res) {\n")
601                 if res_map.from_hu_conv is not None:
602                     out_java_struct.write("\t\t\tthis(null, bindings.C" + human_ty + "_ok(" + res_map.from_hu_conv[0] + "));\n")
603                     if res_map.from_hu_conv[1] != "":
604                         out_java_struct.write("\t\t\t" + res_map.from_hu_conv[1].replace("\n", "\n\t\t\t") + ";\n")
605                 else:
606                     out_java_struct.write("\t\t\tthis(null, bindings.C" + human_ty + "_ok(res));\n")
607             out_java_struct.write("\t\t}\n\t}\n\n")
608
609             out_java.write("\tpublic static native " + err_map.java_ty + " " + struct_name + "_get_err(long arg);\n")
610             write_c(consts.c_fn_ty_pfx + err_map.c_ty + " " + consts.c_fn_name_define_pfx(struct_name + "_get_err", True) + consts.ptr_c_ty + " arg) {\n")
611             write_c("\t" + struct_name + " *val = (" + struct_name + "*)(arg & ~1);\n")
612             write_c("\tCHECK(!val->result_ok);\n\t")
613             out_java_struct.write("\tpublic static final class " + human_ty + "_Err extends " + human_ty + " {\n")
614             if err_map.ret_conv is not None:
615                 write_c(err_map.ret_conv[0].replace("\n", "\n\t") + "(*val->contents.err)")
616                 write_c(err_map.ret_conv[1].replace("\n", "\n\t") + "\n\treturn " + err_map.ret_conv_name)
617             else:
618                 write_c("return *val->contents.err")
619             write_c(";\n}\n")
620
621             if err_map.java_hu_ty != "void":
622                 out_java_struct.write("\t\tpublic final " + err_map.java_hu_ty + " err;\n")
623             out_java_struct.write("\t\tprivate " + human_ty + "_Err(Object _dummy, long ptr) {\n")
624             out_java_struct.write("\t\t\tsuper(_dummy, ptr);\n")
625             if err_map.java_hu_ty == "void":
626                 pass
627             elif err_map.to_hu_conv is not None:
628                 out_java_struct.write("\t\t\t" + err_map.java_ty + " err = bindings." + struct_name + "_get_err(ptr);\n")
629                 out_java_struct.write("\t\t\t" + err_map.to_hu_conv.replace("\n", "\n\t\t\t"))
630                 out_java_struct.write("\n\t\t\tthis.err = " + err_map.to_hu_conv_name + ";\n")
631             else:
632                 out_java_struct.write("\t\t\tthis.err = bindings." + struct_name + "_get_err(ptr);\n")
633             out_java_struct.write("\t\t}\n")
634
635             if struct_name.endswith("NoneZ"):
636                 out_java_struct.write("\t\tpublic " + human_ty + "_Err() {\n\t\t\tthis(null, bindings.C" + human_ty + "_err());\n")
637             else:
638                 out_java_struct.write("\t\tpublic " + human_ty + "_Err(" + err_map.java_hu_ty + " err) {\n")
639                 if err_map.from_hu_conv is not None:
640                     out_java_struct.write("\t\t\tthis(null, bindings.C" + human_ty + "_err(" + err_map.from_hu_conv[0] + "));\n")
641                     if err_map.from_hu_conv[1] != "":
642                         out_java_struct.write("\t\t\t" + err_map.from_hu_conv[1].replace("\n", "\n\t\t\t") + ";\n")
643                 else:
644                     out_java_struct.write("\t\t\tthis(null, bindings.C" + human_ty + "_err(err));\n")
645             out_java_struct.write("\t\t}\n\t}\n}\n")
646
647     def map_tuple(struct_name, field_lines):
648         out_java.write("\tpublic static native long " + struct_name + "_new(")
649         write_c(consts.c_fn_ty_pfx + consts.ptr_c_ty + " " + consts.c_fn_name_define_pfx(struct_name + "_new", len(field_lines) > 3))
650         ty_list = []
651         for idx, line in enumerate(field_lines):
652             if idx != 0 and idx < len(field_lines) - 2:
653                 ty_info = java_c_types(line.strip(';'), None)
654                 if idx != 1:
655                     out_java.write(", ")
656                     write_c(", ")
657                 e = chr(ord('a') + idx - 1)
658                 out_java.write(ty_info.java_ty + " " + e)
659                 write_c(ty_info.c_ty + " " + e)
660                 ty_list.append(ty_info)
661         tuple_types[struct_name] = (ty_list, struct_name)
662         out_java.write(");\n")
663         write_c(") {\n")
664         write_c("\t" + struct_name + "* ret = MALLOC(sizeof(" + struct_name + "), \"" + struct_name + "\");\n")
665         for idx, line in enumerate(field_lines):
666             if idx != 0 and idx < len(field_lines) - 2:
667                 ty_info = type_mapping_generator.map_type(line.strip(';'), False, None, False, False)
668                 e = chr(ord('a') + idx - 1)
669                 if ty_info.arg_conv is not None:
670                     write_c("\t" + ty_info.arg_conv.replace("\n", "\n\t"))
671                     write_c("\n\tret->" + e + " = " + ty_info.arg_conv_name + ";\n")
672                 else:
673                     write_c("\tret->" + e + " = " + e + ";\n")
674                 if ty_info.arg_conv_cleanup is not None:
675                     write_c("\t//TODO: Really need to call " + ty_info.arg_conv_cleanup + " here\n")
676         write_c("\treturn (long)ret;\n")
677         write_c("}\n")
678
679         for idx, ty_info in enumerate(ty_list):
680             e = chr(ord('a') + idx)
681             out_java.write("\tpublic static native " + ty_info.java_ty + " " + struct_name + "_get_" + e + "(long ptr);\n")
682             write_c(consts.c_fn_ty_pfx + ty_info.c_ty + " " + consts.c_fn_name_define_pfx(struct_name + "_get_" + e, True) + consts.ptr_c_ty + " ptr) {\n")
683             write_c("\t" + struct_name + " *tuple = (" + struct_name + "*)(ptr & ~1);\n")
684             conv_info = type_mapping_generator.map_type_with_info(ty_info, False, None, False, True)
685             if conv_info.ret_conv is not None:
686                 write_c("\t" + conv_info.ret_conv[0].replace("\n", "\n\t") + "tuple->" + e + conv_info.ret_conv[1].replace("\n", "\n\t") + "\n")
687                 write_c("\treturn " + conv_info.ret_conv_name + ";\n")
688             else:
689                 write_c("\treturn tuple->" + e + ";\n")
690             write_c("}\n")
691
692     out_java.write(consts.bindings_header)
693
694     with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
695         out_java_struct.write(consts.common_base)
696
697     in_block_comment = False
698     cur_block_obj = None
699
700     const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
701
702     line_indicates_result_regex = re.compile("^   union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
703     line_indicates_vec_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
704     line_indicates_opaque_regex = re.compile("^   bool is_owned;$")
705     line_indicates_trait_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
706     assert(line_indicates_trait_regex.match("   uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
707     assert(line_indicates_trait_regex.match("   struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
708     assert(line_indicates_trait_regex.match("   void *(*clone)(const void *this_arg);"))
709     assert(line_indicates_trait_regex.match("   struct LDKCVec_u8Z (*write)(const void *this_arg);"))
710     line_field_var_regex = re.compile("^   struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
711     assert(line_field_var_regex.match("   struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
712     assert(line_field_var_regex.match("   struct LDKChannelPublicKeys pubkeys;"))
713     struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
714     assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
715     assert(struct_name_regex.match("typedef enum LDKNetwork {"))
716
717     union_enum_items = {}
718     result_ptr_struct_items = {}
719     for line in in_h:
720         if in_block_comment:
721             if line.endswith("*/\n"):
722                 in_block_comment = False
723         elif cur_block_obj is not None:
724             cur_block_obj  = cur_block_obj + line
725             if line.startswith("} "):
726                 field_lines = []
727                 struct_name = None
728                 vec_ty = None
729                 obj_lines = cur_block_obj.split("\n")
730                 is_opaque = False
731                 result_contents = None
732                 is_unitary_enum = False
733                 is_union_enum = False
734                 is_union = False
735                 is_tuple = False
736                 trait_fn_lines = []
737                 field_var_lines = []
738
739                 for idx, struct_line in enumerate(obj_lines):
740                     if struct_line.strip().startswith("/*"):
741                         in_block_comment = True
742                     if in_block_comment:
743                         if struct_line.endswith("*/"):
744                             in_block_comment = False
745                     else:
746                         struct_name_match = struct_name_regex.match(struct_line)
747                         if struct_name_match is not None:
748                             struct_name = struct_name_match.group(3)
749                             if struct_name_match.group(1) == "enum":
750                                 if not struct_name.endswith("_Tag"):
751                                     is_unitary_enum = True
752                                 else:
753                                     is_union_enum = True
754                             elif struct_name_match.group(1) == "union":
755                                 is_union = True
756                         if line_indicates_opaque_regex.match(struct_line):
757                             is_opaque = True
758                         result_match = line_indicates_result_regex.match(struct_line)
759                         if result_match is not None:
760                             result_contents = result_match.group(1)
761                         vec_ty_match = line_indicates_vec_regex.match(struct_line)
762                         if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
763                             vec_ty = vec_ty_match.group(2)
764                         elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
765                             is_tuple = True
766                         trait_fn_match = line_indicates_trait_regex.match(struct_line)
767                         if trait_fn_match is not None:
768                             trait_fn_lines.append(trait_fn_match)
769                         field_var_match = line_field_var_regex.match(struct_line)
770                         if field_var_match is not None:
771                             field_var_lines.append(field_var_match)
772                         field_lines.append(struct_line)
773
774                 assert(struct_name is not None)
775                 assert(len(trait_fn_lines) == 0 or not (is_opaque or is_unitary_enum or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
776                 assert(not is_opaque or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
777                 assert(not is_unitary_enum or not (len(trait_fn_lines) != 0 or is_opaque or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
778                 assert(not is_union_enum or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_opaque or is_union or result_contents is not None or vec_ty is not None))
779                 assert(not is_union or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or result_contents is not None or vec_ty is not None))
780                 assert(result_contents is None or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or is_union or vec_ty is not None))
781                 assert(vec_ty is None or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or is_union or result_contents is not None))
782
783                 if is_opaque:
784                     opaque_structs.add(struct_name)
785                     with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
786                         out_opaque_struct_human = consts.map_opaque_struct(struct_name)
787                         out_java_struct.write(out_opaque_struct_human)
788                 elif result_contents is not None:
789                     assert result_contents in result_ptr_struct_items
790                     res_ty, err_ty = result_ptr_struct_items[result_contents]
791                     map_result(struct_name, res_ty, err_ty)
792                 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
793                     for line in field_lines:
794                         if line.endswith("*result;"):
795                             res_ty = line[:-8].strip()
796                         elif line.endswith("*err;"):
797                             err_ty = line[:-5].strip()
798                     result_ptr_struct_items[struct_name] = (res_ty, err_ty)
799                     result_types.add(struct_name[:-3])
800                 elif is_tuple:
801                     map_tuple(struct_name, field_lines)
802                 elif vec_ty is not None:
803                     ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
804                     if len(ty_info.java_fn_ty_arg) == 1: # ie we're a primitive of some form
805                         out_java.write("\tpublic static native long " + struct_name + "_new(" + ty_info.java_ty + "[] elems);\n")
806                         write_c(consts.c_fn_ty_pfx + consts.ptr_c_ty + " " + consts.c_fn_name_define_pfx(struct_name + "_new", True) + ty_info.c_ty + "Array elems) {\n")
807                         write_c("\t" + struct_name + " *ret = MALLOC(sizeof(" + struct_name + "), \"" + struct_name + "\");\n")
808                         write_c("\tret->datalen = " + consts.get_native_arr_len_call[0] + "elems" + consts.get_native_arr_len_call[1] + ";\n")
809                         write_c("\tif (ret->datalen == 0) {\n")
810                         write_c("\t\tret->data = NULL;\n")
811                         write_c("\t} else {\n")
812                         write_c("\t\tret->data = MALLOC(sizeof(" + vec_ty + ") * ret->datalen, \"" + struct_name + " Data\");\n")
813                         native_arr_ptr_call = consts.get_native_arr_ptr_call(ty_info.ty_info)
814                         write_c("\t\t" + ty_info.c_ty + " *java_elems = " + native_arr_ptr_call[0] + "elems" + native_arr_ptr_call[1] + ";\n")
815                         write_c("\t\tfor (size_t i = 0; i < ret->datalen; i++) {\n")
816                         if ty_info.arg_conv is not None:
817                             write_c("\t\t\t" + ty_info.c_ty + " arr_elem = java_elems[i];\n")
818                             write_c("\t\t\t" + ty_info.arg_conv.replace("\n", "\n\t\t\t") + "\n")
819                             write_c("\t\t\tret->data[i] = " + ty_info.arg_conv_name + ";\n")
820                             assert ty_info.arg_conv_cleanup is None
821                         else:
822                             write_c("\t\t\tret->data[i] = java_elems[i];\n")
823                         write_c("\t\t}\n")
824                         cleanup = consts.release_native_arr_ptr_call(ty_info.ty_info, "elems", "java_elems")
825                         if cleanup is not None:
826                             write_c("\t\t" + cleanup + ";\n")
827                         write_c("\t}\n")
828                         write_c("\treturn (long)ret;\n")
829                         write_c("}\n")
830
831                     if ty_info.is_native_primitive:
832                         clone_fns.add(struct_name.replace("LDK", "") + "_clone")
833                         write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
834                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
835                         write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
836                         write_c("\treturn ret;\n}\n")
837                     elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
838                         ty_name = "CVec_" + ty_info.rust_obj.replace("LDK", "") + "Z";
839                         clone_fns.add(ty_name + "_clone")
840                         write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
841                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
842                         write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
843                         write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
844                         write_c("\t}\n\treturn ret;\n}\n")
845                 elif is_union_enum:
846                     assert(struct_name.endswith("_Tag"))
847                     struct_name = struct_name[:-4]
848                     union_enum_items[struct_name] = {"field_lines": field_lines}
849                 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
850                     enum_var_name = struct_name.split("_")
851                     union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
852                 elif struct_name in union_enum_items:
853                     map_complex_enum(struct_name, union_enum_items[struct_name])
854                 elif is_unitary_enum:
855                     map_unitary_enum(struct_name, field_lines)
856                 elif len(trait_fn_lines) > 0:
857                     trait_structs.add(struct_name)
858                     map_trait(struct_name, field_var_lines, trait_fn_lines)
859                 elif struct_name == "LDKTxOut":
860                     with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
861                         out_java_struct.write(consts.hu_struct_file_prefix)
862                         out_java_struct.write("public class TxOut extends CommonBase{\n")
863                         out_java_struct.write("\tTxOut(java.lang.Object _dummy, long ptr) { super(ptr); }\n")
864                         out_java_struct.write("\tlong to_c_ptr() { return 0; }\n")
865                         out_java_struct.write("\t@Override @SuppressWarnings(\"deprecation\")\n")
866                         out_java_struct.write("\tprotected void finalize() throws Throwable {\n")
867                         out_java_struct.write("\t\tsuper.finalize();\n")
868                         out_java_struct.write("\t\tif (ptr != 0) { bindings.TxOut_free(ptr); }\n")
869                         out_java_struct.write("\t}\n")
870                         # TODO: TxOut body
871                         out_java_struct.write("}")
872                 else:
873                     pass # Everything remaining is a byte[] or some form
874                 cur_block_obj = None
875         else:
876             fn_ptr = fn_ptr_regex.match(line)
877             fn_ret_arr = fn_ret_arr_regex.match(line)
878             reg_fn = reg_fn_regex.match(line)
879             const_val = const_val_regex.match(line)
880
881             if line.startswith("#include <"):
882                 pass
883             elif line.startswith("/*"):
884                 #out_java.write("\t" + line)
885                 if not line.endswith("*/\n"):
886                     in_block_comment = True
887             elif line.startswith("typedef enum "):
888                 cur_block_obj = line
889             elif line.startswith("typedef struct "):
890                 cur_block_obj = line
891             elif line.startswith("typedef union "):
892                 cur_block_obj = line
893             elif fn_ptr is not None:
894                 map_fn(line, fn_ptr, None, None)
895             elif fn_ret_arr is not None:
896                 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None)
897             elif reg_fn is not None:
898                 map_fn(line, reg_fn, None, None)
899             elif const_val_regex is not None:
900                 # TODO Map const variables
901                 pass
902             else:
903                 assert(line == "\n")
904
905     out_java.write(consts.bindings_footer)
906     for struct_name in opaque_structs:
907         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
908             out_java_struct.write("}\n")
909     for struct_name in trait_structs:
910         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
911             out_java_struct.write("}\n")
912
913 with open(sys.argv[4], "w") as out_c:
914     out_c.write(consts.c_file_pfx)
915     out_c.write(consts.init_str())
916     out_c.write(c_file)
917 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
918     util.write(consts.util_fn_sfx)