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