don't return values for void types
[ldk-java] / genbindings.py
index 2443dac65feaed290d4088dd00ad106f0fb21901..27e145ced7b19166a47a9fbbf9af6ecc507a17fc 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 import sys, re
 
 #!/usr/bin/env python3
 import sys, re
 
-if len(sys.argv) != 7:
+if len(sys.argv) < 7:
     print("USAGE: /path/to/lightning.h /path/to/bindings/output /path/to/bindings/ /path/to/bindings/output.c debug lang")
     sys.exit(1)
 
     print("USAGE: /path/to/lightning.h /path/to/bindings/output /path/to/bindings/ /path/to/bindings/output.c debug lang")
     sys.exit(1)
 
@@ -13,14 +13,21 @@ else:
     print("debug should be true or false and indicates whether to track allocations and ensure we don't leak")
     sys.exit(1)
 
     print("debug should be true or false and indicates whether to track allocations and ensure we don't leak")
     sys.exit(1)
 
+target = None
 if sys.argv[6] == "java":
     from java_strings import Consts
 elif sys.argv[6] == "typescript":
 if sys.argv[6] == "java":
     from java_strings import Consts
 elif sys.argv[6] == "typescript":
+    import typescript_strings
     from typescript_strings import Consts
     from typescript_strings import Consts
+    target = typescript_strings.Target.NODEJS
+    if len(sys.argv) == 8 and sys.argv[7] == 'browser':
+        target = typescript_strings.Target.BROWSER
 else:
     print("Only java or typescript can be set for lang")
     sys.exit(1)
 else:
     print("Only java or typescript can be set for lang")
     sys.exit(1)
-consts = Consts(DEBUG)
+
+
+consts = Consts(DEBUG, target=target)
 
 from bindingstypes import *
 
 
 from bindingstypes import *
 
@@ -325,6 +332,13 @@ reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
 clone_fns = set()
 constructor_fns = {}
 c_array_class_caches = set()
 clone_fns = set()
 constructor_fns = {}
 c_array_class_caches = set()
+
+
+from gen_type_mapping import TypeMappingGenerator
+type_mapping_generator = TypeMappingGenerator(java_c_types, consts, c_array_class_caches, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
+
+
+
 with open(sys.argv[1]) as in_h:
     for line in in_h:
         reg_fn = reg_fn_regex.match(line)
 with open(sys.argv[1]) as in_h:
     for line in in_h:
         reg_fn = reg_fn_regex.match(line)
@@ -350,573 +364,62 @@ write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struc
 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
 
 with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
 
 with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
-    def map_type(fn_arg, print_void, ret_arr_len, is_free, holds_ref):
-        ty_info = java_c_types(fn_arg, ret_arr_len)
-        return map_type_with_info(ty_info, print_void, ret_arr_len, is_free, holds_ref)
-
-    def map_type_with_info(ty_info, print_void, ret_arr_len, is_free, holds_ref):
-        if ty_info.c_ty == "void":
-            if not print_void:
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = None, arg_conv_name = None, arg_conv_cleanup = None,
-                    ret_conv = None, ret_conv_name = None, to_hu_conv = None, to_hu_conv_name = None, from_hu_conv = None)
-        if ty_info.c_ty.endswith("Array"):
-            arr_len = ty_info.arr_len
-            if arr_len is not None:
-                arr_name = ty_info.var_name
-            else:
-                arr_name = "ret"
-                arr_len = ret_arr_len
-            if ty_info.c_ty == "int8_tArray":
-                (set_pfx, set_sfx) = consts.set_native_arr_contents(arr_name + "_arr", arr_len, ty_info)
-                ret_conv = ("int8_tArray " + arr_name + "_arr = " + consts.create_native_arr_call(arr_len, ty_info) + ";\n" + set_pfx, "")
-                arg_conv_cleanup = None
-                if not arr_len.isdigit():
-                    arg_conv = ty_info.rust_obj + " " + arr_name + "_ref;\n"
-                    arg_conv = arg_conv + arr_name + "_ref." + arr_len + " = " +  consts.get_native_arr_len_call[0] + arr_name + consts.get_native_arr_len_call[1] + ";\n"
-                    if (not ty_info.is_ptr or not holds_ref) and ty_info.rust_obj != "LDKu8slice":
-                        arg_conv = arg_conv + arr_name + "_ref." + ty_info.arr_access + " = MALLOC(" + arr_name + "_ref." + arr_len + ", \"" + ty_info.rust_obj + " Bytes\");\n"
-                        arg_conv = arg_conv + consts.get_native_arr_contents(arr_name, arr_name + "_ref." + ty_info.arr_access, arr_name + "_ref." + arr_len, ty_info, True) + ";"
-                    else:
-                        arg_conv = arg_conv + arr_name + "_ref." + ty_info.arr_access + " = " + consts.get_native_arr_contents(arr_name, "NO_DEST", arr_name + "_ref." + arr_len, ty_info, False) + ";"
-                        arg_conv_cleanup = consts.cleanup_native_arr_ref_contents(arr_name, arr_name + "_ref." + ty_info.arr_access, arr_name + "_ref." + arr_len, ty_info)
-                    if ty_info.rust_obj == "LDKTransaction":
-                        arg_conv = arg_conv + "\n" + arr_name + "_ref.data_is_owned = " + str(holds_ref).lower() + ";"
-                    ret_conv = (ty_info.rust_obj + " " + arr_name + "_var = ", "")
-                    ret_conv = (ret_conv[0], ";\nint8_tArray " + arr_name + "_arr = " + consts.create_native_arr_call(arr_name + "_var." + arr_len, ty_info) + ";\n")
-                    (pfx, sfx) = consts.set_native_arr_contents(arr_name + "_arr", arr_name + "_var." + arr_len, ty_info)
-                    ret_conv = (ret_conv[0], ret_conv[1] + pfx + arr_name + "_var." + ty_info.arr_access + sfx + ";")
-                    if not holds_ref and ty_info.rust_obj != "LDKu8slice":
-                        ret_conv = (ret_conv[0], ret_conv[1] + "\n" + ty_info.rust_obj.replace("LDK", "") + "_free(" + arr_name + "_var);")
-                elif ty_info.rust_obj is not None:
-                    arg_conv = ty_info.rust_obj + " " + arr_name + "_ref;\n"
-                    arg_conv = arg_conv + "CHECK(" + consts.get_native_arr_len_call[0] + arr_name + consts.get_native_arr_len_call[1] + " == " + arr_len + ");\n"
-                    arg_conv = arg_conv + consts.get_native_arr_contents(arr_name, arr_name + "_ref." + ty_info.arr_access, arr_len, ty_info, True) + ";"
-                    ret_conv = (ret_conv[0], "." + ty_info.arr_access + set_sfx + ";")
-                else:
-                    arg_conv = "unsigned char " + arr_name + "_arr[" + arr_len + "];\n"
-                    arg_conv = arg_conv + "CHECK(" + consts.get_native_arr_len_call[0] + arr_name + consts.get_native_arr_len_call[1] + " == " + arr_len + ");\n"
-                    arg_conv = arg_conv + consts.get_native_arr_contents(arr_name, arr_name + "_arr", arr_len, ty_info, True) + ";\n"
-                    arg_conv = arg_conv + "unsigned char (*" + arr_name + "_ref)[" + arr_len + "] = &" + arr_name + "_arr;"
-                    ret_conv = (ret_conv[0] + "*", set_sfx + ";")
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = arg_conv, arg_conv_name = arr_name + "_ref", arg_conv_cleanup = arg_conv_cleanup,
-                    ret_conv = ret_conv, ret_conv_name = arr_name + "_arr", to_hu_conv = None, to_hu_conv_name = None, from_hu_conv = None)
-            else:
-                assert not arr_len.isdigit() # fixed length arrays not implemented
-                assert ty_info.java_ty[len(ty_info.java_ty) - 2:] == "[]"
-                conv_name = "arr_conv_" + str(len(ty_info.java_hu_ty))
-                idxc = chr(ord('a') + (len(ty_info.java_hu_ty) % 26))
-                ty_info.subty.var_name = conv_name
-                #XXX: We'd really prefer to only ever set to False, avoiding lots of clone, but need smarter free logic
-                #if ty_info.is_ptr or holds_ref:
-                #    ty_info.subty.requires_clone = False
-                ty_info.subty.requires_clone = not ty_info.is_ptr or not holds_ref
-                subty = map_type_with_info(ty_info.subty, False, None, is_free, holds_ref)
-                if arr_name == "":
-                    arr_name = "arg"
-                arg_conv = ty_info.rust_obj + " " + arr_name + "_constr;\n"
-                arg_conv = arg_conv + arr_name + "_constr." + arr_len + " = " + consts.get_native_arr_len_call[0] + arr_name + consts.get_native_arr_len_call[1] + ";\n"
-                arg_conv = arg_conv + "if (" + arr_name + "_constr." + arr_len + " > 0)\n"
-                if subty.rust_obj is None:
-                    szof = subty.c_ty
-                else:
-                    szof = subty.rust_obj
-                arg_conv = arg_conv + "\t" + arr_name + "_constr." + ty_info.arr_access + " = MALLOC(" + arr_name + "_constr." + arr_len + " * sizeof(" + szof + "), \"" + ty_info.rust_obj + " Elements\");\n"
-                arg_conv = arg_conv + "else\n"
-                arg_conv = arg_conv + "\t" + arr_name + "_constr." + ty_info.arr_access + " = NULL;\n"
-                get_arr = consts.get_native_arr_contents(arr_name, "NO_DEST", arr_name + "_constr." + arr_len, ty_info, False)
-                if get_arr != None:
-                    arg_conv = arg_conv + subty.c_ty + "* " + arr_name + "_vals = " + get_arr + ";\n"
-                arg_conv = arg_conv + "for (size_t " + idxc + " = 0; " + idxc + " < " + arr_name + "_constr." + arr_len + "; " + idxc + "++) {\n"
-                if get_arr != None:
-                    arg_conv = arg_conv + "\t" + subty.c_ty + " " + conv_name + " = " + arr_name + "_vals[" + idxc + "];"
-                    if subty.arg_conv is not None:
-                        arg_conv = arg_conv + "\n\t" + subty.arg_conv.replace("\n", "\n\t")
-                else:
-                    arg_conv = arg_conv + "\t" + subty.c_ty + " " + conv_name + " = " + consts.get_native_arr_elem(arr_name, idxc, ty_info) + ";\n"
-                    arg_conv = arg_conv + "\t" + subty.arg_conv.replace("\n", "\n\t")
-                arg_conv = arg_conv + "\n\t" + arr_name + "_constr." + ty_info.arr_access + "[" + idxc + "] = " + subty.arg_conv_name + ";\n}"
-                if get_arr != None:
-                    cleanup = consts.cleanup_native_arr_ref_contents(arr_name, arr_name + "_vals", arr_name + "_constr." + arr_len, ty_info)
-                    if cleanup is not None:
-                        arg_conv = arg_conv + "\n" + cleanup + ";"
-                if ty_info.is_ptr:
-                    arg_conv_name = "&" + arr_name + "_constr"
-                else:
-                    arg_conv_name = arr_name + "_constr"
-                arg_conv_cleanup = None
-                if ty_info.is_ptr:
-                    arg_conv_cleanup = "FREE(" + arr_name + "_constr." + ty_info.arr_access + ");"
-
-                if arr_name == "arg":
-                    arr_name = "ret"
-                ret_conv = (ty_info.rust_obj + " " + arr_name + "_var = ", "")
-                if subty.ret_conv is None:
-                    ret_conv = ("DUMMY", "DUMMY")
-                elif not ty_info.java_ty[:len(ty_info.java_ty) - 2].endswith("[]"):
-                    ret_conv = (ret_conv[0], ";\n" + ty_info.c_ty + " " + arr_name + "_arr = " + consts.create_native_arr_call(arr_name + "_var." + arr_len, ty_info) + ";\n")
-                    ret_conv = (ret_conv[0], ret_conv[1] + subty.c_ty + " *" + arr_name + "_arr_ptr = " + consts.get_native_arr_ptr_call[0] + arr_name + "_arr" + consts.get_native_arr_ptr_call[1] + ";\n")
-                    ret_conv = (ret_conv[0], ret_conv[1] + "for (size_t " + idxc + " = 0; " + idxc + " < " + arr_name + "_var." + arr_len + "; " + idxc + "++) {\n")
-                    ret_conv = (ret_conv[0], ret_conv[1] + "\t" + subty.ret_conv[0].replace("\n", "\n\t"))
-                    ret_conv = (ret_conv[0], ret_conv[1] + arr_name + "_var." + ty_info.arr_access + "[" + idxc + "]" + subty.ret_conv[1].replace("\n", "\n\t"))
-                    ret_conv = (ret_conv[0], ret_conv[1] + "\n\t" + arr_name + "_arr_ptr[" + idxc + "] = " + subty.ret_conv_name + ";\n}")
-                    cleanup = consts.release_native_arr_ptr_call(arr_name + "_arr", arr_name + "_arr_ptr")
-                    if cleanup is not None:
-                        ret_conv = (ret_conv[0], ret_conv[1] + "\n" + cleanup + ";")
-                else:
-                    assert ty_info.java_fn_ty_arg.startswith("[")
-                    clz_var = ty_info.java_fn_ty_arg[1:].replace("[", "arr_of_")
-                    c_array_class_caches.add(clz_var)
-                    ret_conv = (ret_conv[0], ";\n" + ty_info.c_ty + " " + arr_name + "_arr = (*env)->NewObjectArray(env, " + arr_name + "_var." + arr_len + ", " + clz_var + "_clz, NULL);\n")
-                    ret_conv = (ret_conv[0], ret_conv[1] + "for (size_t " + idxc + " = 0; " + idxc + " < " + arr_name + "_var." + arr_len + "; " + idxc + "++) {\n")
-                    ret_conv = (ret_conv[0], ret_conv[1] + "\t" + subty.ret_conv[0].replace("\n", "\n\t"))
-                    ret_conv = (ret_conv[0], ret_conv[1] + arr_name + "_var." + ty_info.arr_access + "[" + idxc + "]" + subty.ret_conv[1].replace("\n", "\n\t"))
-                    ret_conv = (ret_conv[0], ret_conv[1] + "\n\t(*env)->SetObjectArrayElement(env, " + arr_name + "_arr, " + idxc + ", " + subty.ret_conv_name + ");\n")
-                    ret_conv = (ret_conv[0], ret_conv[1] + "}")
-                if not holds_ref:
-                    # XXX: The commented if's are a bit smarter freeing, but we need to be a nudge smarter still
-                    # Note that we don't drop the full vec here - we're passing ownership to java (or have cloned) or free'd by now!
-                    ret_conv = (ret_conv[0], ret_conv[1] + "\nFREE(" + arr_name + "_var." + ty_info.arr_access + ");")
-                    #if subty.rust_obj is not None and subty.rust_obj in opaque_structs:
-                    #    ret_conv = (ret_conv[0], ret_conv[1] + "\nFREE(" + arr_name + "_var." + ty_info.arr_access + ");")
-                    #else:
-                    #    ret_conv = (ret_conv[0], ret_conv[1] + "\n" + ty_info.rust_obj.replace("LDK", "") + "_free(" + arr_name + "_var);")
-
-                to_hu_conv = None
-                to_hu_conv_name = None
-                if subty.to_hu_conv is not None:
-                    to_hu_conv = ty_info.java_hu_ty + " " + conv_name + "_arr = new " + ty_info.subty.java_hu_ty.split("<")[0] + "[" + arr_name + ".length];\n"
-                    to_hu_conv = to_hu_conv + "for (int " + idxc + " = 0; " + idxc + " < " + arr_name + ".length; " + idxc + "++) {\n"
-                    to_hu_conv = to_hu_conv + "\t" + subty.java_ty + " " + conv_name + " = " + arr_name + "[" + idxc + "];\n"
-                    to_hu_conv = to_hu_conv + "\t" + subty.to_hu_conv.replace("\n", "\n\t") + "\n"
-                    to_hu_conv = to_hu_conv + "\t" + conv_name + "_arr[" + idxc + "] = " + subty.to_hu_conv_name + ";\n}"
-                    to_hu_conv_name = conv_name + "_arr"
-                from_hu_conv = None
-                if subty.from_hu_conv is not None:
-                    if subty.java_ty == "long" and subty.java_hu_ty != "long":
-                        from_hu_conv = ("Arrays.stream(" + arr_name + ").mapToLong(" + conv_name + " -> " + subty.from_hu_conv[0] + ").toArray()", "/* TODO 2 " + subty.java_hu_ty + "  */")
-                    elif subty.java_ty == "long":
-                        from_hu_conv = ("Arrays.stream(" + arr_name + ").map(" + conv_name + " -> " + subty.from_hu_conv[0] + ").toArray()", "/* TODO 2 " + subty.java_hu_ty + "  */")
-                    else:
-                        from_hu_conv = ("(" + ty_info.java_ty + ")Arrays.stream(" + arr_name + ").map(" + conv_name + " -> " + subty.from_hu_conv[0] + ").toArray()", "/* TODO 2 " + subty.java_hu_ty + "  */")
-
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = arg_conv, arg_conv_name = arg_conv_name, arg_conv_cleanup = arg_conv_cleanup,
-                    ret_conv = ret_conv, ret_conv_name = arr_name + "_arr", to_hu_conv = to_hu_conv, to_hu_conv_name = to_hu_conv_name, from_hu_conv = from_hu_conv)
-        elif ty_info.java_ty == "String":
-            if ty_info.arr_access is None:
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = None, arg_conv_name = None, arg_conv_cleanup = None,
-                    ret_conv = ("jstring " + ty_info.var_name + "_conv = (*env)->NewStringUTF(env, ", ");"), ret_conv_name = ty_info.var_name + "_conv",
-                    to_hu_conv = None, to_hu_conv_name = None, from_hu_conv = None)
-            else:
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = None, arg_conv_name = None, arg_conv_cleanup = None,
-                    ret_conv = ("LDKStr " + ty_info.var_name + "_str = ",
-                        ";\nchar* " + ty_info.var_name + "_buf = MALLOC(" + ty_info.var_name + "_str." + ty_info.arr_len + " + 1, \"str conv buf\");\n" +
-                        "memcpy(" + ty_info.var_name + "_buf, " + ty_info.var_name + "_str." + ty_info.arr_access + ", " + ty_info.var_name + "_str." + ty_info.arr_len + ");\n" +
-                        ty_info.var_name + "_buf[" + ty_info.var_name + "_str." + ty_info.arr_len + "] = 0;\n" +
-                        "jstring " + ty_info.var_name + "_conv = (*env)->NewStringUTF(env, " + ty_info.var_name + "_str." + ty_info.arr_access + ");\n" +
-                        "FREE(" + ty_info.var_name + "_buf);"),
-                    ret_conv_name = ty_info.var_name + "_conv", to_hu_conv = None, to_hu_conv_name = None, from_hu_conv = None)
-        elif ty_info.var_name == "" and not print_void:
-            # We don't have a parameter name, and want one, just call it arg
-            if ty_info.rust_obj is not None:
-                assert(not is_free or ty_info.rust_obj not in opaque_structs)
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = ty_info.rust_obj + " arg_conv = *(" + ty_info.rust_obj + "*)arg;\nFREE((void*)arg);",
-                    arg_conv_name = "arg_conv", arg_conv_cleanup = None,
-                    ret_conv = None, ret_conv_name = None, to_hu_conv = "TODO 7", to_hu_conv_name = None, from_hu_conv = None)
-            else:
-                assert(not is_free)
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = None, arg_conv_name = "arg", arg_conv_cleanup = None,
-                    ret_conv = None, ret_conv_name = None, to_hu_conv = "TODO 8", to_hu_conv_name = None, from_hu_conv = None)
-        elif ty_info.rust_obj is None:
-            return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                arg_conv = None, arg_conv_name = ty_info.var_name, arg_conv_cleanup = None,
-                ret_conv = None, ret_conv_name = None, to_hu_conv = None, to_hu_conv_name = None, from_hu_conv = None)
-        else:
-            if ty_info.var_name == "":
-                ty_info.var_name = "ret"
-
-            if ty_info.rust_obj in opaque_structs:
-                opaque_arg_conv = ty_info.rust_obj + " " + ty_info.var_name + "_conv;\n"
-                opaque_arg_conv = opaque_arg_conv + ty_info.var_name + "_conv.inner = (void*)(" + ty_info.var_name + " & (~1));\n"
-                if ty_info.is_ptr and holds_ref:
-                    opaque_arg_conv = opaque_arg_conv + ty_info.var_name + "_conv.is_owned = false;"
-                else:
-                    opaque_arg_conv = opaque_arg_conv + ty_info.var_name + "_conv.is_owned = (" + ty_info.var_name + " & 1) || (" + ty_info.var_name + " == 0);"
-                if not is_free and (not ty_info.is_ptr or not holds_ref or ty_info.requires_clone == True) and ty_info.requires_clone != False:
-                    if (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
-                        # TODO: This is a bit too naive, even with the checks above, we really need to know if rust wants a ref or not, not just if its pass as a ptr.
-                        opaque_arg_conv = opaque_arg_conv + "\nif (" + ty_info.var_name + "_conv.inner != NULL)\n"
-                        opaque_arg_conv = opaque_arg_conv + "\t" + ty_info.var_name + "_conv = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&" + ty_info.var_name + "_conv);"
-                    elif ty_info.passed_as_ptr:
-                        opaque_arg_conv = opaque_arg_conv + "\n// Warning: we may need a move here but can't clone!"
-
-                opaque_ret_conv_suf = ";\n"
-                if not holds_ref and ty_info.is_ptr and (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns: # is_ptr, not holds_ref implies passing a pointed-to value to java, which needs copied
-                    opaque_ret_conv_suf = opaque_ret_conv_suf + "if (" + ty_info.var_name + "->inner != NULL)\n"
-                    opaque_ret_conv_suf = opaque_ret_conv_suf + "\t" + ty_info.var_name + "_var = " + ty_info.rust_obj.replace("LDK", "") + "_clone(" + ty_info.var_name + ");\n"
-                elif not holds_ref and ty_info.is_ptr:
-                    opaque_ret_conv_suf = opaque_ret_conv_suf + "// Warning: we may need a move here but can't clone!\n"
-
-                opaque_ret_conv_suf = opaque_ret_conv_suf + "CHECK((((long)" + ty_info.var_name + "_var.inner) & 1) == 0); // We rely on a free low bit, malloc guarantees this.\n"
-                opaque_ret_conv_suf = opaque_ret_conv_suf + "CHECK((((long)&" + ty_info.var_name + "_var) & 1) == 0); // We rely on a free low bit, pointer alignment guarantees this.\n"
-                if holds_ref:
-                    opaque_ret_conv_suf = opaque_ret_conv_suf + "long " + ty_info.var_name + "_ref = (long)" + ty_info.var_name + "_var.inner & ~1;"
-                else:
-                    opaque_ret_conv_suf = opaque_ret_conv_suf + "long " + ty_info.var_name + "_ref = (long)" + ty_info.var_name + "_var.inner;\n"
-                    opaque_ret_conv_suf = opaque_ret_conv_suf + "if (" + ty_info.var_name + "_var.is_owned) {\n"
-                    opaque_ret_conv_suf = opaque_ret_conv_suf + "\t" + ty_info.var_name + "_ref |= 1;\n"
-                    opaque_ret_conv_suf = opaque_ret_conv_suf + "}"
-
-                if ty_info.is_ptr:
-                    return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                        arg_conv = opaque_arg_conv, arg_conv_name = "&" + ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                        ret_conv = (ty_info.rust_obj + " " + ty_info.var_name + "_var = *", opaque_ret_conv_suf),
-                        ret_conv_name = ty_info.var_name + "_ref",
-                        to_hu_conv = ty_info.java_hu_ty + " " + ty_info.var_name + "_hu_conv = new " + ty_info.java_hu_ty + "(null, " + ty_info.var_name + ");",
-                        to_hu_conv_name = ty_info.var_name + "_hu_conv",
-                        from_hu_conv = (ty_info.var_name + " == null ? 0 : " + ty_info.var_name + ".ptr & ~1", "this.ptrs_to.add(" + ty_info.var_name + ")"))
-                else:
-                    return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                        arg_conv = opaque_arg_conv, arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                        ret_conv = (ty_info.rust_obj + " " + ty_info.var_name + "_var = ", opaque_ret_conv_suf),
-                        ret_conv_name = ty_info.var_name + "_ref",
-                        to_hu_conv = ty_info.java_hu_ty + " " + ty_info.var_name + "_hu_conv = new " + ty_info.java_hu_ty + "(null, " + ty_info.var_name + ");",
-                        to_hu_conv_name = ty_info.var_name + "_hu_conv",
-                        from_hu_conv = (ty_info.var_name + " == null ? 0 : " + ty_info.var_name + ".ptr & ~1", "this.ptrs_to.add(" + ty_info.var_name + ")"))
-
-            if not ty_info.is_ptr:
-                if ty_info.rust_obj in unitary_enums:
-                    (ret_pfx, ret_sfx) = consts.c_unitary_enum_to_native_call(ty_info)
-                    (arg_pfx, arg_sfx) = consts.native_unitary_enum_to_c_call(ty_info)
-                    return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                        arg_conv = ty_info.rust_obj + " " + ty_info.var_name + "_conv = " + arg_pfx + ty_info.var_name + arg_sfx + ";",
-                        arg_conv_name = ty_info.var_name + "_conv",
-                        arg_conv_cleanup = None,
-                        ret_conv = (ty_info.c_ty + " " + ty_info.var_name + "_conv = " + ret_pfx, ret_sfx + ";"),
-                        ret_conv_name = ty_info.var_name + "_conv", to_hu_conv = None, to_hu_conv_name = None, from_hu_conv = None)
-                base_conv = ty_info.rust_obj + " " + ty_info.var_name + "_conv = *(" + ty_info.rust_obj + "*)" + ty_info.var_name + ";"
-                if ty_info.rust_obj in trait_structs:
-                    if not is_free:
-                        needs_full_clone = not is_free and (not ty_info.is_ptr and not holds_ref or ty_info.requires_clone == True) and ty_info.requires_clone != False
-                        if needs_full_clone and (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
-                            base_conv = base_conv + "\n" + ty_info.var_name + "_conv = " + ty_info.rust_obj.replace("LDK", "") + "_clone(" + ty_info.var_name + ");"
-                        else:
-                            base_conv = base_conv + "\nif (" + ty_info.var_name + "_conv.free == " + ty_info.rust_obj + "_JCalls_free) {\n"
-                            base_conv = base_conv + "\t// If this_arg is a JCalls struct, then we need to increment the refcnt in it.\n"
-                            base_conv = base_conv + "\t" + ty_info.rust_obj + "_JCalls_clone(" + ty_info.var_name + "_conv.this_arg);\n}"
-                            if needs_full_clone:
-                                base_conv = base_conv + "// Warning: we may need a move here but can't do a full clone!\n"
-
-                    else:
-                        base_conv = base_conv + "\n" + "FREE((void*)" + ty_info.var_name + ");"
-                    return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                        arg_conv = base_conv, arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                        ret_conv = (ty_info.rust_obj + "* ret = MALLOC(sizeof(" + ty_info.rust_obj + "), \"" + ty_info.rust_obj + "\");\n*ret = ", ";"),
-                        ret_conv_name = "(long)ret",
-                        to_hu_conv = ty_info.java_hu_ty + " ret_hu_conv = new " + ty_info.java_hu_ty + "(null, " + ty_info.var_name + ");\nret_hu_conv.ptrs_to.add(this);",
-                        to_hu_conv_name = "ret_hu_conv",
-                        from_hu_conv = (ty_info.var_name + " == null ? 0 : " + ty_info.var_name + ".ptr", "this.ptrs_to.add(" + ty_info.var_name + ")"))
-                if ty_info.rust_obj != "LDKu8slice":
-                    # Don't bother free'ing slices passed in - Rust doesn't auto-free the
-                    # underlying unlike Vecs, and it gives Java more freedom.
-                    base_conv = base_conv + "\nFREE((void*)" + ty_info.var_name + ");";
-                if ty_info.rust_obj in complex_enums:
-                    ret_conv = ("long " + ty_info.var_name + "_ref = (long)&", ";")
-                    if not holds_ref:
-                        ret_conv = (ty_info.rust_obj + " *" + ty_info.var_name + "_copy = MALLOC(sizeof(" + ty_info.rust_obj + "), \"" + ty_info.rust_obj + "\");\n", "")
-                        if ty_info.requires_clone == True: # Set in object array mapping
-                            if (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
-                                ret_conv = (ret_conv[0] + "*" + ty_info.var_name + "_copy = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&", ");\n")
-                            else:
-                                ret_conv = (ret_conv[0] + "*" + ty_info.var_name + "_copy = ", "; // XXX: We likely need to clone here, but no _clone fn is available!\n")
-                        else:
-                            ret_conv = (ret_conv[0] + "*" + ty_info.var_name + "_copy = ", ";\n")
-                        ret_conv = (ret_conv[0], ret_conv[1] + "long " + ty_info.var_name + "_ref = (long)" + ty_info.var_name + "_copy;")
-                    return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                        arg_conv = base_conv, arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                        ret_conv = ret_conv, ret_conv_name = ty_info.var_name + "_ref",
-                        to_hu_conv = ty_info.java_hu_ty + " " + ty_info.var_name + "_hu_conv = " + ty_info.java_hu_ty + ".constr_from_ptr(" + ty_info.var_name + ");\n" + ty_info.var_name + "_hu_conv.ptrs_to.add(this);",
-                        to_hu_conv_name = ty_info.var_name + "_hu_conv", from_hu_conv = (ty_info.var_name + ".ptr", ""))
-                if ty_info.rust_obj in result_types:
-                    if holds_ref:
-                        # If we're trying to return a ref, we have to clone.
-                        # We just blindly assume its implemented and let the compiler fail if its not.
-                        ret_conv = (ty_info.rust_obj + "* " + ty_info.var_name + "_conv = MALLOC(sizeof(" + ty_info.rust_obj + "), \"" + ty_info.rust_obj + "\");\n*" + ty_info.var_name + "_conv = ", ";")
-                        ret_conv = (ret_conv[0], ret_conv[1] + "\n*" + ty_info.var_name + "_conv = " + ty_info.rust_obj.replace("LDK", "") + "_clone(" + ty_info.var_name + "_conv);")
-                    else:
-                        ret_conv = (ty_info.rust_obj + "* " + ty_info.var_name + "_conv = MALLOC(sizeof(" + ty_info.rust_obj + "), \"" + ty_info.rust_obj + "\");\n*" + ty_info.var_name + "_conv = ", ";")
-                    return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                        arg_conv = base_conv, arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                        ret_conv = ret_conv, ret_conv_name = "(long)" + ty_info.var_name + "_conv",
-                        to_hu_conv = ty_info.java_hu_ty + " " + ty_info.var_name + "_hu_conv = " + ty_info.java_hu_ty + ".constr_from_ptr(" + ty_info.var_name + ");",
-                        to_hu_conv_name = ty_info.var_name + "_hu_conv", from_hu_conv = (ty_info.var_name + " != null ? " + ty_info.var_name + ".ptr : 0", ""))
-                if ty_info.rust_obj in tuple_types:
-                    from_hu_conv = "bindings." + tuple_types[ty_info.rust_obj][1].replace("LDK", "") + "_new("
-                    to_hu_conv_pfx = ""
-                    to_hu_conv_sfx = ty_info.java_hu_ty + " " + ty_info.var_name + "_conv = new " + ty_info.java_hu_ty + "("
-                    clone_ret_str = ""
-                    for idx, conv in enumerate(tuple_types[ty_info.rust_obj][0]):
-                        if idx != 0:
-                            to_hu_conv_sfx = to_hu_conv_sfx + ", "
-                            from_hu_conv = from_hu_conv + ", "
-                        conv.var_name = ty_info.var_name + "_" + chr(idx + ord("a"))
-                        conv_map = map_type_with_info(conv, False, None, is_free, holds_ref)
-                        to_hu_conv_pfx = to_hu_conv_pfx + conv.java_ty + " " + ty_info.var_name + "_" + chr(idx + ord("a")) + " = " + "bindings." + tuple_types[ty_info.rust_obj][1] + "_get_" + chr(idx + ord("a")) + "(" + ty_info.var_name + ");\n"
-                        if conv_map.to_hu_conv is not None:
-                            to_hu_conv_pfx = to_hu_conv_pfx + conv_map.to_hu_conv + ";\n"
-                            to_hu_conv_sfx = to_hu_conv_sfx + conv_map.to_hu_conv_name
-                        else:
-                            to_hu_conv_sfx = to_hu_conv_sfx + ty_info.var_name + "_" + chr(idx + ord("a"))
-                        if conv_map.from_hu_conv is not None:
-                            from_hu_conv = from_hu_conv + conv_map.from_hu_conv[0].replace(ty_info.var_name + "_" + chr(idx + ord("a")), ty_info.var_name + "." + chr(idx + ord("a")))
-                            if conv_map.from_hu_conv[1] != "":
-                                from_hu_conv = from_hu_conv + "/*XXX: " + conv_map.from_hu_conv[1] + "*/"
-                        else:
-                            from_hu_conv = from_hu_conv + ty_info.var_name + "." + chr(idx + ord("a"))
-
-                        if conv.is_native_primitive:
-                            pass
-                        elif (conv_map.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
-                            accessor = ty_info.var_name + "_ref->" + chr(idx + ord("a"))
-                            clone_ret_str = clone_ret_str + "\n" + accessor + " = " + conv_map.rust_obj.replace("LDK", "") + "_clone(&" + accessor + ");"
-                        else:
-                            clone_ret_str = clone_ret_str + "\n// XXX: We likely need to clone here, but no _clone fn is available for " + conv_map.java_hu_ty
-                    if not ty_info.is_ptr and not holds_ref:
-                        ret_conv = (ty_info.rust_obj + "* " + ty_info.var_name + "_ref = MALLOC(sizeof(" + ty_info.rust_obj + "), \"" + ty_info.rust_obj + "\");\n*" + ty_info.var_name + "_ref = ", ";")
-                        if not is_free and (not ty_info.is_ptr and not holds_ref or ty_info.requires_clone == True) and ty_info.requires_clone != False:
-                            ret_conv = (ret_conv[0], ret_conv[1] + clone_ret_str)
-                        return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                            arg_conv = base_conv, arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                            ret_conv = ret_conv,
-                            ret_conv_name = "(long)" + ty_info.var_name + "_ref",
-                            to_hu_conv = to_hu_conv_pfx + to_hu_conv_sfx + ");", to_hu_conv_name = ty_info.var_name + "_conv", from_hu_conv = (from_hu_conv + ")", ""))
-                    return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                        arg_conv = base_conv, arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                        ret_conv = ("long " + ty_info.var_name + "_ref = (long)&", ";"), ret_conv_name = ty_info.var_name + "_ref",
-                        to_hu_conv = to_hu_conv_pfx + to_hu_conv_sfx + ");", to_hu_conv_name = ty_info.var_name + "_conv", from_hu_conv = (from_hu_conv + ")", ""))
-
-                # The manually-defined types - TxOut and Transaction
-                assert ty_info.rust_obj == "LDKTxOut"
-                if not ty_info.is_ptr and not holds_ref:
-                    ret_conv = ("LDKTxOut* " + ty_info.var_name + "_ref = MALLOC(sizeof(LDKTxOut), \"LDKTxOut\");\n*" + ty_info.var_name + "_ref = ", ";")
-                else:
-                    ret_conv = ("long " + ty_info.var_name + "_ref = (long)&", ";")
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = base_conv, arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                    ret_conv = ret_conv, ret_conv_name = "(long)" + ty_info.var_name + "_ref",
-                    to_hu_conv = ty_info.java_hu_ty + " " + ty_info.var_name + "_conv = new " +ty_info.java_hu_ty + "(null, " + ty_info.var_name + ");",
-                    to_hu_conv_name = ty_info.var_name + "_conv", from_hu_conv = (ty_info.var_name + ".ptr", ""))
-            elif ty_info.is_ptr:
-                assert(not is_free)
-                if ty_info.rust_obj in complex_enums:
-                    return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                        arg_conv = ty_info.rust_obj + "* " + ty_info.var_name + "_conv = (" + ty_info.rust_obj + "*)" + ty_info.var_name + ";",
-                        arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                        ret_conv = ("long ret_" + ty_info.var_name + " = (long)", ";"), ret_conv_name = "ret_" + ty_info.var_name,
-                        to_hu_conv = ty_info.java_hu_ty + " " + ty_info.var_name + "_hu_conv = " + ty_info.java_hu_ty + ".constr_from_ptr(" + ty_info.var_name + ");",
-                        to_hu_conv_name = ty_info.var_name + "_hu_conv",
-                        from_hu_conv = (ty_info.var_name + " == null ? 0 : " + ty_info.var_name + ".ptr & ~1", "this.ptrs_to.add(" + ty_info.var_name + ")"))
-                elif ty_info.rust_obj in trait_structs:
-                    if ty_info.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
-                        return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                            arg_conv = ty_info.rust_obj + "* " + ty_info.var_name + "_conv = (" + ty_info.rust_obj + "*)" + ty_info.var_name + ";",
-                            arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                            ret_conv = (ty_info.rust_obj + " *" + ty_info.var_name + "_clone = MALLOC(sizeof(" + ty_info.rust_obj + "), \"" + ty_info.rust_obj + "\");\n" +
-                                "*" + ty_info.var_name + "_clone = " + ty_info.rust_obj.replace("LDK", "") + "_clone(", ");"),
-                            ret_conv_name = "(long)" + ty_info.var_name + "_clone",
-                            to_hu_conv = ty_info.java_hu_ty + " ret_hu_conv = new " + ty_info.java_hu_ty + "(null, " + ty_info.var_name + ");\nret_hu_conv.ptrs_to.add(this);",
-                            to_hu_conv_name = "ret_hu_conv",
-                            from_hu_conv = (ty_info.var_name + " == null ? 0 : " + ty_info.var_name + ".ptr", "this.ptrs_to.add(" + ty_info.var_name + ")"))
-                    else:
-                        return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                            arg_conv = ty_info.rust_obj + "* " + ty_info.var_name + "_conv = (" + ty_info.rust_obj + "*)" + ty_info.var_name + ";",
-                            arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                            ret_conv = ("long ret_" + ty_info.var_name + " = (long)", ";"), ret_conv_name = "ret_" + ty_info.var_name,
-                            to_hu_conv = ty_info.java_hu_ty + " ret_hu_conv = new " + ty_info.java_hu_ty + "(null, " + ty_info.var_name + ");\nret_hu_conv.ptrs_to.add(this);",
-                            to_hu_conv_name = "ret_hu_conv",
-                            from_hu_conv = (ty_info.var_name + " == null ? 0 : " + ty_info.var_name + ".ptr", "this.ptrs_to.add(" + ty_info.var_name + ")"))
-                return ConvInfo(ty_info = ty_info, arg_name = ty_info.var_name,
-                    arg_conv = ty_info.rust_obj + "* " + ty_info.var_name + "_conv = (" + ty_info.rust_obj + "*)" + ty_info.var_name + ";",
-                    arg_conv_name = ty_info.var_name + "_conv", arg_conv_cleanup = None,
-                    ret_conv = ("long ret_" + ty_info.var_name + " = (long)", ";"), ret_conv_name = "ret_" + ty_info.var_name,
-                    to_hu_conv = "TODO 3", to_hu_conv_name = None, from_hu_conv = None) # its a pointer, no conv needed
-            assert False # We should have handled every case by now.
 
 
+    # Map a top-level function
     def map_fn(line, re_match, ret_arr_len, c_call_string):
     def map_fn(line, re_match, ret_arr_len, c_call_string):
-        out_java.write("\t// " + line)
-        out_java.write("\tpublic static native ")
-        write_c(consts.c_fn_ty_pfx)
 
 
-        is_free = re_match.group(2).endswith("_free")
-        struct_meth = re_match.group(2).split("_")[0]
+        # PARSING START
+        method_return_type = re_match.group(1)
+        method_name = re_match.group(2)
+        method_comma_separated_arguments = re_match.group(3)
+        method_arguments = method_comma_separated_arguments.split(',')
 
 
-        ret_info = map_type(re_match.group(1), True, ret_arr_len, False, False)
-        write_c(ret_info.c_ty)
-        out_java.write(ret_info.java_ty)
+        is_free = method_name.endswith("_free")
+        struct_meth = method_name.split("_")[0]
 
 
-        if ret_info.ret_conv is not None:
-            ret_conv_pfx, ret_conv_sfx = ret_info.ret_conv
+        return_type_info = type_mapping_generator.map_type(method_return_type, True, ret_arr_len, False, False)
 
 
-        out_java.write(" " + re_match.group(2) + "(")
-        write_c(" " + consts.c_fn_name_pfx + re_match.group(2).replace('_', '_1') + "(" + consts.c_fn_args_pfx)
-
-        arg_names = []
+        argument_types = []
         default_constructor_args = {}
         takes_self = False
         args_known = True
         default_constructor_args = {}
         takes_self = False
         args_known = True
-        for idx, arg in enumerate(re_match.group(3).split(',')):
-            if idx != 0:
-                out_java.write(", ")
-            if arg != "void":
-                write_c(", ")
-            arg_conv_info = map_type(arg, False, None, is_free, True)
-            if arg_conv_info.c_ty != "void":
-                write_c(arg_conv_info.c_ty + " " + arg_conv_info.arg_name)
-                out_java.write(arg_conv_info.java_ty + " " + arg_conv_info.arg_name)
-            if idx == 0 and arg_conv_info.java_hu_ty == struct_meth:
+
+        for argument_index, argument in enumerate(method_arguments):
+            argument_conversion_info = type_mapping_generator.map_type(argument, False, None, is_free, True)
+            if argument_index == 0 and argument_conversion_info.java_hu_ty == struct_meth:
                 takes_self = True
                 takes_self = True
-            if arg_conv_info.arg_conv is not None and "Warning" in arg_conv_info.arg_conv:
-                if arg_conv_info.rust_obj in constructor_fns:
+            if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
+                if argument_conversion_info.rust_obj in constructor_fns:
                     assert not is_free
                     assert not is_free
-                    for explode_arg in constructor_fns[arg_conv_info.rust_obj].split(','):
-                        explode_arg_conv = map_type(explode_arg, False, None, False, True)
+                    for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
+                        explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
                         if explode_arg_conv.c_ty == "void":
                             # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
                             # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
                             args_known = False
                             pass
                         if explode_arg_conv.c_ty == "void":
                             # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
                             # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
                             args_known = False
                             pass
-                        if not arg_conv_info.arg_name in default_constructor_args:
-                            default_constructor_args[arg_conv_info.arg_name] = []
-                        default_constructor_args[arg_conv_info.arg_name].append(explode_arg_conv)
-            arg_names.append(arg_conv_info)
+                        if not argument_conversion_info.arg_name in default_constructor_args:
+                            default_constructor_args[argument_conversion_info.arg_name] = []
+                        default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
+            argument_types.append(argument_conversion_info)
 
 
-        out_java_struct = None
-        if ("LDK" + struct_meth in opaque_structs or "LDK" + struct_meth in trait_structs) and not is_free:
-            out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
-            if not args_known:
-                out_java_struct.write("\t// Skipped " + re_match.group(2) + "\n")
-                out_java_struct.close()
-                out_java_struct = None
-            else:
-                meth_n = re_match.group(2)[len(struct_meth) + 1:]
-                if not takes_self:
-                    out_java_struct.write("\tpublic static " + ret_info.java_hu_ty + " constructor_" + meth_n + "(")
-                else:
-                    out_java_struct.write("\tpublic " + ret_info.java_hu_ty + " " + meth_n + "(")
-                for idx, arg in enumerate(arg_names):
-                    if idx != 0:
-                        if not takes_self or idx > 1:
-                            out_java_struct.write(", ")
-                    elif takes_self:
-                        continue
-                    if arg.java_ty != "void":
-                        if arg.arg_name in default_constructor_args:
-                            for explode_idx, explode_arg in enumerate(default_constructor_args[arg.arg_name]):
-                                if explode_idx != 0:
-                                    out_java_struct.write(", ")
-                                out_java_struct.write(explode_arg.java_hu_ty + " " + arg.arg_name + "_" + explode_arg.arg_name)
-                        else:
-                            out_java_struct.write(arg.java_hu_ty + " " + arg.arg_name)
+        has_out_java_struct = ("LDK" + struct_meth in opaque_structs or "LDK" + struct_meth in trait_structs) and not is_free
+        # PARSING END
 
 
+        out_java.write("\t// " + line)
 
 
-        out_java.write(");\n")
-        write_c(") {\n")
-        if out_java_struct is not None:
-            out_java_struct.write(") {\n")
+        (out_java_delta, out_c_delta, out_java_struct_delta) = consts.map_function(argument_types, c_call_string, is_free, method_name, return_type_info, struct_meth, default_constructor_args, takes_self, args_known, has_out_java_struct, type_mapping_generator)
 
 
-        for info in arg_names:
-            if info.arg_conv is not None:
-                write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
+        out_java.write(out_java_delta)
+        write_c(out_c_delta)
+
+        if has_out_java_struct:
+            out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
+            out_java_struct.write(out_java_struct_delta)
 
 
-        if ret_info.ret_conv is not None:
-            write_c("\t" + ret_conv_pfx.replace('\n', '\n\t'))
-        elif ret_info.c_ty != "void":
-            write_c("\t" + ret_info.c_ty + " ret_val = ")
-        else:
-            write_c("\t")
 
 
-        if c_call_string is None:
-            write_c(re_match.group(2) + "(")
-        else:
-            write_c(c_call_string)
-        for idx, info in enumerate(arg_names):
-            if info.arg_conv_name is not None:
-                if idx != 0:
-                    write_c(", ")
-                elif c_call_string is not None:
-                    continue
-                write_c(info.arg_conv_name)
-        write_c(")")
-        if ret_info.ret_conv is not None:
-            write_c(ret_conv_sfx.replace('\n', '\n\t'))
-        else:
-            write_c(";")
-        for info in arg_names:
-            if info.arg_conv_cleanup is not None:
-                write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
-        if ret_info.ret_conv is not None:
-            write_c("\n\treturn " + ret_info.ret_conv_name + ";")
-        elif ret_info.c_ty != "void":
-            write_c("\n\treturn ret_val;")
-        write_c("\n}\n\n")
-        if out_java_struct is not None:
-            out_java_struct.write("\t\t")
-            if ret_info.java_ty != "void":
-                out_java_struct.write(ret_info.java_ty + " ret = ")
-            out_java_struct.write("bindings." + re_match.group(2) + "(")
-            for idx, info in enumerate(arg_names):
-                if idx != 0:
-                    out_java_struct.write(", ")
-                if idx == 0 and takes_self:
-                    out_java_struct.write("this.ptr")
-                elif info.arg_name in default_constructor_args:
-                    out_java_struct.write("bindings." + info.java_hu_ty + "_new(")
-                    for explode_idx, explode_arg in enumerate(default_constructor_args[info.arg_name]):
-                        if explode_idx != 0:
-                            out_java_struct.write(", ")
-                        expl_arg_name = info.arg_name + "_" + explode_arg.arg_name
-                        if explode_arg.from_hu_conv is not None:
-                            out_java_struct.write(explode_arg.from_hu_conv[0].replace(explode_arg.arg_name, expl_arg_name))
-                        else:
-                            out_java_struct.write(expl_arg_name)
-                    out_java_struct.write(")")
-                elif info.from_hu_conv is not None:
-                    out_java_struct.write(info.from_hu_conv[0])
-                else:
-                    out_java_struct.write(info.arg_name)
-            out_java_struct.write(");\n")
-            if ret_info.to_hu_conv is not None:
-                if not takes_self:
-                    out_java_struct.write("\t\t" + ret_info.to_hu_conv.replace("\n", "\n\t\t").replace("this", ret_info.to_hu_conv_name) + "\n")
-                else:
-                    out_java_struct.write("\t\t" + ret_info.to_hu_conv.replace("\n", "\n\t\t") + "\n")
 
 
-            for idx, info in enumerate(arg_names):
-                if idx == 0 and takes_self:
-                    pass
-                elif info.arg_name in default_constructor_args:
-                    for explode_arg in default_constructor_args[info.arg_name]:
-                        expl_arg_name = info.arg_name + "_" + explode_arg.arg_name
-                        if explode_arg.from_hu_conv is not None and ret_info.to_hu_conv_name:
-                            out_java_struct.write("\t\t" + explode_arg.from_hu_conv[1].replace(explode_arg.arg_name, expl_arg_name).replace("this", ret_info.to_hu_conv_name) + ";\n")
-                elif info.from_hu_conv is not None and info.from_hu_conv[1] != "":
-                    if not takes_self and ret_info.to_hu_conv_name is not None:
-                        out_java_struct.write("\t\t" + info.from_hu_conv[1].replace("this", ret_info.to_hu_conv_name) + ";\n")
-                    else:
-                        out_java_struct.write("\t\t" + info.from_hu_conv[1] + ";\n")
 
 
-            if ret_info.to_hu_conv_name is not None:
-                out_java_struct.write("\t\treturn " + ret_info.to_hu_conv_name + ";\n")
-            elif ret_info.java_ty != "void" and ret_info.rust_obj != "LDK" + struct_meth:
-                out_java_struct.write("\t\treturn ret;\n")
-            out_java_struct.write("\t}\n\n")
-            out_java_struct.close()
 
     def map_unitary_enum(struct_name, field_lines):
         with open(f"{sys.argv[3]}/enums/{struct_name}{consts.file_ext}", "w") as out_java_enum:
 
     def map_unitary_enum(struct_name, field_lines):
         with open(f"{sys.argv[3]}/enums/{struct_name}{consts.file_ext}", "w") as out_java_enum:
@@ -957,7 +460,7 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
                     enum_var_lines = union_enum_items["LDK" + variant_name]
                     for idx, field in enumerate(enum_var_lines):
                         if idx != 0 and idx < len(enum_var_lines) - 2:
                     enum_var_lines = union_enum_items["LDK" + variant_name]
                     for idx, field in enumerate(enum_var_lines):
                         if idx != 0 and idx < len(enum_var_lines) - 2:
-                            fields.append(map_type(field.strip(' ;'), False, None, False, True))
+                            fields.append(type_mapping_generator.map_type(field.strip(' ;'), False, None, False, True))
                         else:
                             # TODO: Assert line format
                             pass
                         else:
                             # TODO: Assert line format
                             pass
@@ -980,18 +483,19 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
                 if var_line.group(1) in trait_structs:
                     field_var_convs.append((var_line.group(1), var_line.group(2)))
                 else:
                 if var_line.group(1) in trait_structs:
                     field_var_convs.append((var_line.group(1), var_line.group(2)))
                 else:
-                    field_var_convs.append(map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False))
+                    field_var_convs.append(
+                        type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False))
 
             field_fns = []
             for fn_line in trait_fn_lines:
 
             field_fns = []
             for fn_line in trait_fn_lines:
-                ret_ty_info = map_type(fn_line.group(2), True, None, False, False)
+                ret_ty_info = type_mapping_generator.map_type(fn_line.group(2), True, None, False, False)
                 is_const = fn_line.group(4) is not None
 
                 arg_tys = []
                 for idx, arg in enumerate(fn_line.group(5).split(',')):
                     if arg == "":
                         continue
                 is_const = fn_line.group(4) is not None
 
                 arg_tys = []
                 for idx, arg in enumerate(fn_line.group(5).split(',')):
                     if arg == "":
                         continue
-                    arg_conv_info = map_type(arg, True, None, False, False)
+                    arg_conv_info = type_mapping_generator.map_type(arg, True, None, False, False)
                     arg_tys.append(arg_conv_info)
                 field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys))
 
                     arg_tys.append(arg_conv_info)
                 field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys))
 
@@ -1034,8 +538,8 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
             out_java_struct.write("\t\t}\n")
             out_java_struct.write("\t}\n")
 
             out_java_struct.write("\t\t}\n")
             out_java_struct.write("\t}\n")
 
-            res_map = map_type(res_ty + " res", True, None, False, True)
-            err_map = map_type(err_ty + " err", True, None, False, True)
+            res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
+            err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
             can_clone = True
             if not res_map.is_native_primitive and (res_map.rust_obj.replace("LDK", "") + "_clone" not in clone_fns):
                 can_clone = False
             can_clone = True
             if not res_map.is_native_primitive and (res_map.rust_obj.replace("LDK", "") + "_clone" not in clone_fns):
                 can_clone = False
@@ -1172,7 +676,7 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
         clone_str = clone_str + "\t" + struct_name + " ret = {\n"
         for idx, line in enumerate(field_lines):
             if idx != 0 and idx < len(field_lines) - 2:
         clone_str = clone_str + "\t" + struct_name + " ret = {\n"
         for idx, line in enumerate(field_lines):
             if idx != 0 and idx < len(field_lines) - 2:
-                ty_info = map_type(line.strip(';'), False, None, False, False)
+                ty_info = type_mapping_generator.map_type(line.strip(';'), False, None, False, False)
                 e = chr(ord('a') + idx - 1)
                 if ty_info.arg_conv is not None:
                     write_c("\t" + ty_info.arg_conv.replace("\n", "\n\t"))
                 e = chr(ord('a') + idx - 1)
                 if ty_info.arg_conv is not None:
                     write_c("\t" + ty_info.arg_conv.replace("\n", "\n\t"))
@@ -1200,7 +704,7 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
             out_java.write("\tpublic static native " + ty_info.java_ty + " " + struct_name + "_get_" + e + "(long ptr);\n")
             write_c(consts.c_fn_ty_pfx + ty_info.c_ty + " " + consts.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1get_1" + e + "(" + consts.c_fn_args_pfx + ", " + consts.ptr_c_ty + " ptr) {\n")
             write_c("\t" + struct_name + " *tuple = (" + struct_name + "*)ptr;\n")
             out_java.write("\tpublic static native " + ty_info.java_ty + " " + struct_name + "_get_" + e + "(long ptr);\n")
             write_c(consts.c_fn_ty_pfx + ty_info.c_ty + " " + consts.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1get_1" + e + "(" + consts.c_fn_args_pfx + ", " + consts.ptr_c_ty + " ptr) {\n")
             write_c("\t" + struct_name + " *tuple = (" + struct_name + "*)ptr;\n")
-            conv_info = map_type_with_info(ty_info, False, None, False, True)
+            conv_info = type_mapping_generator.map_type_with_info(ty_info, False, None, False, True)
             if conv_info.ret_conv is not None:
                 write_c("\t" + conv_info.ret_conv[0].replace("\n", "\n\t") + "tuple->" + e + conv_info.ret_conv[1].replace("\n", "\n\t") + "\n")
                 write_c("\treturn " + conv_info.ret_conv_name + ";\n")
             if conv_info.ret_conv is not None:
                 write_c("\t" + conv_info.ret_conv[0].replace("\n", "\n\t") + "tuple->" + e + conv_info.ret_conv[1].replace("\n", "\n\t") + "\n")
                 write_c("\treturn " + conv_info.ret_conv_name + ";\n")
@@ -1208,39 +712,7 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
                 write_c("\treturn tuple->" + e + ";\n")
             write_c("}\n")
 
                 write_c("\treturn tuple->" + e + ";\n")
             write_c("}\n")
 
-    out_java.write("""package org.ldk.impl;
-import org.ldk.enums.*;
-
-public class bindings {
-       public static class VecOrSliceDef {
-               public long dataptr;
-               public long datalen;
-               public long stride;
-               public VecOrSliceDef(long dataptr, long datalen, long stride) {
-                       this.dataptr = dataptr; this.datalen = datalen; this.stride = stride;
-               }
-       }
-       static {
-               System.loadLibrary(\"lightningjni\");
-               init(java.lang.Enum.class, VecOrSliceDef.class);
-               init_class_cache();
-       }
-       static native void init(java.lang.Class c, java.lang.Class slicedef);
-       static native void init_class_cache();
-
-       public static native boolean deref_bool(long ptr);
-       public static native long deref_long(long ptr);
-       public static native void free_heap_ptr(long ptr);
-       public static native byte[] read_bytes(long ptr, long len);
-       public static native byte[] get_u8_slice_bytes(long slice_ptr);
-       public static native long bytes_to_u8_vec(byte[] bytes);
-       public static native long new_txpointer_copy_data(byte[] txdata);
-       public static native void txpointer_free(long ptr);
-       public static native byte[] txpointer_get_buffer(long ptr);
-       public static native long vec_slice_len(long vec);
-       public static native long new_empty_slice_vec();
-
-""")
+    out_java.write(consts.bindings_header)
 
     with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
         out_java_struct.write(consts.common_base)
 
     with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
         out_java_struct.write(consts.common_base)
@@ -1334,20 +806,8 @@ public class bindings {
                 if is_opaque:
                     opaque_structs.add(struct_name)
                     with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
                 if is_opaque:
                     opaque_structs.add(struct_name)
                     with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
-                        out_java_struct.write(consts.hu_struct_file_prefix)
-                        out_java_struct.write("public class " + struct_name.replace("LDK","") + " extends CommonBase")
-                        if struct_name.startswith("LDKLocked"):
-                            out_java_struct.write(" implements AutoCloseable")
-                        out_java_struct.write(" {\n")
-                        out_java_struct.write("\t" + struct_name.replace("LDK", "") + "(Object _dummy, long ptr) { super(ptr); }\n")
-                        if struct_name.startswith("LDKLocked"):
-                            out_java_struct.write("\t@Override public void close() {\n")
-                        else:
-                            out_java_struct.write("\t@Override @SuppressWarnings(\"deprecation\")\n")
-                            out_java_struct.write("\tprotected void finalize() throws Throwable {\n")
-                            out_java_struct.write("\t\tsuper.finalize();\n")
-                        out_java_struct.write("\t\tif (ptr != 0) { bindings." + struct_name.replace("LDK","") + "_free(ptr); }\n")
-                        out_java_struct.write("\t}\n\n")
+                        out_opaque_struct_human = consts.map_opaque_struct(struct_name)
+                        out_java_struct.write(out_opaque_struct_human)
                 elif result_contents is not None:
                     assert result_contents in result_ptr_struct_items
                     res_ty, err_ty = result_ptr_struct_items[result_contents]
                 elif result_contents is not None:
                     assert result_contents in result_ptr_struct_items
                     res_ty, err_ty = result_ptr_struct_items[result_contents]
@@ -1363,7 +823,7 @@ public class bindings {
                 elif is_tuple:
                     map_tuple(struct_name, field_lines)
                 elif vec_ty is not None:
                 elif is_tuple:
                     map_tuple(struct_name, field_lines)
                 elif vec_ty is not None:
-                    ty_info = map_type(vec_ty + " arr_elem", False, None, False, False)
+                    ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
                     if len(ty_info.java_fn_ty_arg) == 1: # ie we're a primitive of some form
                         out_java.write("\tpublic static native long " + struct_name + "_new(" + ty_info.java_ty + "[] elems);\n")
                         write_c(consts.c_fn_ty_pfx + consts.ptr_c_ty + " " + consts.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1new(" + consts.c_fn_args_pfx + ", " + ty_info.c_ty + "Array elems) {\n")
                     if len(ty_info.java_fn_ty_arg) == 1: # ie we're a primitive of some form
                         out_java.write("\tpublic static native long " + struct_name + "_new(" + ty_info.java_ty + "[] elems);\n")
                         write_c(consts.c_fn_ty_pfx + consts.ptr_c_ty + " " + consts.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1new(" + consts.c_fn_args_pfx + ", " + ty_info.c_ty + "Array elems) {\n")
@@ -1464,13 +924,14 @@ public class bindings {
             else:
                 assert(line == "\n")
 
             else:
                 assert(line == "\n")
 
-    out_java.write("}\n")
+    out_java.write(consts.bindings_footer)
     for struct_name in opaque_structs:
         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
             out_java_struct.write("}\n")
     for struct_name in trait_structs:
         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
             out_java_struct.write("}\n")
     for struct_name in opaque_structs:
         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
             out_java_struct.write("}\n")
     for struct_name in trait_structs:
         with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
             out_java_struct.write("}\n")
+
 with open(sys.argv[4], "w") as out_c:
     out_c.write(consts.c_file_pfx)
     out_c.write(consts.init_str(c_array_class_caches))
 with open(sys.argv[4], "w") as out_c:
     out_c.write(consts.c_file_pfx)
     out_c.write(consts.init_str(c_array_class_caches))