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