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