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