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