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