Update type listing for LDK 0.0.117 bindings and support doubles
[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.result_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("_") and
659             method_name != "check_platform" and method_name != "Result_read" and
660             not expected_struct in unitary_enums and
661             ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
662               or method_name.endswith("_read")))
663
664         # If we're adding a static method, and it returns a primitive or an array of primitives,
665         # and a variable conversion adds a reference on the return type (via `this`), skip the
666         # variable's conversion reference-add (as we obviously cannot need a reference).
667         if impl_on_utils and (return_type_info.is_native_primitive or
668                 (return_type_info.ty_info.subty is not None and return_type_info.ty_info.subty.is_native_primitive)):
669             for arg in argument_types:
670                 if arg.from_hu_conv is not None and arg.from_hu_conv[1] != "":
671                     if "this" in arg.from_hu_conv[1]:
672                         arg.from_hu_conv = (arg.from_hu_conv[0], "")
673
674         out_java.write("\t// " + line)
675         args_known = True # We no longer ever set this to false
676         (out_java_delta, out_c_delta, out_java_struct_delta) = \
677             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)
678         out_java.write(out_java_delta)
679
680         if is_free:
681             assert len(argument_types) == 1
682             assert return_type_info.c_ty == "void"
683             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")
684             if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
685                 write_c("\tif (!ptr_is_owned(" + argument_types[0].ty_info.var_name + ")) return;\n")
686
687             for info in argument_types:
688                 if info.arg_conv is not None:
689                     write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
690             assert c_call_string is None
691             write_c("\t" + method_name + "(")
692             if argument_types[0].arg_conv_name is not None:
693                 write_c(argument_types[0].arg_conv_name)
694             write_c(");")
695             for info in argument_types:
696                 if info.arg_conv_cleanup is not None:
697                     write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
698             write_c("\n}\n\n")
699         else:
700             write_c(out_c_delta)
701
702         out_java_struct = None
703         if impl_on_struct:
704             out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
705             out_java_struct.write(out_java_struct_delta)
706         elif impl_on_utils:
707             out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
708             for line in out_java_struct_delta.splitlines():
709                 out_java_struct.write(line + "\n")
710
711     def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
712         assert struct_name.startswith("LDK")
713         with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
714             unitary_enums.add(struct_name)
715             for idx, (struct_line, _) in enumerate(field_lines):
716                 if idx == 0:
717                     assert(struct_line == "typedef enum %s {" % struct_name)
718                 elif idx == len(field_lines) - 3:
719                     assert(struct_line.endswith("_Sentinel,"))
720                 elif idx == len(field_lines) - 2:
721                     assert(struct_line == "} %s;" % struct_name)
722                 elif idx == len(field_lines) - 1:
723                     assert(struct_line == "")
724             assert struct_name.startswith("LDK")
725             (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)
726             write_c(c_out)
727             out_java_enum.write(native_file_out)
728             out_java.write(native_out)
729
730     def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
731         java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
732
733         enum_variants = []
734         tag_field_lines = union_enum_items["field_lines"]
735         contains_trait = False
736         for idx, (struct_line, variant_docs) in enumerate(tag_field_lines):
737             if idx == 0:
738                 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
739             elif idx == len(tag_field_lines) - 3:
740                 assert(struct_line.endswith("_Sentinel,"))
741             elif idx == len(tag_field_lines) - 2:
742                 assert(struct_line == "} %s_Tag;" % struct_name)
743             elif idx == len(tag_field_lines) - 1:
744                 assert(struct_line == "")
745             else:
746                 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
747                 fields = []
748                 if "LDK" + variant_name in union_enum_items:
749                     enum_var_lines = union_enum_items["LDK" + variant_name]
750                     for idx, (field, field_docs) in enumerate(enum_var_lines):
751                         if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
752                             field_ty = type_mapping_generator.java_c_types(field.strip(' ;'), None)
753                             contains_trait |= field_ty.contains_trait
754                             if field_docs is not None and doc_to_field_nullable(field_docs):
755                                 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, True)
756                             else:
757                                 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, False)
758                             fields.append((field_conv, field_docs))
759                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, False))
760                 elif camel_to_snake(variant_name) in inline_enum_variants:
761                     # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
762                     # docs through to there, and then potentially mark the field nullable.
763                     (variant_info, variant_field_docs) = inline_enum_variants[camel_to_snake(variant_name)]
764                     variant_ty_text = variant_info + " " + camel_to_snake(variant_name)
765                     variant_ty_info = type_mapping_generator.java_c_types(variant_ty_text, None)
766                     if variant_field_docs is not None and doc_to_field_nullable(variant_field_docs):
767                         mapped = type_mapping_generator.map_type_with_info(variant_ty_info, False, None, False, True, True)
768                     else:
769                         mapped = type_mapping_generator.map_type_with_info(variant_ty_info, False, None, False, True, False)
770                     contains_trait |= mapped.ty_info.contains_trait
771                     fields.append((mapped, None))
772                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
773                 else:
774                     enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
775         complex_enums[struct_name] = contains_trait
776
777         with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
778             (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
779
780             out_java_enum.write(out_java_enum_addendum)
781             out_java.write(out_java_addendum)
782             write_c(out_c_addendum)
783
784     def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
785         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
786             field_var_convs = []
787             flattened_field_var_convs = []
788             for var_line in field_var_lines:
789                 if var_line.group(1) in trait_structs:
790                     field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
791                     flattened_field_var_convs.append((var_line.group(1), var_line.group(2), var_line.group(2)))
792                     for field_var in trait_structs[var_line.group(1)]:
793                         if isinstance(field_var, ConvInfo):
794                             flattened_field_var_convs.append(field_var)
795                         else:
796                             path = var_line.group(2)
797                             if len(field_var) > 2:
798                                 path = var_line.group(2) + "." + field_var[2]
799                             flattened_field_var_convs.append((field_var[0], field_var[1], path))
800                 else:
801                     mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
802                     field_var_convs.append(mapped)
803                     flattened_field_var_convs.append(mapped)
804             trait_structs[struct_name] = flattened_field_var_convs
805
806             field_fns = []
807             for fn_docs, fn_line in trait_fn_lines:
808                 if fn_line == "cloned":
809                     ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
810                     field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
811                 else:
812                     (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
813                     if ret_nullable:
814                         ret_ty_info = type_mapping_generator.map_nullable_type(fn_line.group(2).strip() + " ret", True, None, False, False)
815                         ret_ty_info.nullable = True
816                     else:
817                         ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
818                     is_const = fn_line.group(4) is not None
819
820                     arg_tys = []
821                     for idx, arg in enumerate(fn_line.group(5).split(',')):
822                         if arg == "":
823                             continue
824                         arg_ty_info = type_mapping_generator.java_c_types(arg, None)
825                         if arg_ty_info.var_name in nullable_params:
826                             # Types that are actually null instead of all-0s aren't yet handled on the Java side:
827                             arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, True)
828                         else:
829                             arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, False)
830                         arg_tys.append(arg_conv_info)
831                     field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
832
833             (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)
834             write_c(out_c_addendum)
835             out_java_trait.write(out_java_trait_addendum)
836             out_java.write(out_java_addendum)
837
838         for fn_docs, fn_line in trait_fn_lines:
839             if fn_line == "cloned":
840                 continue
841             # For now, just disable enabling the _call_log - we don't know how to inverse-map String
842             is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
843             if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
844                 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"
845                 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)
846         for idx, var_line in enumerate(field_var_lines):
847             if var_line.group(1) not in trait_structs:
848                 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
849                 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
850                 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
851                 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
852                 write_c("}\n")
853                 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"
854                 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)
855
856     def map_result(struct_name, res_ty, err_ty):
857         result_types.add(struct_name)
858         human_ty = struct_name.replace("LDKCResult", "Result")
859         res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
860         err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
861         java_hu_struct = consts.map_result(struct_name, res_map, err_map)
862         create_getter(struct_name, res_ty, "ok", ("*", "->contents.result"), ("", "->result_ok"))
863         create_getter(struct_name, err_ty, "err", ("*", "->contents.err"), ("!", "->result_ok"))
864         with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
865             out_java_struct.write(java_hu_struct)
866
867     def create_getter(struct_name, field_decl, field_name, accessor, check_sfx):
868         field_ty = java_c_types(field_decl + " " + field_name, None)
869         ptr_fn_defn = field_decl + " *" + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
870         owned_fn_defn = field_decl + " " + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
871
872         holds_ref = False
873         if field_ty.rust_obj is not None and field_ty.rust_obj in opaque_structs:
874             fn_defn = owned_fn_defn
875             write_c("static inline " + fn_defn + "{\n")
876             write_c("\t" + field_ty.rust_obj + " ret = " + accessor[0] + "owner" + accessor[1] + ";\n")
877             write_c("\tret.is_owned = false;\n")
878             write_c("\treturn ret;\n")
879         elif field_ty.rust_obj is not None and field_ty.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
880             fn_defn = owned_fn_defn
881             write_c("static inline " + fn_defn + "{\n")
882             if check_sfx is not None:
883                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
884             write_c("\treturn " + field_ty.rust_obj.replace("LDK", "") + "_clone(&" + accessor[0] + "owner" + accessor[1] + ");\n")
885         elif field_ty.arr_len is not None or field_ty.is_native_primitive or field_ty.rust_obj in unitary_enums:
886             fn_defn = owned_fn_defn
887             write_c("static inline " + fn_defn + "{\n")
888             if check_sfx is not None:
889                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
890             write_c("\treturn " + accessor[0] + "owner" + accessor[1] + ";\n")
891             holds_ref = True
892         else:
893             fn_defn = ptr_fn_defn
894             write_c("static inline " + fn_defn + "{\n")
895             if check_sfx is not None:
896                 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
897             write_c("\treturn &" + accessor[0] + "owner" + accessor[1] + ";\n")
898             holds_ref = True
899         write_c("}\n")
900         dummy_line = fn_defn + ";\n"
901         map_fn_with_ref_option(dummy_line, reg_fn_regex.match(dummy_line), None, None, "", holds_ref)
902
903     def map_tuple(struct_name, field_lines):
904         human_ty = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
905         with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
906             out_java_struct.write(consts.map_tuple(struct_name))
907             ty_list = []
908             for idx, (line, _) in enumerate(field_lines):
909                 if idx != 0 and idx < len(field_lines) - 2:
910                     ty_list.append(java_c_types(line.strip(';'), None))
911             tuple_types[struct_name] = (ty_list, struct_name)
912
913         # Map virtual getter functions
914         for idx, (line, _) in enumerate(field_lines):
915             if idx != 0 and idx < len(field_lines) - 2:
916                 field_name = chr(ord('a') + idx - 1)
917                 assert line.endswith(" " + field_name + ";")
918                 create_getter(struct_name, line[:-3].strip(), field_name, ("", "->" + field_name), None)
919
920     out_java.write(consts.bindings_header)
921     with open(f"{sys.argv[2]}/version{consts.file_ext}", "w") as out_java_version:
922         out_java_version.write(consts.bindings_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
923
924     with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
925         out_java_struct.write(consts.common_base)
926
927     block_comment = None
928     last_block_comment = None
929     cur_block_obj = None
930
931     const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
932
933     line_indicates_result_regex = re.compile("^   union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
934     line_indicates_vec_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
935     line_indicates_opaque_regex = re.compile("^   bool is_owned;$")
936     line_indicates_trait_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
937     assert(line_indicates_trait_regex.match("   uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
938     assert(line_indicates_trait_regex.match("   struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
939     assert(line_indicates_trait_regex.match("   struct LDKCVec_u8Z (*write)(const void *this_arg);"))
940     line_indicates_trait_clone_regex = re.compile("^   void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
941     assert(line_indicates_trait_clone_regex.match("   void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
942     line_field_var_regex = re.compile("^   struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
943     assert(line_field_var_regex.match("   struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
944     assert(line_field_var_regex.match("   struct LDKChannelPublicKeys pubkeys;"))
945     struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
946     assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
947     assert(struct_name_regex.match("typedef enum LDKNetwork {"))
948
949     union_enum_items = {}
950     result_ptr_struct_items = {}
951     for line in in_h:
952         if block_comment is not None:
953             if line.endswith("*/\n"):
954                 last_block_comment = block_comment.strip("\n")
955                 block_comment = None
956             else:
957                 block_comment = block_comment + line.strip(" /*")
958         elif cur_block_obj is not None:
959             cur_block_obj  = cur_block_obj + line
960             if line.startswith("} "):
961                 field_lines = []
962                 struct_name = None
963                 vec_ty = None
964                 obj_lines = cur_block_obj.split("\n")
965                 is_opaque = False
966                 result_contents = None
967                 is_unitary_enum = False
968                 is_union_enum = False
969                 is_union = False
970                 is_tuple = False
971                 trait_fn_lines = []
972                 field_var_lines = []
973                 last_struct_block_comment = None
974
975                 for idx, struct_line in enumerate(obj_lines):
976                     if struct_line.strip().startswith("/*"):
977                         block_comment = struct_line.strip(" /*")
978                     if block_comment is not None:
979                         if struct_line.endswith("*/"):
980                             last_struct_block_comment = block_comment.strip("\n")
981                             block_comment = None
982                         else:
983                             block_comment = block_comment + "\n" + struct_line.strip(" /*").replace("…", "...")
984                     else:
985                         struct_name_match = struct_name_regex.match(struct_line)
986                         if struct_name_match is not None:
987                             struct_name = struct_name_match.group(3)
988                             if struct_name_match.group(1) == "enum":
989                                 if not struct_name.endswith("_Tag"):
990                                     is_unitary_enum = True
991                                 else:
992                                     is_union_enum = True
993                             elif struct_name_match.group(1) == "union":
994                                 is_union = True
995                         if line_indicates_opaque_regex.match(struct_line):
996                             is_opaque = True
997                         result_match = line_indicates_result_regex.match(struct_line)
998                         if result_match is not None:
999                             result_contents = result_match.group(1)
1000                         vec_ty_match = line_indicates_vec_regex.match(struct_line)
1001                         if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
1002                             vec_ty = vec_ty_match.group(2)
1003                         elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
1004                             is_tuple = True
1005                         trait_fn_match = line_indicates_trait_regex.match(struct_line)
1006                         if trait_fn_match is not None:
1007                             trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
1008                         trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
1009                         if trait_clone_fn_match is not None:
1010                             trait_fn_lines.append((last_struct_block_comment, "cloned"))
1011                         field_var_match = line_field_var_regex.match(struct_line)
1012                         if field_var_match is not None:
1013                             field_var_lines.append(field_var_match)
1014                         field_lines.append((struct_line, last_struct_block_comment))
1015                         last_struct_block_comment = None
1016
1017                 assert(struct_name is not None)
1018                 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))
1019                 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))
1020                 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))
1021                 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))
1022                 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))
1023                 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))
1024                 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))
1025
1026                 if is_opaque:
1027                     opaque_structs.add(struct_name)
1028                     with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
1029                         out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
1030                         last_block_comment = None
1031                         out_java_struct.write(out_opaque_struct_human)
1032                 elif result_contents is not None:
1033                     assert result_contents in result_ptr_struct_items
1034                     res_ty, err_ty = result_ptr_struct_items[result_contents]
1035                     map_result(struct_name, res_ty, err_ty)
1036                 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
1037                     for line, _ in field_lines:
1038                         if line.endswith("*result;"):
1039                             res_ty = line[:-8].strip()
1040                         elif line.endswith("*err;"):
1041                             err_ty = line[:-5].strip()
1042                     result_ptr_struct_items[struct_name] = (res_ty, err_ty)
1043                     result_types.add(struct_name[:-3])
1044                 elif is_tuple:
1045                     map_tuple(struct_name, field_lines)
1046                 elif vec_ty is not None:
1047                     ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
1048                     if ty_info.is_native_primitive:
1049                         clone_fns.add(struct_name.replace("LDK", "") + "_clone")
1050                         write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
1051                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
1052                         write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
1053                         write_c("\treturn ret;\n}\n")
1054                     elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
1055                         ty_name = struct_name.replace("LDK", "")
1056                         clone_fns.add(ty_name + "_clone")
1057                         write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
1058                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
1059                         write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
1060                         write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
1061                         write_c("\t}\n\treturn ret;\n}\n")
1062                 elif is_union_enum:
1063                     assert(struct_name.endswith("_Tag"))
1064                     struct_name = struct_name[:-4]
1065                     union_enum_items[struct_name] = {"field_lines": field_lines}
1066                 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
1067                     enum_var_name = struct_name.split("_")
1068                     union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
1069                 elif struct_name in union_enum_items:
1070                     tuple_variants = {}
1071                     elem_items = -1
1072                     for line, field_block_comment in field_lines:
1073                         if line == "      struct {":
1074                             elem_items = 0
1075                         elif line == "      };":
1076                             elem_items = -1
1077                         elif elem_items > -1:
1078                             line = line.strip()
1079                             if line.startswith("struct "):
1080                                 line = line[7:]
1081                             elif line.startswith("enum "):
1082                                 line = line[5:]
1083                             split = line.split(" ")
1084                             assert len(split) == 2
1085                             tuple_variants[split[1].strip(";")] = (split[0], field_block_comment)
1086                             elem_items += 1
1087                             if elem_items > 1:
1088                                 # We don't currently support tuple variant with more than one element
1089                                 assert False
1090                     map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
1091                     last_block_comment = None
1092                 elif is_unitary_enum:
1093                     map_unitary_enum(struct_name, field_lines, last_block_comment)
1094                     last_block_comment = None
1095                 elif len(trait_fn_lines) > 0:
1096                     map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
1097                 elif struct_name == "LDKTxOut":
1098                     with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
1099                         out_java_struct.write(consts.hu_struct_file_prefix)
1100                         out_java_struct.write(consts.txout_defn)
1101                         out_java_struct.write(consts.hu_struct_file_suffix)
1102                         fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
1103                         write_c(fn_line + " {")
1104                         write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
1105                         write_c("}")
1106                         map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
1107                         fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
1108                         write_c(fn_line + " {")
1109                         write_c("\treturn thing->value;")
1110                         write_c("}")
1111                         map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
1112                 elif struct_name == "LDKTxIn":
1113                     with open(f"{sys.argv[3]}/structs/TxIn{consts.file_ext}", "w") as out_java_struct:
1114                         out_java_struct.write(consts.hu_struct_file_prefix)
1115                         out_java_struct.write(consts.txin_defn)
1116                         out_java_struct.write(consts.hu_struct_file_suffix)
1117                         fn_line = "struct LDKWitness TxIn_get_witness (struct LDKTxIn* thing)"
1118                         write_c(fn_line + " {")
1119                         write_c("\treturn Witness_clone(&thing->witness);")
1120                         write_c("}")
1121                         map_fn(fn_line + "\n", re.compile("(.*) (TxIn_get_witness) \((.*)\)").match(fn_line), None, None, None)
1122                         fn_line = "struct LDKCVec_u8Z TxIn_get_script_sig (struct LDKTxIn* thing)"
1123                         write_c(fn_line + " {")
1124                         write_c("\treturn CVec_u8Z_clone(&thing->script_sig);")
1125                         write_c("}")
1126                         map_fn(fn_line + "\n", re.compile("(.*) (TxIn_get_script_sig) \((.*)\)").match(fn_line), None, None, None)
1127                         fn_line = "LDKThirtyTwoBytes TxIn_get_previous_txid (struct LDKTxIn* thing)"
1128                         write_c(fn_line + " {")
1129                         write_c("\treturn thing->previous_txid;")
1130                         write_c("}")
1131                         map_fn(fn_line + "\n", re.compile("(.*) (TxIn_get_previous_txid) \((.*)\)").match(fn_line), None, None, None)
1132                         fn_line = "uint32_t TxIn_get_previous_vout (struct LDKTxIn* thing)"
1133                         write_c(fn_line + " {")
1134                         write_c("\treturn thing->previous_vout;")
1135                         write_c("}")
1136                         map_fn(fn_line + "\n", re.compile("(.*) (TxIn_get_previous_vout) \((.*)\)").match(fn_line), None, None, None)
1137                         fn_line = "uint32_t TxIn_get_sequence (struct LDKTxIn* thing)"
1138                         write_c(fn_line + " {")
1139                         write_c("\treturn thing->sequence;")
1140                         write_c("}")
1141                         map_fn(fn_line + "\n", re.compile("(.*) (TxIn_get_sequence) \((.*)\)").match(fn_line), None, None, None)
1142                 elif struct_name == "LDKBigEndianScalar":
1143                     with open(f"{sys.argv[3]}/structs/BigEndianScalar{consts.file_ext}", "w") as out_java_struct:
1144                         out_java_struct.write(consts.hu_struct_file_prefix)
1145                         out_java_struct.write(consts.scalar_defn)
1146                         out_java_struct.write(consts.hu_struct_file_suffix)
1147                         fn_line = "struct LDKThirtyTwoBytes BigEndianScalar_get_bytes (struct LDKBigEndianScalar* thing)"
1148                         write_c(fn_line + " {\n")
1149                         write_c("\tLDKThirtyTwoBytes ret = { .data = *thing->big_endian_bytes };\n")
1150                         write_c("\treturn ret;\n")
1151                         write_c("}\n")
1152                         map_fn(fn_line + "\n", re.compile("(.*) (BigEndianScalar_get_bytes) \((.*)\)").match(fn_line), None, None, None)
1153
1154                         # We need to be able to FREE a heap-allocated BigEndianScalar, but because
1155                         # there's nothing heap-allocated inside it the C bindings don't bother
1156                         # exposing a `_free` method. Instead, we have to manually write one here,
1157                         # though it doesn't need to do anything, the autogenerated wrapper will do
1158                         # the required FREE.
1159                         fn_line = "static void BigEndianScalar_free (struct LDKBigEndianScalar thing)"
1160                         write_c(fn_line + " {}\n")
1161                         map_fn(fn_line + "\n", re.compile("static (.*) (BigEndianScalar_free) \((.*)\)").match(fn_line), None, None, None)
1162                 else:
1163                     pass # Everything remaining is a byte[] of some form
1164                 cur_block_obj = None
1165         else:
1166             fn_ptr = fn_ptr_regex.match(line)
1167             fn_ret_arr = fn_ret_arr_regex.match(line)
1168             reg_fn = reg_fn_regex.match(line)
1169             const_val = const_val_regex.match(line)
1170
1171             if line.startswith("#include <"):
1172                 pass
1173             elif line.startswith("/*"):
1174                 if not line.endswith("*/\n"):
1175                     block_comment = line.strip(" /*")
1176             elif line.startswith("typedef enum "):
1177                 cur_block_obj = line
1178             elif line.startswith("typedef struct "):
1179                 cur_block_obj = line
1180             elif line.startswith("typedef union "):
1181                 cur_block_obj = line
1182             elif fn_ptr is not None:
1183                 map_fn(line, fn_ptr, None, None, last_block_comment)
1184                 last_block_comment = None
1185             elif fn_ret_arr is not None:
1186                 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
1187                 last_block_comment = None
1188             elif reg_fn is not None:
1189                 map_fn(line, reg_fn, None, None, last_block_comment)
1190                 last_block_comment = None
1191             elif const_val_regex is not None:
1192                 # TODO Map const variables
1193                 pass
1194             else:
1195                 assert(line == "\n")
1196
1197     out_java.write(consts.bindings_footer())
1198     for struct_name in opaque_structs:
1199         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1200             out_java_struct.write("}\n" + consts.hu_struct_file_suffix)
1201     for struct_name in trait_structs:
1202         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1203             out_java_struct.write("}\n" + consts.hu_struct_file_suffix)
1204     for struct_name in complex_enums:
1205         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
1206             out_java_struct.write("}\n" + consts.hu_struct_file_suffix)
1207     for struct_name in result_types:
1208         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
1209             out_java_struct.write("}\n" + consts.hu_struct_file_suffix)
1210     for struct_name in tuple_types:
1211         struct_hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
1212         with open(f"{sys.argv[3]}/structs/{struct_hu_name}{consts.file_ext}", "a") as out_java_struct:
1213             out_java_struct.write("}\n" + consts.hu_struct_file_suffix)
1214
1215 with open(f"{sys.argv[4]}/bindings.c.body", "w") as out_c:
1216     out_c.write(consts.c_file_pfx)
1217     out_c.write(consts.init_str())
1218     out_c.write(c_file)
1219 with open(f"{sys.argv[4]}/version.c", "w") as out_c:
1220     out_c.write(consts.c_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
1221 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
1222     util.write(consts.util_fn_sfx)
1223 consts.cleanup()