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