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