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