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