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