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