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