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