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