Merge pull request #45 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 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         elif ma.group(1).strip().startswith("LDKC2Tuple"):
308             c_ty = consts.ptr_c_ty
309             java_ty = consts.ptr_native_ty
310             java_hu_ty = "TwoTuple<"
311             if not ma.group(1).strip() in tuple_types:
312                 assert java_c_types_none_allowed
313                 return None
314             for idx, ty_info in enumerate(tuple_types[ma.group(1).strip()][0]):
315                 if idx != 0:
316                     java_hu_ty = java_hu_ty + ", "
317                 if ty_info.is_native_primitive:
318                     if ty_info.java_hu_ty == "int":
319                         java_hu_ty = java_hu_ty + "Integer" # Java concrete integer type is Integer, not Int
320                     else:
321                         java_hu_ty = java_hu_ty + ty_info.java_hu_ty.title() # If we're a primitive, capitalize the first letter
322                 else:
323                     java_hu_ty = java_hu_ty + ty_info.java_hu_ty
324             java_hu_ty = java_hu_ty + ">"
325             fn_ty_arg = "J"
326             fn_arg = ma.group(2).strip()
327             rust_obj = ma.group(1).strip()
328             take_by_ptr = True
329         elif ma.group(1).strip().startswith("LDKC3Tuple"):
330             c_ty = consts.ptr_c_ty
331             java_ty = consts.ptr_native_ty
332             java_hu_ty = "ThreeTuple<"
333             if not ma.group(1).strip() in tuple_types:
334                 assert java_c_types_none_allowed
335                 return None
336             for idx, ty_info in enumerate(tuple_types[ma.group(1).strip()][0]):
337                 if idx != 0:
338                     java_hu_ty = java_hu_ty + ", "
339                 if ty_info.is_native_primitive:
340                     if ty_info.java_hu_ty == "int":
341                         java_hu_ty = java_hu_ty + "Integer" # Java concrete integer type is Integer, not Int
342                     else:
343                         java_hu_ty = java_hu_ty + ty_info.java_hu_ty.title() # If we're a primitive, capitalize the first letter
344                 else:
345                     java_hu_ty = java_hu_ty + ty_info.java_hu_ty
346             java_hu_ty = java_hu_ty + ">"
347             fn_ty_arg = "J"
348             fn_arg = ma.group(2).strip()
349             rust_obj = ma.group(1).strip()
350             take_by_ptr = True
351         else:
352             c_ty = consts.ptr_c_ty
353             java_ty = consts.ptr_native_ty
354             java_hu_ty = ma.group(1).strip().replace("LDKCOption", "Option").replace("LDKCResult", "Result").replace("LDK", "")
355             fn_ty_arg = "J"
356             fn_arg = ma.group(2).strip()
357             rust_obj = ma.group(1).strip()
358             take_by_ptr = True
359
360     if fn_arg.startswith(" *") or fn_arg.startswith("*"):
361         fn_arg = fn_arg.replace("*", "").strip()
362         is_ptr = True
363         c_ty = consts.ptr_c_ty
364         java_ty = consts.ptr_native_ty
365         fn_ty_arg = "J"
366
367     var_is_arr = var_is_arr_regex.match(fn_arg)
368     if var_is_arr is not None or ret_arr_len is not None:
369         assert(not take_by_ptr)
370         assert(not is_ptr)
371         # is there a special case for plurals?
372         if len(mapped_type) == 2:
373             java_ty = mapped_type[1]
374         else:
375             java_ty = java_ty + "[]"
376         c_ty = c_ty + "Array"
377         if var_is_arr is not None:
378             if var_is_arr.group(1) == "":
379                 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,
380                     passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name="arg",
381                     arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False)
382             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,
383                 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name=var_is_arr.group(1),
384                 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False)
385
386     if java_hu_ty is None:
387         java_hu_ty = java_ty
388     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,
389         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)
390
391 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
392 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
393 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
394 clone_fns = set()
395 constructor_fns = {}
396
397 from gen_type_mapping import TypeMappingGenerator
398 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
399
400 with open(sys.argv[1]) as in_h:
401     for line in in_h:
402         reg_fn = reg_fn_regex.match(line)
403         if reg_fn is not None:
404             if reg_fn.group(2).endswith("_clone"):
405                 clone_fns.add(reg_fn.group(2))
406             else:
407                 rty = java_c_types(reg_fn.group(1), None)
408                 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
409                     constructor_fns[rty.rust_obj] = reg_fn.group(3)
410             continue
411         arr_fn = fn_ret_arr_regex.match(line)
412         if arr_fn is not None:
413             if arr_fn.group(2).endswith("_clone"):
414                 clone_fns.add(arr_fn.group(2))
415             # No object constructors return arrays, as then they wouldn't be an object constructor
416             continue
417
418 # Define some manual clones...
419 clone_fns.add("ThirtyTwoBytes_clone")
420 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
421
422 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
423
424 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
425     util.write(consts.util_fn_pfx)
426
427 with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
428     # Map a top-level function
429     def map_fn(line, re_match, ret_arr_len, c_call_string, doc_comment):
430         method_return_type = re_match.group(1)
431         method_name = re_match.group(2)
432         orig_method_name = str(method_name)
433         method_comma_separated_arguments = re_match.group(3)
434         method_arguments = method_comma_separated_arguments.split(',')
435
436         is_free = method_name.endswith("_free")
437         if method_name.startswith("COption") or method_name.startswith("CResult"):
438             struct_meth = method_name.rsplit("Z", 1)[0][1:] + "Z"
439         else:
440             struct_meth = method_name.split("_")[0]
441
442         return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, False)
443
444         (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
445         if ret_nullable:
446             return_type_info.nullable = True
447
448         argument_types = []
449         default_constructor_args = {}
450         takes_self = False
451         takes_self_ptr = False
452         args_known = True
453
454         for argument_index, argument in enumerate(method_arguments):
455             argument_conversion_info = type_mapping_generator.map_type(argument, False, None, is_free, True)
456             if argument_index == 0 and argument_conversion_info.java_hu_ty == struct_meth:
457                 takes_self = True
458                 if argument_conversion_info.ty_info.is_ptr:
459                     takes_self_ptr = True
460             elif argument_conversion_info.arg_name in params_nullable:
461                 argument_conversion_info.nullable = True
462                 if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
463                     arg_ty_info = java_c_types(argument, None)
464                     print("WARNING: Remapping argument " + arg_ty_info.var_name + " of function " + method_name + " to a reference")
465                     print("    The argument appears to require a move, or not clonable, and is nullable.")
466                     print("    Normally for arguments that require a move and are not clonable, we split")
467                     print("    the argument into the type's constructor's arguments and just use those to")
468                     print("    construct a new object on the fly.")
469                     print("    However, because the object is nullable, doing so would mean we can no")
470                     print("    longer allow the user to pass null, as we now have an argument list instead.")
471                     print("    Thus, we blindly assume its really an Option<&Type> instead of an Option<Type>.")
472                     print("    It may or may not actually be a reference, but its the simplest mapping option")
473                     print("    and also the only use of this code today.")
474                     arg_ty_info.requires_clone = False
475                     argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty_info, False, None, is_free, True)
476                     assert argument_conversion_info.arg_conv is not None and "Warning" not in argument_conversion_info.arg_conv
477
478             if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
479                 if argument_conversion_info.rust_obj in constructor_fns:
480                     assert not is_free
481                     for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
482                         explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
483                         if explode_arg_conv.c_ty == "void":
484                             # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
485                             # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
486                             args_known = False
487                             pass
488                         if not argument_conversion_info.arg_name in default_constructor_args:
489                             default_constructor_args[argument_conversion_info.arg_name] = []
490                         default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
491             argument_types.append(argument_conversion_info)
492         if not takes_self and return_type_info.java_hu_ty != struct_meth:
493             if not return_type_info.java_hu_ty.startswith("Result_" + struct_meth):
494                 method_name = orig_method_name
495                 struct_meth = ""
496
497         out_java.write("\t// " + line)
498         (out_java_delta, out_c_delta, out_java_struct_delta) = \
499             consts.map_function(argument_types, c_call_string, method_name, return_type_info, struct_meth, default_constructor_args, takes_self, takes_self_ptr, args_known, type_mapping_generator, doc_comment)
500         out_java.write(out_java_delta)
501
502         if is_free:
503             assert len(argument_types) == 1
504             assert return_type_info.c_ty == "void"
505             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")
506             if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
507                 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
508
509             for info in argument_types:
510                 if info.arg_conv is not None:
511                     write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
512             assert c_call_string is None
513             write_c("\t" + method_name + "(")
514             if argument_types[0].arg_conv_name is not None:
515                 write_c(argument_types[0].arg_conv_name)
516             write_c(");")
517             for info in argument_types:
518                 if info.arg_conv_cleanup is not None:
519                     write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
520             write_c("\n}\n\n")
521         else:
522             write_c(out_c_delta)
523
524         out_java_struct = None
525         expected_struct = "LDK" + struct_meth
526         expected_cstruct = "LDKC" + struct_meth
527         if (expected_struct in opaque_structs or expected_struct in trait_structs
528                 or expected_struct in complex_enums or expected_cstruct in complex_enums
529                 or expected_cstruct in result_types) and not is_free:
530             out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
531             out_java_struct.write(out_java_struct_delta)
532         elif (not is_free and not method_name.endswith("_clone") and
533                 not method_name.startswith("TxOut") and
534                 not method_name.startswith("_") and
535                 method_name != "check_platform" and method_name != "Result_read" and
536                 not expected_struct in unitary_enums and
537                 ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
538                   or method_name.endswith("_read"))):
539             out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
540             for line in out_java_struct_delta.splitlines():
541                 if not line.strip().startswith("this."):
542                     out_java_struct.write(line + "\n")
543                 else:
544                     out_java_struct.write("\t\t// " + line.strip() + "\n")
545
546     def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
547         assert struct_name.startswith("LDK")
548         with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
549             unitary_enums.add(struct_name)
550             for idx, (struct_line, _) in enumerate(field_lines):
551                 if idx == 0:
552                     assert(struct_line == "typedef enum %s {" % struct_name)
553                 elif idx == len(field_lines) - 3:
554                     assert(struct_line.endswith("_Sentinel,"))
555                 elif idx == len(field_lines) - 2:
556                     assert(struct_line == "} %s;" % struct_name)
557                 elif idx == len(field_lines) - 1:
558                     assert(struct_line == "")
559             assert struct_name.startswith("LDK")
560             (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)
561             write_c(c_out)
562             out_java_enum.write(native_file_out)
563             out_java.write(native_out)
564
565     def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
566         java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
567         complex_enums.add(struct_name)
568
569         enum_variants = []
570         tag_field_lines = union_enum_items["field_lines"]
571         for idx, (struct_line, _) in enumerate(tag_field_lines):
572             if idx == 0:
573                 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
574             elif idx == len(tag_field_lines) - 3:
575                 assert(struct_line.endswith("_Sentinel,"))
576             elif idx == len(tag_field_lines) - 2:
577                 assert(struct_line == "} %s_Tag;" % struct_name)
578             elif idx == len(tag_field_lines) - 1:
579                 assert(struct_line == "")
580             else:
581                 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
582                 fields = []
583                 if "LDK" + variant_name in union_enum_items:
584                     enum_var_lines = union_enum_items["LDK" + variant_name]
585                     for idx, (field, field_docs) in enumerate(enum_var_lines):
586                         if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
587                             field_ty = type_mapping_generator.map_type(field.strip(' ;'), False, None, False, True)
588                             if field_docs is not None and doc_to_field_nullable(field_docs):
589                                 field_ty.nullable = True
590                             fields.append((field_ty, field_docs))
591                     enum_variants.append(ComplexEnumVariantInfo(variant_name, fields, False))
592                 elif camel_to_snake(variant_name) in inline_enum_variants:
593                     # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
594                     # docs through to there, and then potentially mark the field nullable.
595                     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))
596                     enum_variants.append(ComplexEnumVariantInfo(variant_name, fields, True))
597                 else:
598                     enum_variants.append(ComplexEnumVariantInfo(variant_name, fields, True))
599
600         with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
601             (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
602
603             out_java_enum.write(out_java_enum_addendum)
604             out_java.write(out_java_addendum)
605             write_c(out_c_addendum)
606
607     def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
608         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
609             field_var_convs = []
610             flattened_field_var_convs = []
611             for var_line in field_var_lines:
612                 if var_line.group(1) in trait_structs:
613                     field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
614                     flattened_field_var_convs.append((var_line.group(1), var_line.group(2), ))
615                     flattened_field_var_convs.extend(trait_structs[var_line.group(1)])
616                 else:
617                     mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
618                     field_var_convs.append(mapped)
619                     flattened_field_var_convs.append(mapped)
620             trait_structs[struct_name] = field_var_convs
621
622             field_fns = []
623             for fn_docs, fn_line in trait_fn_lines:
624                 if fn_line == "cloned":
625                     ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
626                     field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
627                 else:
628                     ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
629                     is_const = fn_line.group(4) is not None
630                     (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
631                     if ret_nullable:
632                         assert False # This isn't yet handled on the Java side
633                         ret_ty_info.nullable = True
634
635                     arg_tys = []
636                     for idx, arg in enumerate(fn_line.group(5).split(',')):
637                         if arg == "":
638                             continue
639                         arg_conv_info = type_mapping_generator.map_type(arg, True, None, False, False)
640                         if arg_conv_info.arg_name in nullable_params:
641                             # Types that are actually null instead of all-0s aren't yet handled on the Java side:
642                             assert arg_conv_info.rust_obj == "LDKPublicKey"
643                             arg_conv_info.nullable = True
644                         arg_tys.append(arg_conv_info)
645                     field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
646
647             (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)
648             write_c(out_c_addendum)
649             out_java_trait.write(out_java_trait_addendum)
650             out_java.write(out_java_addendum)
651
652         for fn_docs, fn_line in trait_fn_lines:
653             if fn_line == "cloned":
654                 continue
655             # For now, just disable enabling the _call_log - we don't know how to inverse-map String
656             is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
657             if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
658                 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"
659                 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)
660         for idx, var_line in enumerate(field_var_lines):
661             if var_line.group(1) not in trait_structs:
662                 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
663                 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
664                 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
665                 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
666                 write_c("}\n")
667                 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"
668                 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)
669
670     def map_result(struct_name, res_ty, err_ty):
671         result_types.add(struct_name)
672         human_ty = struct_name.replace("LDKCResult", "Result")
673         with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
674             out_java_struct.write(consts.hu_struct_file_prefix)
675             out_java_struct.write("public class " + human_ty + " extends CommonBase {\n")
676             out_java_struct.write("\tprivate " + human_ty + "(Object _dummy, long ptr) { super(ptr); }\n")
677             out_java_struct.write("\tprotected void finalize() throws Throwable {\n")
678             out_java_struct.write("\t\tif (ptr != 0) { bindings." + struct_name.replace("LDK","") + "_free(ptr); } super.finalize();\n")
679             out_java_struct.write("\t}\n\n")
680             out_java_struct.write("\tstatic " + human_ty + " constr_from_ptr(long ptr) {\n")
681             out_java_struct.write("\t\tif (bindings." + struct_name + "_result_ok(ptr)) {\n")
682             out_java_struct.write("\t\t\treturn new " + human_ty + "_OK(null, ptr);\n")
683             out_java_struct.write("\t\t} else {\n")
684             out_java_struct.write("\t\t\treturn new " + human_ty + "_Err(null, ptr);\n")
685             out_java_struct.write("\t\t}\n")
686             out_java_struct.write("\t}\n")
687
688             res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
689             err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
690             can_clone = True
691             if not res_map.is_native_primitive and (res_map.rust_obj.replace("LDK", "") + "_clone" not in clone_fns):
692                 can_clone = False
693             if not err_map.is_native_primitive and (err_map.rust_obj.replace("LDK", "") + "_clone" not in clone_fns):
694                 can_clone = False
695
696             out_java.write("\tpublic static native boolean " + struct_name + "_result_ok(long arg);\n")
697             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")
698             write_c("\treturn ((" + struct_name + "*)arg)->result_ok;\n")
699             write_c("}\n")
700
701             out_java.write("\tpublic static native " + res_map.java_ty + " " + struct_name + "_get_ok(long arg);\n")
702             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")
703             write_c("\t" + struct_name + " *val = (" + struct_name + "*)(arg & ~1);\n")
704             write_c("\tCHECK(val->result_ok);\n\t")
705             out_java_struct.write("\tpublic static final class " + human_ty + "_OK extends " + human_ty + " {\n")
706             if res_map.ret_conv is not None:
707                 write_c(res_map.ret_conv[0].replace("\n", "\n\t") + "(*val->contents.result)")
708                 write_c(res_map.ret_conv[1].replace("\n", "\n\t") + "\n\treturn " + res_map.ret_conv_name)
709             else:
710                 write_c("return *val->contents.result")
711             write_c(";\n}\n")
712
713             if res_map.java_hu_ty != "void":
714                 out_java_struct.write("\t\tpublic final " + res_map.java_hu_ty + " res;\n")
715             out_java_struct.write("\t\tprivate " + human_ty + "_OK(Object _dummy, long ptr) {\n")
716             out_java_struct.write("\t\t\tsuper(_dummy, ptr);\n")
717             if res_map.java_hu_ty == "void":
718                 pass
719             elif res_map.to_hu_conv is not None:
720                 out_java_struct.write("\t\t\t" + res_map.java_ty + " res = bindings." + struct_name + "_get_ok(ptr);\n")
721                 out_java_struct.write("\t\t\t" + res_map.to_hu_conv.replace("\n", "\n\t\t\t"))
722                 out_java_struct.write("\n\t\t\tthis.res = " + res_map.to_hu_conv_name + ";\n")
723             else:
724                 out_java_struct.write("\t\t\tthis.res = bindings." + struct_name + "_get_ok(ptr);\n")
725             out_java_struct.write("\t\t}\n")
726             out_java_struct.write("\t}\n\n")
727
728             out_java.write("\tpublic static native " + err_map.java_ty + " " + struct_name + "_get_err(long arg);\n")
729             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")
730             write_c("\t" + struct_name + " *val = (" + struct_name + "*)(arg & ~1);\n")
731             write_c("\tCHECK(!val->result_ok);\n\t")
732             out_java_struct.write("\tpublic static final class " + human_ty + "_Err extends " + human_ty + " {\n")
733             if err_map.ret_conv is not None:
734                 write_c(err_map.ret_conv[0].replace("\n", "\n\t") + "(*val->contents.err)")
735                 write_c(err_map.ret_conv[1].replace("\n", "\n\t") + "\n\treturn " + err_map.ret_conv_name)
736             else:
737                 write_c("return *val->contents.err")
738             write_c(";\n}\n")
739
740             if err_map.java_hu_ty != "void":
741                 out_java_struct.write("\t\tpublic final " + err_map.java_hu_ty + " err;\n")
742             out_java_struct.write("\t\tprivate " + human_ty + "_Err(Object _dummy, long ptr) {\n")
743             out_java_struct.write("\t\t\tsuper(_dummy, ptr);\n")
744             if err_map.java_hu_ty == "void":
745                 pass
746             elif err_map.to_hu_conv is not None:
747                 out_java_struct.write("\t\t\t" + err_map.java_ty + " err = bindings." + struct_name + "_get_err(ptr);\n")
748                 out_java_struct.write("\t\t\t" + err_map.to_hu_conv.replace("\n", "\n\t\t\t"))
749                 out_java_struct.write("\n\t\t\tthis.err = " + err_map.to_hu_conv_name + ";\n")
750             else:
751                 out_java_struct.write("\t\t\tthis.err = bindings." + struct_name + "_get_err(ptr);\n")
752             out_java_struct.write("\t\t}\n")
753
754             out_java_struct.write("\t}\n\n")
755
756     def map_tuple(struct_name, field_lines):
757         out_java.write("\tpublic static native long " + struct_name + "_new(")
758         write_c(consts.c_fn_ty_pfx + consts.ptr_c_ty + " " + consts.c_fn_name_define_pfx(struct_name + "_new", len(field_lines) > 3))
759         ty_list = []
760         for idx, (line, _) in enumerate(field_lines):
761             if idx != 0 and idx < len(field_lines) - 2:
762                 ty_info = java_c_types(line.strip(';'), None)
763                 if idx != 1:
764                     out_java.write(", ")
765                     write_c(", ")
766                 e = chr(ord('a') + idx - 1)
767                 out_java.write(ty_info.java_ty + " " + e)
768                 write_c(ty_info.c_ty + " " + e)
769                 ty_list.append(ty_info)
770         tuple_types[struct_name] = (ty_list, struct_name)
771         out_java.write(");\n")
772         write_c(") {\n")
773         write_c("\t" + struct_name + "* ret = MALLOC(sizeof(" + struct_name + "), \"" + struct_name + "\");\n")
774         for idx, (line, _) in enumerate(field_lines):
775             if idx != 0 and idx < len(field_lines) - 2:
776                 ty_info = type_mapping_generator.map_type(line.strip(';'), False, None, False, False)
777                 e = chr(ord('a') + idx - 1)
778                 if ty_info.arg_conv is not None:
779                     write_c("\t" + ty_info.arg_conv.replace("\n", "\n\t"))
780                     write_c("\n\tret->" + e + " = " + ty_info.arg_conv_name + ";\n")
781                 else:
782                     write_c("\tret->" + e + " = " + e + ";\n")
783                 if ty_info.arg_conv_cleanup is not None:
784                     write_c("\t//TODO: Really need to call " + ty_info.arg_conv_cleanup + " here\n")
785         write_c("\treturn (uint64_t)ret;\n")
786         write_c("}\n")
787
788         for idx, ty_info in enumerate(ty_list):
789             e = chr(ord('a') + idx)
790             out_java.write("\tpublic static native " + ty_info.java_ty + " " + struct_name + "_get_" + e + "(long ptr);\n")
791             write_c(consts.c_fn_ty_pfx + ty_info.c_ty + " " + consts.c_fn_name_define_pfx(struct_name + "_get_" + e, True) + consts.ptr_c_ty + " ptr) {\n")
792             write_c("\t" + struct_name + " *tuple = (" + struct_name + "*)(ptr & ~1);\n")
793             conv_info = type_mapping_generator.map_type_with_info(ty_info, False, None, False, True)
794             if conv_info.ret_conv is not None:
795                 write_c("\t" + conv_info.ret_conv[0].replace("\n", "\n\t") + "tuple->" + e + conv_info.ret_conv[1].replace("\n", "\n\t") + "\n")
796                 write_c("\treturn " + conv_info.ret_conv_name + ";\n")
797             else:
798                 write_c("\treturn tuple->" + e + ";\n")
799             write_c("}\n")
800
801     out_java.write(consts.bindings_header.replace('<git_version_ldk_garbagecollected>', local_git_version))
802
803     with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
804         out_java_struct.write(consts.common_base)
805
806     block_comment = None
807     last_block_comment = None
808     cur_block_obj = None
809
810     const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
811
812     line_indicates_result_regex = re.compile("^   union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
813     line_indicates_vec_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
814     line_indicates_opaque_regex = re.compile("^   bool is_owned;$")
815     line_indicates_trait_regex = re.compile("^   (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
816     assert(line_indicates_trait_regex.match("   uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
817     assert(line_indicates_trait_regex.match("   struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
818     assert(line_indicates_trait_regex.match("   struct LDKCVec_u8Z (*write)(const void *this_arg);"))
819     line_indicates_trait_clone_regex = re.compile("^   void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
820     assert(line_indicates_trait_clone_regex.match("   void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
821     line_field_var_regex = re.compile("^   struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
822     assert(line_field_var_regex.match("   struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
823     assert(line_field_var_regex.match("   struct LDKChannelPublicKeys pubkeys;"))
824     struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
825     assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
826     assert(struct_name_regex.match("typedef enum LDKNetwork {"))
827
828     union_enum_items = {}
829     result_ptr_struct_items = {}
830     for line in in_h:
831         if block_comment is not None:
832             if line.endswith("*/\n"):
833                 last_block_comment = block_comment.strip("\n")
834                 block_comment = None
835             else:
836                 block_comment = block_comment + line.strip(" /*")
837         elif cur_block_obj is not None:
838             cur_block_obj  = cur_block_obj + line
839             if line.startswith("} "):
840                 field_lines = []
841                 struct_name = None
842                 vec_ty = None
843                 obj_lines = cur_block_obj.split("\n")
844                 is_opaque = False
845                 result_contents = None
846                 is_unitary_enum = False
847                 is_union_enum = False
848                 is_union = False
849                 is_tuple = False
850                 trait_fn_lines = []
851                 field_var_lines = []
852                 last_struct_block_comment = None
853
854                 for idx, struct_line in enumerate(obj_lines):
855                     if struct_line.strip().startswith("/*"):
856                         block_comment = struct_line.strip(" /*")
857                     if block_comment is not None:
858                         if struct_line.endswith("*/"):
859                             last_struct_block_comment = block_comment.strip("\n")
860                             block_comment = None
861                         else:
862                             block_comment = block_comment + "\n" + struct_line.strip(" /*")
863                     else:
864                         struct_name_match = struct_name_regex.match(struct_line)
865                         if struct_name_match is not None:
866                             struct_name = struct_name_match.group(3)
867                             if struct_name_match.group(1) == "enum":
868                                 if not struct_name.endswith("_Tag"):
869                                     is_unitary_enum = True
870                                 else:
871                                     is_union_enum = True
872                             elif struct_name_match.group(1) == "union":
873                                 is_union = True
874                         if line_indicates_opaque_regex.match(struct_line):
875                             is_opaque = True
876                         result_match = line_indicates_result_regex.match(struct_line)
877                         if result_match is not None:
878                             result_contents = result_match.group(1)
879                         vec_ty_match = line_indicates_vec_regex.match(struct_line)
880                         if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
881                             vec_ty = vec_ty_match.group(2)
882                         elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
883                             is_tuple = True
884                         trait_fn_match = line_indicates_trait_regex.match(struct_line)
885                         if trait_fn_match is not None:
886                             trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
887                         trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
888                         if trait_clone_fn_match is not None:
889                             trait_fn_lines.append((last_struct_block_comment, "cloned"))
890                         field_var_match = line_field_var_regex.match(struct_line)
891                         if field_var_match is not None:
892                             field_var_lines.append(field_var_match)
893                         field_lines.append((struct_line, last_struct_block_comment))
894                         last_struct_block_comment = None
895
896                 assert(struct_name is not None)
897                 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))
898                 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))
899                 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))
900                 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))
901                 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))
902                 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))
903                 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))
904
905                 if is_opaque:
906                     opaque_structs.add(struct_name)
907                     with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
908                         out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
909                         last_block_comment = None
910                         out_java_struct.write(out_opaque_struct_human)
911                 elif result_contents is not None:
912                     assert result_contents in result_ptr_struct_items
913                     res_ty, err_ty = result_ptr_struct_items[result_contents]
914                     map_result(struct_name, res_ty, err_ty)
915                 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
916                     for line, _ in field_lines:
917                         if line.endswith("*result;"):
918                             res_ty = line[:-8].strip()
919                         elif line.endswith("*err;"):
920                             err_ty = line[:-5].strip()
921                     result_ptr_struct_items[struct_name] = (res_ty, err_ty)
922                     result_types.add(struct_name[:-3])
923                 elif is_tuple:
924                     map_tuple(struct_name, field_lines)
925                 elif vec_ty is not None:
926                     ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
927                     if len(ty_info.java_fn_ty_arg) == 1: # ie we're a primitive of some form
928                         out_java.write("\tpublic static native long " + struct_name + "_new(" + ty_info.java_ty + "[] elems);\n")
929                         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")
930                         write_c("\t" + struct_name + " *ret = MALLOC(sizeof(" + struct_name + "), \"" + struct_name + "\");\n")
931                         write_c("\tret->datalen = " + consts.get_native_arr_len_call[0] + "elems" + consts.get_native_arr_len_call[1] + ";\n")
932                         write_c("\tif (ret->datalen == 0) {\n")
933                         write_c("\t\tret->data = NULL;\n")
934                         write_c("\t} else {\n")
935                         write_c("\t\tret->data = MALLOC(sizeof(" + vec_ty + ") * ret->datalen, \"" + struct_name + " Data\");\n")
936                         native_arr_ptr_call = consts.get_native_arr_ptr_call(ty_info.ty_info)
937                         write_c("\t\t" + ty_info.c_ty + " *java_elems = " + native_arr_ptr_call[0] + "elems" + native_arr_ptr_call[1] + ";\n")
938                         write_c("\t\tfor (size_t i = 0; i < ret->datalen; i++) {\n")
939                         if ty_info.arg_conv is not None:
940                             write_c("\t\t\t" + ty_info.c_ty + " arr_elem = java_elems[i];\n")
941                             write_c("\t\t\t" + ty_info.arg_conv.replace("\n", "\n\t\t\t") + "\n")
942                             write_c("\t\t\tret->data[i] = " + ty_info.arg_conv_name + ";\n")
943                             assert ty_info.arg_conv_cleanup is None
944                         else:
945                             write_c("\t\t\tret->data[i] = java_elems[i];\n")
946                         write_c("\t\t}\n")
947                         cleanup = consts.release_native_arr_ptr_call(ty_info.ty_info, "elems", "java_elems")
948                         if cleanup is not None:
949                             write_c("\t\t" + cleanup + ";\n")
950                         write_c("\t}\n")
951                         write_c("\treturn (uint64_t)ret;\n")
952                         write_c("}\n")
953
954                     if ty_info.is_native_primitive:
955                         clone_fns.add(struct_name.replace("LDK", "") + "_clone")
956                         write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
957                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
958                         write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
959                         write_c("\treturn ret;\n}\n")
960                     elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
961                         ty_name = "CVec_" + ty_info.rust_obj.replace("LDK", "") + "Z";
962                         clone_fns.add(ty_name + "_clone")
963                         write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
964                         write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
965                         write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
966                         write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
967                         write_c("\t}\n\treturn ret;\n}\n")
968                 elif is_union_enum:
969                     assert(struct_name.endswith("_Tag"))
970                     struct_name = struct_name[:-4]
971                     union_enum_items[struct_name] = {"field_lines": field_lines}
972                 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
973                     enum_var_name = struct_name.split("_")
974                     union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
975                 elif struct_name in union_enum_items:
976                     tuple_variants = {}
977                     elem_items = -1
978                     for line, _ in field_lines:
979                         if line == "      struct {":
980                             elem_items = 0
981                         elif line == "      };":
982                             elem_items = -1
983                         elif elem_items > -1:
984                             line = line.strip()
985                             if line.startswith("struct "):
986                                 line = line[7:]
987                             elif line.startswith("enum "):
988                                 line = line[5:]
989                             split = line.split(" ")
990                             assert len(split) == 2
991                             tuple_variants[split[1].strip(";")] = split[0]
992                             elem_items += 1
993                             if elem_items > 1:
994                                 # We don't currently support tuple variant with more than one element
995                                 assert False
996                     map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
997                     last_block_comment = None
998                 elif is_unitary_enum:
999                     map_unitary_enum(struct_name, field_lines, last_block_comment)
1000                     last_block_comment = None
1001                 elif len(trait_fn_lines) > 0:
1002                     map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
1003                 elif struct_name == "LDKTxOut":
1004                     with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
1005                         out_java_struct.write(consts.hu_struct_file_prefix)
1006                         out_java_struct.write("public class TxOut extends CommonBase{\n")
1007                         out_java_struct.write("\t/** The script_pubkey in this output */\n")
1008                         out_java_struct.write("\tpublic final byte[] script_pubkey;\n")
1009                         out_java_struct.write("\t/** The value, in satoshis, of this output */\n")
1010                         out_java_struct.write("\tpublic final long value;\n")
1011                         out_java_struct.write("\n")
1012                         out_java_struct.write("\tTxOut(java.lang.Object _dummy, long ptr) {\n")
1013                         out_java_struct.write("\t\tsuper(ptr);\n")
1014                         out_java_struct.write("\t\tthis.script_pubkey = bindings.TxOut_get_script_pubkey(ptr);\n")
1015                         out_java_struct.write("\t\tthis.value = bindings.TxOut_get_value(ptr);\n")
1016                         out_java_struct.write("\t}\n")
1017                         out_java_struct.write("\tpublic TxOut(long value, byte[] script_pubkey) {\n")
1018                         out_java_struct.write("\t\tsuper(bindings.TxOut_new(script_pubkey, value));\n")
1019                         out_java_struct.write("\t\tthis.script_pubkey = bindings.TxOut_get_script_pubkey(ptr);\n")
1020                         out_java_struct.write("\t\tthis.value = bindings.TxOut_get_value(ptr);\n")
1021                         out_java_struct.write("\t}\n")
1022                         out_java_struct.write("\n")
1023                         out_java_struct.write("\t@Override @SuppressWarnings(\"deprecation\")\n")
1024                         out_java_struct.write("\tprotected void finalize() throws Throwable {\n")
1025                         out_java_struct.write("\t\tsuper.finalize();\n")
1026                         out_java_struct.write("\t\tif (ptr != 0) { bindings.TxOut_free(ptr); }\n")
1027                         out_java_struct.write("\t}\n")
1028                         out_java_struct.write("\n")
1029                         out_java_struct.write("}")
1030                         fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
1031                         write_c(fn_line + " {")
1032                         write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
1033                         write_c("}")
1034                         map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
1035                         fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
1036                         write_c(fn_line + " {")
1037                         write_c("\treturn thing->value;")
1038                         write_c("}")
1039                         map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
1040                 else:
1041                     pass # Everything remaining is a byte[] or some form
1042                 cur_block_obj = None
1043         else:
1044             fn_ptr = fn_ptr_regex.match(line)
1045             fn_ret_arr = fn_ret_arr_regex.match(line)
1046             reg_fn = reg_fn_regex.match(line)
1047             const_val = const_val_regex.match(line)
1048
1049             if line.startswith("#include <"):
1050                 pass
1051             elif line.startswith("/*"):
1052                 if not line.endswith("*/\n"):
1053                     block_comment = line.strip(" /*")
1054             elif line.startswith("typedef enum "):
1055                 cur_block_obj = line
1056             elif line.startswith("typedef struct "):
1057                 cur_block_obj = line
1058             elif line.startswith("typedef union "):
1059                 cur_block_obj = line
1060             elif fn_ptr is not None:
1061                 map_fn(line, fn_ptr, None, None, last_block_comment)
1062                 last_block_comment = None
1063             elif fn_ret_arr is not None:
1064                 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
1065                 last_block_comment = None
1066             elif reg_fn is not None:
1067                 map_fn(line, reg_fn, None, None, last_block_comment)
1068                 last_block_comment = None
1069             elif const_val_regex is not None:
1070                 # TODO Map const variables
1071                 pass
1072             else:
1073                 assert(line == "\n")
1074
1075     out_java.write(consts.bindings_footer)
1076     for struct_name in opaque_structs:
1077         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1078             out_java_struct.write("}\n")
1079     for struct_name in trait_structs:
1080         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1081             out_java_struct.write("}\n")
1082     for struct_name in complex_enums:
1083         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
1084             out_java_struct.write("}\n")
1085     for struct_name in result_types:
1086         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
1087             out_java_struct.write("}\n")
1088
1089 with open(sys.argv[4], "w") as out_c:
1090     out_c.write(consts.c_file_pfx.replace('<git_version_ldk_garbagecollected>', local_git_version))
1091     out_c.write(consts.init_str())
1092     out_c.write(c_file)
1093 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
1094     util.write(consts.util_fn_sfx)