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