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