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