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