[Java] Update LDK batteries to latest upstream API
[ldk-java] / genbindings.py
1 #!/usr/bin/env python3
2 import os, sys, re, subprocess
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, outdir=sys.argv[4])
35
36 local_git_version = os.getenv("LDK_GARBAGECOLLECTED_GIT_OVERRIDE")
37 if local_git_version is None:
38     local_git_version = subprocess.check_output(["git", "describe", '--tag', '--dirty']).decode("utf-8").strip()
39
40 from bindingstypes import *
41
42 c_file = ""
43 def write_c(s):
44     global c_file
45     c_file += s
46
47 def camel_to_snake(s):
48     # Convert camel case to snake case, in a way that appears to match cbindgen
49     con = "_"
50     ret = ""
51     lastchar = ""
52     lastund = False
53     for char in s:
54         if lastchar.isupper():
55             if not char.isupper() and not lastund:
56                 ret = ret + "_"
57                 lastund = True
58             else:
59                 lastund = False
60             ret = ret + lastchar.lower()
61         else:
62             ret = ret + lastchar
63             if char.isupper() and not lastund:
64                 ret = ret + "_"
65                 lastund = True
66             else:
67                 lastund = False
68         lastchar = char
69         if char.isnumeric():
70             lastund = True
71     return (ret + lastchar.lower()).strip("_")
72
73 def doc_to_field_nullable(doc):
74     if doc is None:
75         return False
76     for line in doc.splitlines():
77         if "Note that this (or a relevant inner pointer) may be NULL or all-0s to represent None" in line:
78             return True
79     return False
80
81 def doc_to_params_ret_nullable(doc):
82     if doc is None:
83         return (set(), False)
84     params = set()
85     ret_null = False
86     for line in doc.splitlines():
87         if "may be NULL or all-0s to represent None" not in line:
88             continue
89         if "Note that the return value" in line:
90             ret_null = True
91         elif "Note that " in line:
92             param = line.split("Note that ")[1].split(" ")[0]
93             params.add(param)
94     return (params, ret_null)
95
96 unitary_enums = set()
97 # Map from enum name to "contains trait object"
98 complex_enums = {}
99 opaque_structs = set()
100 trait_structs = {}
101 result_types = set()
102 tuple_types = {}
103
104 var_is_arr_regex = re.compile("\(\* ?([A-za-z0-9_]*)\)\[([a-z0-9]*)\]")
105 var_ty_regex = re.compile("([A-za-z_0-9]*)(.*)")
106 java_c_types_none_allowed = True # Unset when we do the real pass that populates the above sets
107 def java_c_types(fn_arg, ret_arr_len):
108     fn_arg = fn_arg.strip()
109     if fn_arg.startswith("MUST_USE_RES "):
110         fn_arg = fn_arg[13:]
111     is_const = False
112     if fn_arg.startswith("const "):
113         fn_arg = fn_arg[6:]
114         is_const = True
115     if fn_arg.startswith("struct "):
116         fn_arg = fn_arg[7:]
117     if fn_arg.startswith("enum "):
118         fn_arg = fn_arg[5:]
119     nonnull_ptr = "NONNULL_PTR" in fn_arg
120     fn_arg = fn_arg.replace("NONNULL_PTR", "")
121
122     is_ptr = False
123     take_by_ptr = False
124     rust_obj = None
125     arr_access = None
126     java_hu_ty = None
127     if fn_arg.startswith("LDKPaymentPreimage") or fn_arg.startswith("LDKPaymentSecret") or fn_arg.startswith("LDKPaymentHash"):
128         if fn_arg.startswith("LDKPaymentPreimage"):
129             fn_arg = "uint8_t (*" + fn_arg[19:] + ")[32]"
130         elif fn_arg.startswith("LDKPaymentSecret"):
131             fn_arg = "uint8_t (*" + fn_arg[17:] + ")[32]"
132         elif fn_arg.startswith("LDKPaymentHash"):
133             fn_arg = "uint8_t (*" + fn_arg[15:] + ")[32]"
134         assert var_is_arr_regex.match(fn_arg[8:])
135         rust_obj = "LDKThirtyTwoBytes"
136         arr_access = "data"
137     elif fn_arg.startswith("LDKThirtyTwoBytes"):
138         fn_arg = "uint8_t (*" + fn_arg[18:] + ")[32]"
139         assert var_is_arr_regex.match(fn_arg[8:])
140         rust_obj = "LDKThirtyTwoBytes"
141         arr_access = "data"
142     elif fn_arg.startswith("LDKTxid"):
143         fn_arg = "uint8_t (*" + fn_arg[8:] + ")[32]"
144         assert var_is_arr_regex.match(fn_arg[8:])
145         rust_obj = "LDKThirtyTwoBytes"
146         arr_access = "data"
147     elif fn_arg.startswith("LDKPublicKey"):
148         fn_arg = "uint8_t (*" + fn_arg[13:] + ")[33]"
149         assert var_is_arr_regex.match(fn_arg[8:])
150         rust_obj = "LDKPublicKey"
151         arr_access = "compressed_form"
152     elif fn_arg.startswith("LDKSecretKey"):
153         fn_arg = "uint8_t (*" + fn_arg[13:] + ")[32]"
154         assert var_is_arr_regex.match(fn_arg[8:])
155         rust_obj = "LDKSecretKey"
156         arr_access = "bytes"
157     elif fn_arg.startswith("LDKSignature"):
158         fn_arg = "uint8_t (*" + fn_arg[13:] + ")[64]"
159         assert var_is_arr_regex.match(fn_arg[8:])
160         rust_obj = "LDKSignature"
161         arr_access = "compact_form"
162     elif fn_arg.startswith("LDKRecoverableSignature"):
163         fn_arg = "uint8_t (*" + fn_arg[24:] + ")[68]"
164         assert var_is_arr_regex.match(fn_arg[8:])
165         rust_obj = "LDKRecoverableSignature"
166         arr_access = "serialized_form"
167     elif fn_arg.startswith("LDKThreeBytes"):
168         fn_arg = "uint8_t (*" + fn_arg[14:] + ")[3]"
169         assert var_is_arr_regex.match(fn_arg[8:])
170         rust_obj = "LDKThreeBytes"
171         arr_access = "data"
172     elif fn_arg.startswith("LDKFourBytes"):
173         fn_arg = "uint8_t (*" + fn_arg[13:] + ")[4]"
174         assert var_is_arr_regex.match(fn_arg[8:])
175         rust_obj = "LDKFourBytes"
176         arr_access = "data"
177     elif fn_arg.startswith("LDKSixteenBytes"):
178         fn_arg = "uint8_t (*" + fn_arg[16:] + ")[16]"
179         assert var_is_arr_regex.match(fn_arg[8:])
180         rust_obj = "LDKSixteenBytes"
181         arr_access = "data"
182     elif fn_arg.startswith("LDKTwentyBytes"):
183         fn_arg = "uint8_t (*" + fn_arg[15:] + ")[20]"
184         assert var_is_arr_regex.match(fn_arg[8:])
185         rust_obj = "LDKTwentyBytes"
186         arr_access = "data"
187     elif fn_arg.startswith("LDKTwelveBytes"):
188         fn_arg = "uint8_t (*" + fn_arg[15:] + ")[12]"
189         assert var_is_arr_regex.match(fn_arg[8:])
190         rust_obj = "LDKTwelveBytes"
191         arr_access = "data"
192     elif fn_arg.startswith("LDKu8slice"):
193         fn_arg = "uint8_t (*" + fn_arg[11:] + ")[datalen]"
194         assert var_is_arr_regex.match(fn_arg[8:])
195         rust_obj = "LDKu8slice"
196         arr_access = "data"
197     elif fn_arg.startswith("LDKCVec_u8Z"):
198         fn_arg = "uint8_t (*" + fn_arg[12:] + ")[datalen]"
199         rust_obj = "LDKCVec_u8Z"
200         assert var_is_arr_regex.match(fn_arg[8:])
201         arr_access = "data"
202     elif fn_arg.startswith("LDKTransaction ") or fn_arg == "LDKTransaction":
203         fn_arg = "uint8_t (*" + fn_arg[15:] + ")[datalen]"
204         rust_obj = "LDKTransaction"
205         assert var_is_arr_regex.match(fn_arg[8:])
206         arr_access = "data"
207     elif fn_arg.startswith("LDKCVec_"):
208         is_ptr = False
209         if "*" in fn_arg:
210             fn_arg = fn_arg.replace("*", "")
211             is_ptr = True
212
213         tyn = fn_arg[8:].split(" ")
214         assert tyn[0].endswith("Z")
215         if tyn[0] == "u64Z":
216             new_arg = "uint64_t"
217         else:
218             new_arg = "LDK" + tyn[0][:-1]
219         for a in tyn[1:]:
220             new_arg = new_arg + " " + a
221         res = java_c_types(new_arg, ret_arr_len)
222         if res is None:
223             assert java_c_types_none_allowed
224             return None
225         if is_ptr:
226             res.pass_by_ref = True
227         java_ty = consts.java_arr_ty_str(res.java_ty)
228         if res.is_native_primitive or res.passed_as_ptr:
229             return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=java_ty, java_hu_ty=res.java_hu_ty + "[]",
230                 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=res.c_ty + "Array", passed_as_ptr=False, is_ptr=is_ptr,
231                 nonnull_ptr=nonnull_ptr, is_const=is_const,
232                 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
233         else:
234             return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=java_ty, java_hu_ty=res.java_hu_ty + "[]",
235                 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=consts.ptr_arr, passed_as_ptr=False, is_ptr=is_ptr,
236                 nonnull_ptr=nonnull_ptr, is_const=is_const,
237                 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
238
239     is_primitive = False
240     contains_trait = False
241     arr_len = None
242     mapped_type = []
243     java_type_plural = None
244     arr_ty = None
245     if fn_arg.startswith("void"):
246         java_ty = "void"
247         c_ty = "void"
248         fn_ty_arg = "V"
249         fn_arg = fn_arg[4:].strip()
250         is_primitive = True
251     elif fn_arg.startswith("bool"):
252         java_ty = "boolean"
253         c_ty = "jboolean"
254         fn_ty_arg = "Z"
255         arr_ty = "bool"
256         fn_arg = fn_arg[4:].strip()
257         is_primitive = True
258     elif fn_arg.startswith("uint8_t"):
259         mapped_type = consts.c_type_map['uint8_t']
260         java_ty = mapped_type[0]
261         c_ty = "int8_t"
262         fn_ty_arg = "B"
263         arr_ty = "uint8_t"
264         fn_arg = fn_arg[7:].strip()
265         is_primitive = True
266     elif fn_arg.startswith("LDKu5"):
267         java_ty = consts.c_type_map['uint8_t'][0]
268         java_hu_ty = "UInt5"
269         rust_obj = "LDKu5"
270         c_ty = "int8_t"
271         arr_ty = "uint8_t"
272         fn_ty_arg = "B"
273         fn_arg = fn_arg[6:].strip()
274     elif fn_arg.startswith("uint16_t"):
275         mapped_type = consts.c_type_map['uint16_t']
276         java_ty = mapped_type[0]
277         c_ty = "int16_t"
278         arr_ty = "uint16_t"
279         fn_ty_arg = "S"
280         fn_arg = fn_arg[8:].strip()
281         is_primitive = True
282     elif fn_arg.startswith("uint32_t"):
283         mapped_type = consts.c_type_map['uint32_t']
284         java_ty = mapped_type[0]
285         c_ty = "int32_t"
286         arr_ty = "uint32_t"
287         fn_ty_arg = "I"
288         fn_arg = fn_arg[8:].strip()
289         is_primitive = True
290     elif fn_arg.startswith("uint64_t") or fn_arg.startswith("uintptr_t"):
291         # TODO: uintptr_t is arch-dependent :(
292         mapped_type = consts.c_type_map['uint64_t']
293         java_ty = mapped_type[0]
294         fn_ty_arg = "J"
295         if fn_arg.startswith("uint64_t"):
296             c_ty = "int64_t"
297             arr_ty = "uint64_t"
298             fn_arg = fn_arg[8:].strip()
299         else:
300             java_ty = consts.ptr_native_ty
301             c_ty = consts.ptr_c_ty
302             arr_ty = "uintptr_t"
303             rust_obj = "uintptr_t"
304             fn_arg = fn_arg[9:].strip()
305         is_primitive = True
306     elif is_const and fn_arg.startswith("char *"):
307         java_ty = consts.java_type_map["String"]
308         java_hu_ty = consts.java_hu_type_map["String"]
309         c_ty = "const char*"
310         arr_ty = "LDKStr"
311         fn_ty_arg = "Ljava/lang/String;"
312         fn_arg = fn_arg[6:].strip()
313     elif fn_arg.startswith("LDKStr"):
314         rust_obj = "LDKStr"
315         arr_ty = "LDKStr"
316         java_ty = consts.java_type_map["String"]
317         java_hu_ty = consts.java_hu_type_map["String"]
318         c_ty = "jstring"
319         fn_ty_arg = "Ljava/lang/String;"
320         fn_arg = fn_arg[6:].strip()
321         arr_access = "chars"
322         arr_len = "len"
323     elif fn_arg.startswith("LDKError ") or fn_arg == "LDKError":
324         java_ty = consts.c_type_map['uint32_t'][0]
325         java_hu_ty = "UnqualifiedError"
326         rust_obj = "LDKError"
327         c_ty = "int32_t"
328         arr_ty = "uint32_t"
329         fn_ty_arg = "I"
330         fn_arg = fn_arg[8:].strip()
331     else:
332         ma = var_ty_regex.match(fn_arg)
333         arr_ty = ma.group(1).strip()
334         if ma.group(1).strip() in unitary_enums:
335             assert ma.group(1).strip().startswith("LDK")
336             java_ty = ma.group(1).strip()[3:]
337             java_hu_ty = java_ty
338             c_ty = consts.result_c_ty
339             fn_ty_arg = "Lorg/ldk/enums/" + java_ty + ";"
340             fn_arg = ma.group(2).strip()
341             rust_obj = ma.group(1).strip()
342         else:
343             c_ty = consts.ptr_c_ty
344             java_ty = consts.ptr_native_ty
345             java_hu_ty = ma.group(1).strip()
346             java_hu_ty = java_hu_ty.replace("LDKCOption", "Option")
347             java_hu_ty = java_hu_ty.replace("LDKCResult", "Result")
348             java_hu_ty = java_hu_ty.replace("LDKC2Tuple", "TwoTuple")
349             java_hu_ty = java_hu_ty.replace("LDKC3Tuple", "ThreeTuple")
350             java_hu_ty = java_hu_ty.replace("LDK", "")
351             fn_ty_arg = "J"
352             fn_arg = ma.group(2).strip()
353             rust_obj = ma.group(1).strip()
354             if rust_obj in trait_structs:
355                 contains_trait = True
356             elif rust_obj in complex_enums:
357                 contains_trait = complex_enums[rust_obj]
358             take_by_ptr = True
359
360     if fn_arg.startswith(" *") or fn_arg.startswith("*"):
361         fn_arg = fn_arg.replace("*", "").strip()
362         is_ptr = True
363         c_ty = consts.ptr_c_ty
364         java_ty = consts.ptr_native_ty
365         fn_ty_arg = "J"
366
367     var_is_arr = var_is_arr_regex.match(fn_arg)
368     subty = None
369     if var_is_arr is not None or ret_arr_len is not None:
370         assert(not take_by_ptr)
371         assert(not is_ptr)
372         # is there a special case for plurals?
373         if len(mapped_type) == 3:
374             java_ty = mapped_type[1]
375             java_hu_ty = mapped_type[2]
376         else:
377             java_ty = java_ty + "[]"
378             java_hu_ty = java_ty
379         c_ty = c_ty + "Array"
380
381         subty = java_c_types(arr_ty, None)
382         if subty is None:
383             assert java_c_types_none_allowed
384             return None
385         if is_ptr:
386             subty.pass_by_ref = True
387
388         if var_is_arr is not None:
389             if var_is_arr.group(1) == "":
390                 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, is_const=is_const,
391                     passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name="arg", subty=subty,
392                     arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
393             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, is_const=is_const,
394                 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name=var_is_arr.group(1), subty=subty,
395                 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
396
397     if java_hu_ty is None:
398         java_hu_ty = java_ty
399     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,
400         is_const=is_const, is_ptr=is_ptr, nonnull_ptr=nonnull_ptr, var_name=fn_arg, arr_len=arr_len, arr_access=arr_access, is_native_primitive=is_primitive,
401         contains_trait=contains_trait, subty=subty)
402
403 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
404 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
405 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
406 clone_fns = set()
407 constructor_fns = {}
408
409 from gen_type_mapping import TypeMappingGenerator
410 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
411
412 with open(sys.argv[1]) as in_h:
413     for line in in_h:
414         reg_fn = reg_fn_regex.match(line)
415         if reg_fn is not None:
416             if reg_fn.group(2).endswith("_clone"):
417                 clone_fns.add(reg_fn.group(2))
418             else:
419                 rty = java_c_types(reg_fn.group(1), None)
420                 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
421                     constructor_fns[rty.rust_obj] = reg_fn.group(3)
422             continue
423         arr_fn = fn_ret_arr_regex.match(line)
424         if arr_fn is not None:
425             if arr_fn.group(2).endswith("_clone"):
426                 clone_fns.add(arr_fn.group(2))
427             # No object constructors return arrays, as then they wouldn't be an object constructor
428             continue
429
430 # Define some manual clones...
431 clone_fns.add("ThirtyTwoBytes_clone")
432 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
433
434 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
435
436 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
437     util.write(consts.util_fn_pfx)
438
439 with open(sys.argv[1]) as in_h, open(f"{sys.argv[2]}/bindings{consts.file_ext}", "w") as out_java:
440     # Map a top-level function
441     def map_fn(line, re_match, ret_arr_len, c_call_string, doc_comment):
442         map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, False)
443     def map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, force_holds_ref):
444         method_return_type = re_match.group(1)
445         method_name = re_match.group(2)
446         method_comma_separated_arguments = re_match.group(3)
447         method_arguments = method_comma_separated_arguments.split(',')
448
449         if method_name.startswith("__"):
450             return
451
452         is_free = method_name.endswith("_free")
453         if method_name.startswith("COption") or method_name.startswith("CResult"):
454             struct_meth = method_name.rsplit("Z", 1)[0][1:] + "Z"
455             expected_struct = "LDKC" + struct_meth
456             struct_meth_name = method_name[len(struct_meth) + 1:].strip("_")
457         elif method_name.startswith("C2Tuple") or method_name.startswith("C3Tuple"):
458             tuple_name = method_name.rsplit("Z", 1)[0][2:] + "Z"
459             if method_name.startswith("C2Tuple"):
460                 struct_meth = "Two" + tuple_name
461                 expected_struct = "LDKC2" + tuple_name
462             else:
463                 struct_meth = "Three" + tuple_name
464                 expected_struct = "LDKC3" + tuple_name
465             struct_meth_name = method_name[len(tuple_name) + 2:].strip("_")
466         else:
467             struct_meth = method_name.split("_")[0]
468             expected_struct = "LDK" + struct_meth
469             struct_meth_name = method_name[len(struct_meth) + 1 if len(struct_meth) != 0 else 0:].strip("_")
470
471         (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
472         if ret_nullable:
473             return_type_info = type_mapping_generator.map_nullable_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
474         else:
475             return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
476
477         if method_name.endswith("_clone") and expected_struct not in unitary_enums:
478             meth_line = "uintptr_t " + expected_struct.replace("LDK", "") + "_clone_ptr(" + expected_struct + " *NONNULL_PTR arg)"
479             write_c("static inline " + meth_line + " {\n")
480             write_c("\t" + return_type_info.ret_conv[0].replace("\n", "\n\t"))
481             write_c(method_name + "(arg)")
482             write_c(return_type_info.ret_conv[1])
483             write_c("\n\treturn " + return_type_info.ret_conv_name + ";\n}\n")
484             map_fn(meth_line + ";\n", re.compile("(uintptr_t) ([A-Za-z_0-9]*)\((.*)\)").match(meth_line), None, None, None)
485
486         argument_types = []
487         default_constructor_args = {}
488         takes_self = False
489         takes_self_ptr = False
490         args_known = True
491
492         for argument_index, argument in enumerate(method_arguments):
493             arg_ty = type_mapping_generator.java_c_types(argument, None)
494             argument_conversion_info = None
495             if argument_index == 0 and arg_ty.java_hu_ty == struct_meth:
496                 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
497                 takes_self = True
498                 if argument_conversion_info.ty_info.is_ptr:
499                     takes_self_ptr = True
500             elif arg_ty.var_name in params_nullable:
501                 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, True)
502                 if argument_conversion_info.arg_conv is not None and "WARNING" in argument_conversion_info.arg_conv:
503                     arg_ty_info = java_c_types(argument, None)
504                     print("WARNING: Remapping argument " + arg_ty_info.var_name + " of function " + method_name + " to a reference")
505                     print("    The argument appears to require a move, or not clonable, and is nullable.")
506                     print("    Normally for arguments that require a move and are not clonable, we split")
507                     print("    the argument into the type's constructor's arguments and just use those to")
508                     print("    construct a new object on the fly.")
509                     print("    However, because the object is nullable, doing so would mean we can no")
510                     print("    longer allow the user to pass null, as we now have an argument list instead.")
511                     print("    Thus, we blindly assume its really an Option<&Type> instead of an Option<Type>.")
512                     print("    It may or may not actually be a reference, but its the simplest mapping option")
513                     print("    and also the only use of this code today.")
514                     arg_ty_info.requires_clone = False
515                     argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty_info, False, None, is_free, True, True)
516                     assert argument_conversion_info.nullable
517                     assert argument_conversion_info.arg_conv is not None and "WARNING" not in argument_conversion_info.arg_conv
518             else:
519                 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
520
521             if argument_conversion_info.arg_conv is not None and "WARNING" in argument_conversion_info.arg_conv:
522                 if argument_conversion_info.rust_obj in constructor_fns:
523                     assert not is_free
524                     for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
525                         explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
526                         if explode_arg_conv.c_ty == "void":
527                             # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
528                             # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
529                             args_known = False
530                             pass
531                         if not argument_conversion_info.arg_name in default_constructor_args:
532                             default_constructor_args[argument_conversion_info.arg_name] = []
533                         default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
534             argument_types.append(argument_conversion_info)
535
536         if not takes_self and return_type_info.java_hu_ty != struct_meth:
537             if not return_type_info.java_hu_ty.startswith("Result_" + struct_meth):
538                 struct_meth_name = method_name
539                 struct_meth = ""
540                 expected_struct = ""
541
542         impl_on_struct = (expected_struct in opaque_structs or expected_struct in trait_structs or
543             expected_struct in complex_enums or expected_struct in complex_enums or
544             expected_struct in result_types or expected_struct in tuple_types) and not is_free
545         impl_on_utils = not impl_on_struct and (not is_free and not method_name.endswith("_clone") and
546             not method_name.startswith("TxOut") and
547             not method_name.startswith("_") and
548             method_name != "check_platform" and method_name != "Result_read" and
549             not expected_struct in unitary_enums and
550             ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
551               or method_name.endswith("_read")))
552
553         # If we're adding a static method, and it returns a primitive or an array of primitives,
554         # and a variable conversion adds a reference on the return type (via `this`), skip the
555         # variable's conversion reference-add (as we obviously cannot need a reference).
556         if impl_on_utils and (return_type_info.is_native_primitive or
557                 (return_type_info.ty_info.subty is not None and return_type_info.ty_info.subty.is_native_primitive)):
558             for arg in argument_types:
559                 if arg.from_hu_conv is not None and arg.from_hu_conv[1] != "":
560                     if "this" in arg.from_hu_conv[1]:
561                         arg.from_hu_conv = (arg.from_hu_conv[0], "")
562
563         out_java.write("\t// " + line)
564         (out_java_delta, out_c_delta, out_java_struct_delta) = \
565             consts.map_function(argument_types, c_call_string, method_name, struct_meth_name, return_type_info, struct_meth, default_constructor_args, takes_self, takes_self_ptr, args_known, type_mapping_generator, doc_comment)
566         out_java.write(out_java_delta)
567
568         if is_free:
569             assert len(argument_types) == 1
570             assert return_type_info.c_ty == "void"
571             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")
572             if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
573                 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
574
575             for info in argument_types:
576                 if info.arg_conv is not None:
577                     write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
578             assert c_call_string is None
579             write_c("\t" + method_name + "(")
580             if argument_types[0].arg_conv_name is not None:
581                 write_c(argument_types[0].arg_conv_name)
582             write_c(");")
583             for info in argument_types:
584                 if info.arg_conv_cleanup is not None:
585                     write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
586             write_c("\n}\n\n")
587         else:
588             write_c(out_c_delta)
589
590         out_java_struct = None
591         if impl_on_struct:
592             out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
593             out_java_struct.write(out_java_struct_delta)
594         elif impl_on_utils:
595             out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
596             for line in out_java_struct_delta.splitlines():
597                 out_java_struct.write(line + "\n")
598
599     def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
600         assert struct_name.startswith("LDK")
601         with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
602             unitary_enums.add(struct_name)
603             for idx, (struct_line, _) in enumerate(field_lines):
604                 if idx == 0:
605                     assert(struct_line == "typedef enum %s {" % struct_name)
606                 elif idx == len(field_lines) - 3:
607                     assert(struct_line.endswith("_Sentinel,"))
608                 elif idx == len(field_lines) - 2:
609                     assert(struct_line == "} %s;" % struct_name)
610                 elif idx == len(field_lines) - 1:
611                     assert(struct_line == "")
612             assert struct_name.startswith("LDK")
613             (c_out, native_file_out, native_out) = consts.native_c_unitary_enum_map(struct_name[3:], [(x.strip().strip(","), y) for x, y in field_lines[1:-3]], enum_doc_comment)
614             write_c(c_out)
615             out_java_enum.write(native_file_out)
616             out_java.write(native_out)
617
618     def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
619         java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
620
621         enum_variants = []
622         tag_field_lines = union_enum_items["field_lines"]
623         contains_trait = False
624         for idx, (struct_line, variant_docs) in enumerate(tag_field_lines):
625             if idx == 0:
626                 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
627             elif idx == len(tag_field_lines) - 3:
628                 assert(struct_line.endswith("_Sentinel,"))
629             elif idx == len(tag_field_lines) - 2:
630                 assert(struct_line == "} %s_Tag;" % struct_name)
631             elif idx == len(tag_field_lines) - 1:
632                 assert(struct_line == "")
633             else:
634                 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
635                 fields = []
636                 if "LDK" + variant_name in union_enum_items:
637                     enum_var_lines = union_enum_items["LDK" + variant_name]
638                     for idx, (field, field_docs) in enumerate(enum_var_lines):
639                         if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
640                             field_ty = type_mapping_generator.java_c_types(field.strip(' ;'), None)
641                             contains_trait |= field_ty.contains_trait
642                             if field_docs is not None and doc_to_field_nullable(field_docs):
643                                 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, True)
644                             else:
645                                 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, False)
646                             fields.append((field_conv, field_docs))
647                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, False))
648                 elif camel_to_snake(variant_name) in inline_enum_variants:
649                     # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
650                     # docs through to there, and then potentially mark the field nullable.
651                     mapped = type_mapping_generator.map_type(inline_enum_variants[camel_to_snake(variant_name)] + " " + camel_to_snake(variant_name), False, None, False, True)
652                     contains_trait |= mapped.ty_info.contains_trait
653                     fields.append((mapped, None))
654                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
655                 else:
656                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
657         complex_enums[struct_name] = contains_trait
658
659         with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
660             (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
661
662             out_java_enum.write(out_java_enum_addendum)
663             out_java.write(out_java_addendum)
664             write_c(out_c_addendum)
665
666     def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
667         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
668             field_var_convs = []
669             flattened_field_var_convs = []
670             for var_line in field_var_lines:
671                 if var_line.group(1) in trait_structs:
672                     field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
673                     flattened_field_var_convs.append((var_line.group(1), var_line.group(2), ))
674                     flattened_field_var_convs.extend(trait_structs[var_line.group(1)])
675                 else:
676                     mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
677                     field_var_convs.append(mapped)
678                     flattened_field_var_convs.append(mapped)
679             trait_structs[struct_name] = field_var_convs
680
681             field_fns = []
682             for fn_docs, fn_line in trait_fn_lines:
683                 if fn_line == "cloned":
684                     ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
685                     field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
686                 else:
687                     (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
688                     if ret_nullable:
689                         assert False # This isn't yet handled on the Java side
690                         ret_ty_info.nullable = True
691                         ret_ty_info = type_mapping_generator.map_nullable_type(fn_line.group(2).strip() + " ret", True, None, False, False)
692                     else:
693                         ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
694                     is_const = fn_line.group(4) is not None
695
696                     arg_tys = []
697                     for idx, arg in enumerate(fn_line.group(5).split(',')):
698                         if arg == "":
699                             continue
700                         arg_ty_info = type_mapping_generator.java_c_types(arg, None)
701                         if arg_ty_info.var_name in nullable_params:
702                             # Types that are actually null instead of all-0s aren't yet handled on the Java side:
703                             arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, True)
704                         else:
705                             arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, False)
706                         arg_tys.append(arg_conv_info)
707                     field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
708
709             (out_java_addendum, out_java_trait_addendum, out_c_addendum) = consts.native_c_map_trait(struct_name, field_var_convs, flattened_field_var_convs, field_fns, trait_doc_comment)
710             write_c(out_c_addendum)
711             out_java_trait.write(out_java_trait_addendum)
712             out_java.write(out_java_addendum)
713
714         for fn_docs, fn_line in trait_fn_lines:
715             if fn_line == "cloned":
716                 continue
717             # For now, just disable enabling the _call_log - we don't know how to inverse-map String
718             is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
719             if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
720                 dummy_line = fn_line.group(2) + struct_name.replace("LDK", "") + "_" + fn_line.group(3) + " " + struct_name + " *NONNULL_PTR this_arg" + fn_line.group(5) + "\n"
721                 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", fn_docs)
722         for idx, var_line in enumerate(field_var_lines):
723             if var_line.group(1) not in trait_structs:
724                 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
725                 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
726                 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
727                 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
728                 write_c("}\n")
729                 dummy_line = var_line.group(1) + " " + struct_name.replace("LDK", "") + "_get_" + var_line.group(2) + " " + struct_name + " *NONNULL_PTR this_arg" + fn_line.group(5) + "\n"
730                 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", fn_docs)
731
732     def map_result(struct_name, res_ty, err_ty):
733         result_types.add(struct_name)
734         human_ty = struct_name.replace("LDKCResult", "Result")
735         res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
736         err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
737         java_hu_struct = consts.map_result(struct_name, res_map, err_map)
738         create_getter(struct_name, res_ty, "ok", ("*", "->contents.result"), ("", "->result_ok"))
739         create_getter(struct_name, err_ty, "err", ("*", "->contents.err"), ("!", "->result_ok"))
740         with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
741             out_java_struct.write(java_hu_struct)
742
743     def create_getter(struct_name, field_decl, field_name, accessor, check_sfx):
744         field_ty = java_c_types(field_decl + " " + field_name, None)
745         ptr_fn_defn = field_decl + " *" + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
746         owned_fn_defn = field_decl + " " + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
747
748         holds_ref = False
749         if field_ty.rust_obj is not None and field_ty.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
750             fn_defn = owned_fn_defn
751             write_c("static inline " + fn_defn + "{\n")
752             if check_sfx is not None:
753                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
754             write_c("\treturn " + field_ty.rust_obj.replace("LDK", "") + "_clone(&" + accessor[0] + "owner" + accessor[1] + ");\n")
755         elif field_ty.arr_len is not None or field_ty.is_native_primitive or field_ty.rust_obj in unitary_enums:
756             fn_defn = owned_fn_defn
757             write_c("static inline " + fn_defn + "{\n")
758             if check_sfx is not None:
759                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
760             write_c("\treturn " + accessor[0] + "owner" + accessor[1] + ";\n")
761             holds_ref = True
762         else:
763             fn_defn = ptr_fn_defn
764             write_c("static inline " + fn_defn + "{\n")
765             if check_sfx is not None:
766                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
767             write_c("\treturn &" + accessor[0] + "owner" + accessor[1] + ";\n")
768             holds_ref = True
769         write_c("}\n")
770         dummy_line = fn_defn + ";\n"
771         map_fn_with_ref_option(dummy_line, reg_fn_regex.match(dummy_line), None, None, "", holds_ref)
772
773     def map_tuple(struct_name, field_lines):
774         human_ty = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
775         with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
776             out_java_struct.write(consts.map_tuple(struct_name))
777             ty_list = []
778             for idx, (line, _) in enumerate(field_lines):
779                 if idx != 0 and idx < len(field_lines) - 2:
780                     ty_list.append(java_c_types(line.strip(';'), None))
781             tuple_types[struct_name] = (ty_list, struct_name)
782
783         # Map virtual getter functions
784         for idx, (line, _) in enumerate(field_lines):
785             if idx != 0 and idx < len(field_lines) - 2:
786                 field_name = chr(ord('a') + idx - 1)
787                 assert line.endswith(" " + field_name + ";")
788                 create_getter(struct_name, line[:-3].strip(), field_name, ("", "->" + field_name), None)
789
790     out_java.write(consts.bindings_header)
791     with open(f"{sys.argv[2]}/version{consts.file_ext}", "w") as out_java_version:
792         out_java_version.write(consts.bindings_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
793
794     with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
795         out_java_struct.write(consts.common_base)
796
797     block_comment = None
798     last_block_comment = None
799     cur_block_obj = None
800
801     const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
802
803     line_indicates_result_regex = re.compile("^   union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
804     line_indicates_vec_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
805     line_indicates_opaque_regex = re.compile("^   bool is_owned;$")
806     line_indicates_trait_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
807     assert(line_indicates_trait_regex.match("   uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
808     assert(line_indicates_trait_regex.match("   struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
809     assert(line_indicates_trait_regex.match("   struct LDKCVec_u8Z (*write)(const void *this_arg);"))
810     line_indicates_trait_clone_regex = re.compile("^   void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
811     assert(line_indicates_trait_clone_regex.match("   void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
812     line_field_var_regex = re.compile("^   struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
813     assert(line_field_var_regex.match("   struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
814     assert(line_field_var_regex.match("   struct LDKChannelPublicKeys pubkeys;"))
815     struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
816     assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
817     assert(struct_name_regex.match("typedef enum LDKNetwork {"))
818
819     union_enum_items = {}
820     result_ptr_struct_items = {}
821     for line in in_h:
822         if block_comment is not None:
823             if line.endswith("*/\n"):
824                 last_block_comment = block_comment.strip("\n")
825                 block_comment = None
826             else:
827                 block_comment = block_comment + line.strip(" /*")
828         elif cur_block_obj is not None:
829             cur_block_obj  = cur_block_obj + line
830             if line.startswith("} "):
831                 field_lines = []
832                 struct_name = None
833                 vec_ty = None
834                 obj_lines = cur_block_obj.split("\n")
835                 is_opaque = False
836                 result_contents = None
837                 is_unitary_enum = False
838                 is_union_enum = False
839                 is_union = False
840                 is_tuple = False
841                 trait_fn_lines = []
842                 field_var_lines = []
843                 last_struct_block_comment = None
844
845                 for idx, struct_line in enumerate(obj_lines):
846                     if struct_line.strip().startswith("/*"):
847                         block_comment = struct_line.strip(" /*")
848                     if block_comment is not None:
849                         if struct_line.endswith("*/"):
850                             last_struct_block_comment = block_comment.strip("\n")
851                             block_comment = None
852                         else:
853                             block_comment = block_comment + "\n" + struct_line.strip(" /*").replace("…", "...")
854                     else:
855                         struct_name_match = struct_name_regex.match(struct_line)
856                         if struct_name_match is not None:
857                             struct_name = struct_name_match.group(3)
858                             if struct_name_match.group(1) == "enum":
859                                 if not struct_name.endswith("_Tag"):
860                                     is_unitary_enum = True
861                                 else:
862                                     is_union_enum = True
863                             elif struct_name_match.group(1) == "union":
864                                 is_union = True
865                         if line_indicates_opaque_regex.match(struct_line):
866                             is_opaque = True
867                         result_match = line_indicates_result_regex.match(struct_line)
868                         if result_match is not None:
869                             result_contents = result_match.group(1)
870                         vec_ty_match = line_indicates_vec_regex.match(struct_line)
871                         if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
872                             vec_ty = vec_ty_match.group(2)
873                         elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
874                             is_tuple = True
875                         trait_fn_match = line_indicates_trait_regex.match(struct_line)
876                         if trait_fn_match is not None:
877                             trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
878                         trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
879                         if trait_clone_fn_match is not None:
880                             trait_fn_lines.append((last_struct_block_comment, "cloned"))
881                         field_var_match = line_field_var_regex.match(struct_line)
882                         if field_var_match is not None:
883                             field_var_lines.append(field_var_match)
884                         field_lines.append((struct_line, last_struct_block_comment))
885                         last_struct_block_comment = None
886
887                 assert(struct_name is not None)
888                 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))
889                 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))
890                 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))
891                 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))
892                 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))
893                 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))
894                 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))
895
896                 if is_opaque:
897                     opaque_structs.add(struct_name)
898                     with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
899                         out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
900                         last_block_comment = None
901                         out_java_struct.write(out_opaque_struct_human)
902                 elif result_contents is not None:
903                     assert result_contents in result_ptr_struct_items
904                     res_ty, err_ty = result_ptr_struct_items[result_contents]
905                     map_result(struct_name, res_ty, err_ty)
906                 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
907                     for line, _ in field_lines:
908                         if line.endswith("*result;"):
909                             res_ty = line[:-8].strip()
910                         elif line.endswith("*err;"):
911                             err_ty = line[:-5].strip()
912                     result_ptr_struct_items[struct_name] = (res_ty, err_ty)
913                     result_types.add(struct_name[:-3])
914                 elif is_tuple:
915                     map_tuple(struct_name, field_lines)
916                 elif vec_ty is not None:
917                     ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
918                     if ty_info.is_native_primitive:
919                         clone_fns.add(struct_name.replace("LDK", "") + "_clone")
920                         write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
921                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
922                         write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
923                         write_c("\treturn ret;\n}\n")
924                     elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
925                         ty_name = struct_name.replace("LDK", "")
926                         clone_fns.add(ty_name + "_clone")
927                         write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
928                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
929                         write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
930                         write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
931                         write_c("\t}\n\treturn ret;\n}\n")
932                 elif is_union_enum:
933                     assert(struct_name.endswith("_Tag"))
934                     struct_name = struct_name[:-4]
935                     union_enum_items[struct_name] = {"field_lines": field_lines}
936                 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
937                     enum_var_name = struct_name.split("_")
938                     union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
939                 elif struct_name in union_enum_items:
940                     tuple_variants = {}
941                     elem_items = -1
942                     for line, _ in field_lines:
943                         if line == "      struct {":
944                             elem_items = 0
945                         elif line == "      };":
946                             elem_items = -1
947                         elif elem_items > -1:
948                             line = line.strip()
949                             if line.startswith("struct "):
950                                 line = line[7:]
951                             elif line.startswith("enum "):
952                                 line = line[5:]
953                             split = line.split(" ")
954                             assert len(split) == 2
955                             tuple_variants[split[1].strip(";")] = split[0]
956                             elem_items += 1
957                             if elem_items > 1:
958                                 # We don't currently support tuple variant with more than one element
959                                 assert False
960                     map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
961                     last_block_comment = None
962                 elif is_unitary_enum:
963                     map_unitary_enum(struct_name, field_lines, last_block_comment)
964                     last_block_comment = None
965                 elif len(trait_fn_lines) > 0:
966                     map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
967                 elif struct_name == "LDKTxOut":
968                     with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
969                         out_java_struct.write(consts.hu_struct_file_prefix)
970                         out_java_struct.write(consts.txout_defn)
971                         fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
972                         write_c(fn_line + " {")
973                         write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
974                         write_c("}")
975                         map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
976                         fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
977                         write_c(fn_line + " {")
978                         write_c("\treturn thing->value;")
979                         write_c("}")
980                         map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
981                 else:
982                     pass # Everything remaining is a byte[] or some form
983                 cur_block_obj = None
984         else:
985             fn_ptr = fn_ptr_regex.match(line)
986             fn_ret_arr = fn_ret_arr_regex.match(line)
987             reg_fn = reg_fn_regex.match(line)
988             const_val = const_val_regex.match(line)
989
990             if line.startswith("#include <"):
991                 pass
992             elif line.startswith("/*"):
993                 if not line.endswith("*/\n"):
994                     block_comment = line.strip(" /*")
995             elif line.startswith("typedef enum "):
996                 cur_block_obj = line
997             elif line.startswith("typedef struct "):
998                 cur_block_obj = line
999             elif line.startswith("typedef union "):
1000                 cur_block_obj = line
1001             elif fn_ptr is not None:
1002                 map_fn(line, fn_ptr, None, None, last_block_comment)
1003                 last_block_comment = None
1004             elif fn_ret_arr is not None:
1005                 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
1006                 last_block_comment = None
1007             elif reg_fn is not None:
1008                 map_fn(line, reg_fn, None, None, last_block_comment)
1009                 last_block_comment = None
1010             elif const_val_regex is not None:
1011                 # TODO Map const variables
1012                 pass
1013             else:
1014                 assert(line == "\n")
1015
1016     out_java.write(consts.bindings_footer)
1017     for struct_name in opaque_structs:
1018         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1019             out_java_struct.write("}\n")
1020     for struct_name in trait_structs:
1021         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1022             out_java_struct.write("}\n")
1023     for struct_name in complex_enums:
1024         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
1025             out_java_struct.write("}\n")
1026     for struct_name in result_types:
1027         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
1028             out_java_struct.write("}\n")
1029     for struct_name in tuple_types:
1030         struct_hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
1031         with open(f"{sys.argv[3]}/structs/{struct_hu_name}{consts.file_ext}", "a") as out_java_struct:
1032             out_java_struct.write("}\n")
1033
1034 with open(f"{sys.argv[4]}/bindings.c.body", "w") as out_c:
1035     out_c.write(consts.c_file_pfx)
1036     out_c.write(consts.init_str())
1037     out_c.write(c_file)
1038 with open(f"{sys.argv[4]}/version.c", "w") as out_c:
1039     out_c.write(consts.c_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
1040 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
1041     util.write(consts.util_fn_sfx)
1042 consts.cleanup()