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