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