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