Correct handling of arrays of 32-byte-array equivalents (Payment*)
[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 = "intptr_t"
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     else:
324         ma = var_ty_regex.match(fn_arg)
325         arr_ty = ma.group(1).strip()
326         if ma.group(1).strip() in unitary_enums:
327             assert ma.group(1).strip().startswith("LDK")
328             java_ty = ma.group(1).strip()[3:]
329             java_hu_ty = java_ty
330             c_ty = consts.result_c_ty
331             fn_ty_arg = "Lorg/ldk/enums/" + java_ty + ";"
332             fn_arg = ma.group(2).strip()
333             rust_obj = ma.group(1).strip()
334         else:
335             c_ty = consts.ptr_c_ty
336             java_ty = consts.ptr_native_ty
337             java_hu_ty = ma.group(1).strip()
338             java_hu_ty = java_hu_ty.replace("LDKCOption", "Option")
339             java_hu_ty = java_hu_ty.replace("LDKCResult", "Result")
340             java_hu_ty = java_hu_ty.replace("LDKC2Tuple", "TwoTuple")
341             java_hu_ty = java_hu_ty.replace("LDKC3Tuple", "ThreeTuple")
342             java_hu_ty = java_hu_ty.replace("LDK", "")
343             fn_ty_arg = "J"
344             fn_arg = ma.group(2).strip()
345             rust_obj = ma.group(1).strip()
346             if rust_obj in trait_structs:
347                 contains_trait = True
348             elif rust_obj in complex_enums:
349                 contains_trait = complex_enums[rust_obj]
350             take_by_ptr = True
351
352     if fn_arg.startswith(" *") or fn_arg.startswith("*"):
353         fn_arg = fn_arg.replace("*", "").strip()
354         is_ptr = True
355         c_ty = consts.ptr_c_ty
356         java_ty = consts.ptr_native_ty
357         fn_ty_arg = "J"
358
359     var_is_arr = var_is_arr_regex.match(fn_arg)
360     subty = None
361     if var_is_arr is not None or ret_arr_len is not None:
362         assert(not take_by_ptr)
363         assert(not is_ptr)
364         # is there a special case for plurals?
365         if len(mapped_type) == 3:
366             java_ty = mapped_type[1]
367             java_hu_ty = mapped_type[2]
368         else:
369             java_ty = java_ty + "[]"
370             java_hu_ty = java_ty
371         c_ty = c_ty + "Array"
372
373         subty = java_c_types(arr_ty, None)
374         if subty is None:
375             assert java_c_types_none_allowed
376             return None
377         if is_ptr:
378             subty.pass_by_ref = True
379
380         if var_is_arr is not None:
381             if var_is_arr.group(1) == "":
382                 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,
383                     passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name="arg", subty=subty,
384                     arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
385             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,
386                 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name=var_is_arr.group(1), subty=subty,
387                 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
388
389     if java_hu_ty is None:
390         java_hu_ty = java_ty
391     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,
392         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,
393         contains_trait=contains_trait, subty=subty)
394
395 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
396 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
397 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
398 clone_fns = set()
399 constructor_fns = {}
400
401 from gen_type_mapping import TypeMappingGenerator
402 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
403
404 with open(sys.argv[1]) as in_h:
405     for line in in_h:
406         reg_fn = reg_fn_regex.match(line)
407         if reg_fn is not None:
408             if reg_fn.group(2).endswith("_clone"):
409                 clone_fns.add(reg_fn.group(2))
410             else:
411                 rty = java_c_types(reg_fn.group(1), None)
412                 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
413                     constructor_fns[rty.rust_obj] = reg_fn.group(3)
414             continue
415         arr_fn = fn_ret_arr_regex.match(line)
416         if arr_fn is not None:
417             if arr_fn.group(2).endswith("_clone"):
418                 clone_fns.add(arr_fn.group(2))
419             # No object constructors return arrays, as then they wouldn't be an object constructor
420             continue
421
422 # Define some manual clones...
423 clone_fns.add("ThirtyTwoBytes_clone")
424 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
425
426 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
427
428 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
429     util.write(consts.util_fn_pfx)
430
431 with open(sys.argv[1]) as in_h, open(f"{sys.argv[2]}/bindings{consts.file_ext}", "w") as out_java:
432     # Map a top-level function
433     def map_fn(line, re_match, ret_arr_len, c_call_string, doc_comment):
434         map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, False)
435     def map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, force_holds_ref):
436         method_return_type = re_match.group(1)
437         method_name = re_match.group(2)
438         method_comma_separated_arguments = re_match.group(3)
439         method_arguments = method_comma_separated_arguments.split(',')
440
441         if method_name.startswith("__"):
442             return
443
444         is_free = method_name.endswith("_free")
445         if method_name.startswith("COption") or method_name.startswith("CResult"):
446             struct_meth = method_name.rsplit("Z", 1)[0][1:] + "Z"
447             expected_struct = "LDKC" + struct_meth
448             struct_meth_name = method_name[len(struct_meth) + 1:].strip("_")
449         elif method_name.startswith("C2Tuple") or method_name.startswith("C3Tuple"):
450             tuple_name = method_name.rsplit("Z", 1)[0][2:] + "Z"
451             if method_name.startswith("C2Tuple"):
452                 struct_meth = "Two" + tuple_name
453                 expected_struct = "LDKC2" + tuple_name
454             else:
455                 struct_meth = "Three" + tuple_name
456                 expected_struct = "LDKC3" + tuple_name
457             struct_meth_name = method_name[len(tuple_name) + 2:].strip("_")
458         else:
459             struct_meth = method_name.split("_")[0]
460             expected_struct = "LDK" + struct_meth
461             struct_meth_name = method_name[len(struct_meth) + 1 if len(struct_meth) != 0 else 0:].strip("_")
462
463         (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
464         if ret_nullable:
465             return_type_info = type_mapping_generator.map_nullable_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
466         else:
467             return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
468
469         if method_name.endswith("_clone") and expected_struct not in unitary_enums:
470             meth_line = "uintptr_t " + expected_struct.replace("LDK", "") + "_clone_ptr(" + expected_struct + " *NONNULL_PTR arg)"
471             write_c("static inline " + meth_line + " {\n")
472             write_c("\t" + return_type_info.ret_conv[0].replace("\n", "\n\t"))
473             write_c(method_name + "(arg)")
474             write_c(return_type_info.ret_conv[1])
475             write_c("\n\treturn " + return_type_info.ret_conv_name + ";\n}\n")
476             map_fn(meth_line + ";\n", re.compile("(uintptr_t) ([A-Za-z_0-9]*)\((.*)\)").match(meth_line), None, None, None)
477
478         argument_types = []
479         default_constructor_args = {}
480         takes_self = False
481         takes_self_ptr = False
482         args_known = True
483
484         for argument_index, argument in enumerate(method_arguments):
485             arg_ty = type_mapping_generator.java_c_types(argument, None)
486             argument_conversion_info = None
487             if argument_index == 0 and arg_ty.java_hu_ty == struct_meth:
488                 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
489                 takes_self = True
490                 if argument_conversion_info.ty_info.is_ptr:
491                     takes_self_ptr = True
492             elif arg_ty.var_name in params_nullable:
493                 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, True)
494                 if argument_conversion_info.arg_conv is not None and "WARNING" in argument_conversion_info.arg_conv:
495                     arg_ty_info = java_c_types(argument, None)
496                     print("WARNING: Remapping argument " + arg_ty_info.var_name + " of function " + method_name + " to a reference")
497                     print("    The argument appears to require a move, or not clonable, and is nullable.")
498                     print("    Normally for arguments that require a move and are not clonable, we split")
499                     print("    the argument into the type's constructor's arguments and just use those to")
500                     print("    construct a new object on the fly.")
501                     print("    However, because the object is nullable, doing so would mean we can no")
502                     print("    longer allow the user to pass null, as we now have an argument list instead.")
503                     print("    Thus, we blindly assume its really an Option<&Type> instead of an Option<Type>.")
504                     print("    It may or may not actually be a reference, but its the simplest mapping option")
505                     print("    and also the only use of this code today.")
506                     arg_ty_info.requires_clone = False
507                     argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty_info, False, None, is_free, True, True)
508                     assert argument_conversion_info.nullable
509                     assert argument_conversion_info.arg_conv is not None and "WARNING" not in argument_conversion_info.arg_conv
510             else:
511                 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
512
513             if argument_conversion_info.arg_conv is not None and "WARNING" in argument_conversion_info.arg_conv:
514                 if argument_conversion_info.rust_obj in constructor_fns:
515                     assert not is_free
516                     for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
517                         explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
518                         if explode_arg_conv.c_ty == "void":
519                             # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
520                             # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
521                             args_known = False
522                             pass
523                         if not argument_conversion_info.arg_name in default_constructor_args:
524                             default_constructor_args[argument_conversion_info.arg_name] = []
525                         default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
526             argument_types.append(argument_conversion_info)
527
528         if not takes_self and return_type_info.java_hu_ty != struct_meth:
529             if not return_type_info.java_hu_ty.startswith("Result_" + struct_meth):
530                 struct_meth_name = method_name
531                 struct_meth = ""
532                 expected_struct = ""
533
534         impl_on_struct = (expected_struct in opaque_structs or expected_struct in trait_structs or
535             expected_struct in complex_enums or expected_struct in complex_enums or
536             expected_struct in result_types or expected_struct in tuple_types) and not is_free
537         impl_on_utils = not impl_on_struct and (not is_free and not method_name.endswith("_clone") and
538             not method_name.startswith("TxOut") and
539             not method_name.startswith("_") and
540             method_name != "check_platform" and method_name != "Result_read" and
541             not expected_struct in unitary_enums and
542             ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
543               or method_name.endswith("_read")))
544
545         # If we're adding a static method, and it returns a primitive or an array of primitives,
546         # and a variable conversion adds a reference on the return type (via `this`), skip the
547         # variable's conversion reference-add (as we obviously cannot need a reference).
548         if impl_on_utils and (return_type_info.is_native_primitive or
549                 (return_type_info.ty_info.subty is not None and return_type_info.ty_info.subty.is_native_primitive)):
550             for arg in argument_types:
551                 if arg.from_hu_conv is not None and arg.from_hu_conv[1] != "":
552                     if "this" in arg.from_hu_conv[1]:
553                         arg.from_hu_conv = (arg.from_hu_conv[0], "")
554
555         out_java.write("\t// " + line)
556         (out_java_delta, out_c_delta, out_java_struct_delta) = \
557             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)
558         out_java.write(out_java_delta)
559
560         if is_free:
561             assert len(argument_types) == 1
562             assert return_type_info.c_ty == "void"
563             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")
564             if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
565                 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
566
567             for info in argument_types:
568                 if info.arg_conv is not None:
569                     write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
570             assert c_call_string is None
571             write_c("\t" + method_name + "(")
572             if argument_types[0].arg_conv_name is not None:
573                 write_c(argument_types[0].arg_conv_name)
574             write_c(");")
575             for info in argument_types:
576                 if info.arg_conv_cleanup is not None:
577                     write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
578             write_c("\n}\n\n")
579         else:
580             write_c(out_c_delta)
581
582         out_java_struct = None
583         if impl_on_struct:
584             out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
585             out_java_struct.write(out_java_struct_delta)
586         elif impl_on_utils:
587             out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
588             for line in out_java_struct_delta.splitlines():
589                 out_java_struct.write(line + "\n")
590
591     def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
592         assert struct_name.startswith("LDK")
593         with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
594             unitary_enums.add(struct_name)
595             for idx, (struct_line, _) in enumerate(field_lines):
596                 if idx == 0:
597                     assert(struct_line == "typedef enum %s {" % struct_name)
598                 elif idx == len(field_lines) - 3:
599                     assert(struct_line.endswith("_Sentinel,"))
600                 elif idx == len(field_lines) - 2:
601                     assert(struct_line == "} %s;" % struct_name)
602                 elif idx == len(field_lines) - 1:
603                     assert(struct_line == "")
604             assert struct_name.startswith("LDK")
605             (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)
606             write_c(c_out)
607             out_java_enum.write(native_file_out)
608             out_java.write(native_out)
609
610     def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
611         java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
612
613         enum_variants = []
614         tag_field_lines = union_enum_items["field_lines"]
615         contains_trait = False
616         for idx, (struct_line, variant_docs) in enumerate(tag_field_lines):
617             if idx == 0:
618                 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
619             elif idx == len(tag_field_lines) - 3:
620                 assert(struct_line.endswith("_Sentinel,"))
621             elif idx == len(tag_field_lines) - 2:
622                 assert(struct_line == "} %s_Tag;" % struct_name)
623             elif idx == len(tag_field_lines) - 1:
624                 assert(struct_line == "")
625             else:
626                 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
627                 fields = []
628                 if "LDK" + variant_name in union_enum_items:
629                     enum_var_lines = union_enum_items["LDK" + variant_name]
630                     for idx, (field, field_docs) in enumerate(enum_var_lines):
631                         if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
632                             field_ty = type_mapping_generator.java_c_types(field.strip(' ;'), None)
633                             contains_trait |= field_ty.contains_trait
634                             if field_docs is not None and doc_to_field_nullable(field_docs):
635                                 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, True)
636                             else:
637                                 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, False)
638                             fields.append((field_conv, field_docs))
639                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, False))
640                 elif camel_to_snake(variant_name) in inline_enum_variants:
641                     # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
642                     # docs through to there, and then potentially mark the field nullable.
643                     mapped = type_mapping_generator.map_type(inline_enum_variants[camel_to_snake(variant_name)] + " " + camel_to_snake(variant_name), False, None, False, True)
644                     contains_trait |= mapped.ty_info.contains_trait
645                     fields.append((mapped, None))
646                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
647                 else:
648                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
649         complex_enums[struct_name] = contains_trait
650
651         with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
652             (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
653
654             out_java_enum.write(out_java_enum_addendum)
655             out_java.write(out_java_addendum)
656             write_c(out_c_addendum)
657
658     def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
659         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
660             field_var_convs = []
661             flattened_field_var_convs = []
662             for var_line in field_var_lines:
663                 if var_line.group(1) in trait_structs:
664                     field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
665                     flattened_field_var_convs.append((var_line.group(1), var_line.group(2), ))
666                     flattened_field_var_convs.extend(trait_structs[var_line.group(1)])
667                 else:
668                     mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
669                     field_var_convs.append(mapped)
670                     flattened_field_var_convs.append(mapped)
671             trait_structs[struct_name] = field_var_convs
672
673             field_fns = []
674             for fn_docs, fn_line in trait_fn_lines:
675                 if fn_line == "cloned":
676                     ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
677                     field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
678                 else:
679                     (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
680                     if ret_nullable:
681                         assert False # This isn't yet handled on the Java side
682                         ret_ty_info.nullable = True
683                         ret_ty_info = type_mapping_generator.map_nullable_type(fn_line.group(2).strip() + " ret", True, None, False, False)
684                     else:
685                         ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
686                     is_const = fn_line.group(4) is not None
687
688                     arg_tys = []
689                     for idx, arg in enumerate(fn_line.group(5).split(',')):
690                         if arg == "":
691                             continue
692                         arg_ty_info = type_mapping_generator.java_c_types(arg, None)
693                         if arg_ty_info.var_name in nullable_params:
694                             # Types that are actually null instead of all-0s aren't yet handled on the Java side:
695                             arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, True)
696                         else:
697                             arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, False)
698                         arg_tys.append(arg_conv_info)
699                     field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
700
701             (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)
702             write_c(out_c_addendum)
703             out_java_trait.write(out_java_trait_addendum)
704             out_java.write(out_java_addendum)
705
706         for fn_docs, fn_line in trait_fn_lines:
707             if fn_line == "cloned":
708                 continue
709             # For now, just disable enabling the _call_log - we don't know how to inverse-map String
710             is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
711             if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
712                 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"
713                 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)
714         for idx, var_line in enumerate(field_var_lines):
715             if var_line.group(1) not in trait_structs:
716                 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
717                 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
718                 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
719                 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
720                 write_c("}\n")
721                 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"
722                 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)
723
724     def map_result(struct_name, res_ty, err_ty):
725         result_types.add(struct_name)
726         human_ty = struct_name.replace("LDKCResult", "Result")
727         res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
728         err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
729         java_hu_struct = consts.map_result(struct_name, res_map, err_map)
730         create_getter(struct_name, res_ty, "ok", ("*", "->contents.result"), ("", "->result_ok"))
731         create_getter(struct_name, err_ty, "err", ("*", "->contents.err"), ("!", "->result_ok"))
732         with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
733             out_java_struct.write(java_hu_struct)
734
735     def create_getter(struct_name, field_decl, field_name, accessor, check_sfx):
736         field_ty = java_c_types(field_decl + " " + field_name, None)
737         ptr_fn_defn = field_decl + " *" + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
738         owned_fn_defn = field_decl + " " + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
739
740         holds_ref = False
741         if field_ty.rust_obj is not None and field_ty.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
742             fn_defn = owned_fn_defn
743             write_c("static inline " + fn_defn + "{\n")
744             if check_sfx is not None:
745                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
746             write_c("\treturn " + field_ty.rust_obj.replace("LDK", "") + "_clone(&" + accessor[0] + "owner" + accessor[1] + ");\n")
747         elif field_ty.arr_len is not None or field_ty.is_native_primitive or field_ty.rust_obj in unitary_enums:
748             fn_defn = owned_fn_defn
749             write_c("static inline " + fn_defn + "{\n")
750             if check_sfx is not None:
751                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
752             write_c("\treturn " + accessor[0] + "owner" + accessor[1] + ";\n")
753             holds_ref = True
754         else:
755             fn_defn = ptr_fn_defn
756             write_c("static inline " + fn_defn + "{\n")
757             if check_sfx is not None:
758                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
759             write_c("\treturn &" + accessor[0] + "owner" + accessor[1] + ";\n")
760             holds_ref = True
761         write_c("}\n")
762         dummy_line = fn_defn + ";\n"
763         map_fn_with_ref_option(dummy_line, reg_fn_regex.match(dummy_line), None, None, "", holds_ref)
764
765     def map_tuple(struct_name, field_lines):
766         human_ty = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
767         with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
768             out_java_struct.write(consts.map_tuple(struct_name))
769             ty_list = []
770             for idx, (line, _) in enumerate(field_lines):
771                 if idx != 0 and idx < len(field_lines) - 2:
772                     ty_list.append(java_c_types(line.strip(';'), None))
773             tuple_types[struct_name] = (ty_list, struct_name)
774
775         # Map virtual getter functions
776         for idx, (line, _) in enumerate(field_lines):
777             if idx != 0 and idx < len(field_lines) - 2:
778                 field_name = chr(ord('a') + idx - 1)
779                 assert line.endswith(" " + field_name + ";")
780                 create_getter(struct_name, line[:-3].strip(), field_name, ("", "->" + field_name), None)
781
782     out_java.write(consts.bindings_header)
783     with open(f"{sys.argv[2]}/version{consts.file_ext}", "w") as out_java_version:
784         out_java_version.write(consts.bindings_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
785
786     with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
787         out_java_struct.write(consts.common_base)
788
789     block_comment = None
790     last_block_comment = None
791     cur_block_obj = None
792
793     const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
794
795     line_indicates_result_regex = re.compile("^   union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
796     line_indicates_vec_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
797     line_indicates_opaque_regex = re.compile("^   bool is_owned;$")
798     line_indicates_trait_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
799     assert(line_indicates_trait_regex.match("   uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
800     assert(line_indicates_trait_regex.match("   struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
801     assert(line_indicates_trait_regex.match("   struct LDKCVec_u8Z (*write)(const void *this_arg);"))
802     line_indicates_trait_clone_regex = re.compile("^   void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
803     assert(line_indicates_trait_clone_regex.match("   void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
804     line_field_var_regex = re.compile("^   struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
805     assert(line_field_var_regex.match("   struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
806     assert(line_field_var_regex.match("   struct LDKChannelPublicKeys pubkeys;"))
807     struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
808     assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
809     assert(struct_name_regex.match("typedef enum LDKNetwork {"))
810
811     union_enum_items = {}
812     result_ptr_struct_items = {}
813     for line in in_h:
814         if block_comment is not None:
815             if line.endswith("*/\n"):
816                 last_block_comment = block_comment.strip("\n")
817                 block_comment = None
818             else:
819                 block_comment = block_comment + line.strip(" /*")
820         elif cur_block_obj is not None:
821             cur_block_obj  = cur_block_obj + line
822             if line.startswith("} "):
823                 field_lines = []
824                 struct_name = None
825                 vec_ty = None
826                 obj_lines = cur_block_obj.split("\n")
827                 is_opaque = False
828                 result_contents = None
829                 is_unitary_enum = False
830                 is_union_enum = False
831                 is_union = False
832                 is_tuple = False
833                 trait_fn_lines = []
834                 field_var_lines = []
835                 last_struct_block_comment = None
836
837                 for idx, struct_line in enumerate(obj_lines):
838                     if struct_line.strip().startswith("/*"):
839                         block_comment = struct_line.strip(" /*")
840                     if block_comment is not None:
841                         if struct_line.endswith("*/"):
842                             last_struct_block_comment = block_comment.strip("\n")
843                             block_comment = None
844                         else:
845                             block_comment = block_comment + "\n" + struct_line.strip(" /*").replace("…", "...")
846                     else:
847                         struct_name_match = struct_name_regex.match(struct_line)
848                         if struct_name_match is not None:
849                             struct_name = struct_name_match.group(3)
850                             if struct_name_match.group(1) == "enum":
851                                 if not struct_name.endswith("_Tag"):
852                                     is_unitary_enum = True
853                                 else:
854                                     is_union_enum = True
855                             elif struct_name_match.group(1) == "union":
856                                 is_union = True
857                         if line_indicates_opaque_regex.match(struct_line):
858                             is_opaque = True
859                         result_match = line_indicates_result_regex.match(struct_line)
860                         if result_match is not None:
861                             result_contents = result_match.group(1)
862                         vec_ty_match = line_indicates_vec_regex.match(struct_line)
863                         if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
864                             vec_ty = vec_ty_match.group(2)
865                         elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
866                             is_tuple = True
867                         trait_fn_match = line_indicates_trait_regex.match(struct_line)
868                         if trait_fn_match is not None:
869                             trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
870                         trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
871                         if trait_clone_fn_match is not None:
872                             trait_fn_lines.append((last_struct_block_comment, "cloned"))
873                         field_var_match = line_field_var_regex.match(struct_line)
874                         if field_var_match is not None:
875                             field_var_lines.append(field_var_match)
876                         field_lines.append((struct_line, last_struct_block_comment))
877                         last_struct_block_comment = None
878
879                 assert(struct_name is not None)
880                 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))
881                 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))
882                 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))
883                 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))
884                 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))
885                 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))
886                 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))
887
888                 if is_opaque:
889                     opaque_structs.add(struct_name)
890                     with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
891                         out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
892                         last_block_comment = None
893                         out_java_struct.write(out_opaque_struct_human)
894                 elif result_contents is not None:
895                     assert result_contents in result_ptr_struct_items
896                     res_ty, err_ty = result_ptr_struct_items[result_contents]
897                     map_result(struct_name, res_ty, err_ty)
898                 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
899                     for line, _ in field_lines:
900                         if line.endswith("*result;"):
901                             res_ty = line[:-8].strip()
902                         elif line.endswith("*err;"):
903                             err_ty = line[:-5].strip()
904                     result_ptr_struct_items[struct_name] = (res_ty, err_ty)
905                     result_types.add(struct_name[:-3])
906                 elif is_tuple:
907                     map_tuple(struct_name, field_lines)
908                 elif vec_ty is not None:
909                     ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
910                     if ty_info.is_native_primitive:
911                         clone_fns.add(struct_name.replace("LDK", "") + "_clone")
912                         write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
913                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
914                         write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
915                         write_c("\treturn ret;\n}\n")
916                     elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
917                         ty_name = struct_name.replace("LDK", "")
918                         clone_fns.add(ty_name + "_clone")
919                         write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
920                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
921                         write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
922                         write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
923                         write_c("\t}\n\treturn ret;\n}\n")
924                 elif is_union_enum:
925                     assert(struct_name.endswith("_Tag"))
926                     struct_name = struct_name[:-4]
927                     union_enum_items[struct_name] = {"field_lines": field_lines}
928                 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
929                     enum_var_name = struct_name.split("_")
930                     union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
931                 elif struct_name in union_enum_items:
932                     tuple_variants = {}
933                     elem_items = -1
934                     for line, _ in field_lines:
935                         if line == "      struct {":
936                             elem_items = 0
937                         elif line == "      };":
938                             elem_items = -1
939                         elif elem_items > -1:
940                             line = line.strip()
941                             if line.startswith("struct "):
942                                 line = line[7:]
943                             elif line.startswith("enum "):
944                                 line = line[5:]
945                             split = line.split(" ")
946                             assert len(split) == 2
947                             tuple_variants[split[1].strip(";")] = split[0]
948                             elem_items += 1
949                             if elem_items > 1:
950                                 # We don't currently support tuple variant with more than one element
951                                 assert False
952                     map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
953                     last_block_comment = None
954                 elif is_unitary_enum:
955                     map_unitary_enum(struct_name, field_lines, last_block_comment)
956                     last_block_comment = None
957                 elif len(trait_fn_lines) > 0:
958                     map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
959                 elif struct_name == "LDKTxOut":
960                     with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
961                         out_java_struct.write(consts.hu_struct_file_prefix)
962                         out_java_struct.write(consts.txout_defn)
963                         fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
964                         write_c(fn_line + " {")
965                         write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
966                         write_c("}")
967                         map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
968                         fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
969                         write_c(fn_line + " {")
970                         write_c("\treturn thing->value;")
971                         write_c("}")
972                         map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
973                 else:
974                     pass # Everything remaining is a byte[] or some form
975                 cur_block_obj = None
976         else:
977             fn_ptr = fn_ptr_regex.match(line)
978             fn_ret_arr = fn_ret_arr_regex.match(line)
979             reg_fn = reg_fn_regex.match(line)
980             const_val = const_val_regex.match(line)
981
982             if line.startswith("#include <"):
983                 pass
984             elif line.startswith("/*"):
985                 if not line.endswith("*/\n"):
986                     block_comment = line.strip(" /*")
987             elif line.startswith("typedef enum "):
988                 cur_block_obj = line
989             elif line.startswith("typedef struct "):
990                 cur_block_obj = line
991             elif line.startswith("typedef union "):
992                 cur_block_obj = line
993             elif fn_ptr is not None:
994                 map_fn(line, fn_ptr, None, None, last_block_comment)
995                 last_block_comment = None
996             elif fn_ret_arr is not None:
997                 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
998                 last_block_comment = None
999             elif reg_fn is not None:
1000                 map_fn(line, reg_fn, None, None, last_block_comment)
1001                 last_block_comment = None
1002             elif const_val_regex is not None:
1003                 # TODO Map const variables
1004                 pass
1005             else:
1006                 assert(line == "\n")
1007
1008     out_java.write(consts.bindings_footer)
1009     for struct_name in opaque_structs:
1010         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1011             out_java_struct.write("}\n")
1012     for struct_name in trait_structs:
1013         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1014             out_java_struct.write("}\n")
1015     for struct_name in complex_enums:
1016         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
1017             out_java_struct.write("}\n")
1018     for struct_name in result_types:
1019         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
1020             out_java_struct.write("}\n")
1021     for struct_name in tuple_types:
1022         struct_hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
1023         with open(f"{sys.argv[3]}/structs/{struct_hu_name}{consts.file_ext}", "a") as out_java_struct:
1024             out_java_struct.write("}\n")
1025
1026 with open(f"{sys.argv[4]}/bindings.c.body", "w") as out_c:
1027     out_c.write(consts.c_file_pfx)
1028     out_c.write(consts.init_str())
1029     out_c.write(c_file)
1030 with open(f"{sys.argv[4]}/version.c", "w") as out_c:
1031     out_c.write(consts.c_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
1032 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
1033     util.write(consts.util_fn_sfx)
1034 consts.cleanup()