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