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