Bindings updates
[ldk-java] / java_strings.py
index c9a32171b738c289cf645c57781ffbffe2366966..7ae38a14242d64cf0707676fa830065633cf0707 100644 (file)
@@ -1,13 +1,13 @@
 from bindingstypes import *
 
 class Consts:
-    def __init__(self, DEBUG):
-
+    def __init__(self, DEBUG: bool, **kwargs):
+        self.c_array_class_caches = set()
         self.c_type_map = dict(
             uint8_t = ['byte'],
             uint16_t = ['short'],
             uint32_t = ['int'],
-            long = ['long'],
+            uint64_t = ['long'],
         )
 
         self.to_hu_conv_templates = dict(
@@ -15,6 +15,42 @@ class Consts:
             default = '{human_type} {var_name}_hu_conv = new {human_type}(null, {var_name});'
         )
 
+        self.bindings_header = """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();
+
+"""
+
+        self.bindings_footer = "}\n"
+
         self.common_base = """package org.ldk.structs;
 import java.util.LinkedList;
 class CommonBase {
@@ -31,6 +67,7 @@ class CommonBase {
 #include <string.h>
 #include <stdatomic.h>
 #include <stdlib.h>
+
 """
 
         if not DEBUG:
@@ -245,6 +282,15 @@ _Static_assert(sizeof(void*) <= 8, "Pointers must fit into 64 bits");
 typedef jlongArray int64_tArray;
 typedef jbyteArray int8_tArray;
 
+static inline jstring str_ref_to_java(JNIEnv *env, const char* chars, size_t len) {
+       // Sadly we need to create a temporary because Java can't accept a char* without a 0-terminator
+       char* err_buf = MALLOC(len + 1, "str conv buf");
+       memcpy(err_buf, chars, len);
+       err_buf[len] = 0;
+       jstring err_conv = (*env)->NewStringUTF(env, chars);
+       FREE(err_buf);
+       return err_conv;
+}
 """
 
         self.hu_struct_file_prefix = """package org.ldk.structs;
@@ -257,7 +303,6 @@ import java.util.Arrays;
 @SuppressWarnings("unchecked") // We correctly assign various generic arrays
 """
         self.c_fn_ty_pfx = "JNIEXPORT "
-        self.c_fn_name_pfx = "JNICALL Java_org_ldk_impl_bindings_"
         self.c_fn_args_pfx = "JNIEnv *env, jclass clz"
         self.file_ext = ".java"
         self.ptr_c_ty = "int64_t"
@@ -265,13 +310,18 @@ import java.util.Arrays;
         self.result_c_ty = "jclass"
         self.ptr_arr = "jobjectArray"
         self.get_native_arr_len_call = ("(*env)->GetArrayLength(env, ", ")")
-        self.get_native_arr_ptr_call = ("(*env)->GetPrimitiveArrayCritical(env, ", ", NULL)")
 
-    def release_native_arr_ptr_call(self, arr_var, arr_ptr_var):
-        return "(*env)->ReleasePrimitiveArrayCritical(env, " + arr_var + ", " + arr_ptr_var + ", 0)"
+    def release_native_arr_ptr_call(self, ty_info, arr_var, arr_ptr_var):
+        if ty_info.subty is None or not ty_info.subty.c_ty.endswith("Array"):
+            return "(*env)->ReleasePrimitiveArrayCritical(env, " + arr_var + ", " + arr_ptr_var + ", 0)"
+        return None
     def create_native_arr_call(self, arr_len, ty_info):
         if ty_info.c_ty == "int8_tArray":
             return "(*env)->NewByteArray(env, " + arr_len + ")"
+        elif ty_info.subty.c_ty.endswith("Array"):
+            clz_var = ty_info.java_fn_ty_arg[1:].replace("[", "arr_of_")
+            self.c_array_class_caches.add(clz_var)
+            return "(*env)->NewObjectArray(env, " + arr_len + ", " + clz_var + "_clz, NULL);\n"
         else:
             return "(*env)->New" + ty_info.java_ty.strip("[]").title() + "Array(env, " + arr_len + ")"
     def set_native_arr_contents(self, arr_name, arr_len, ty_info):
@@ -294,18 +344,34 @@ import java.util.Arrays;
             return "(*env)->GetObjectArrayElement(env, " + arr_name + ", " + idxc + ")"
         else:
             assert False # Only called if above is None
+    def get_native_arr_ptr_call(self, ty_info):
+        if ty_info.subty is not None and ty_info.subty.c_ty.endswith("Array"):
+            return None
+        return ("(*env)->GetPrimitiveArrayCritical(env, ", ", NULL)")
+    def get_native_arr_entry_call(self, ty_info, arr_name, idxc, entry_access):
+        if ty_info.subty is None or not ty_info.subty.c_ty.endswith("Array"):
+            return None
+        return "(*env)->SetObjectArrayElement(env, " + arr_name + ", " + idxc + ", " + entry_access + ")"
     def cleanup_native_arr_ref_contents(self, arr_name, dest_name, arr_len, ty_info):
         if ty_info.c_ty == "int8_tArray":
             return "(*env)->ReleaseByteArrayElements(env, " + arr_name + ", (int8_t*)" + dest_name + ", 0);"
         else:
             return "(*env)->Release" + ty_info.java_ty.strip("[]").title() + "ArrayElements(env, " + arr_name + ", " + dest_name + ", 0)"
 
-    def init_str(self, c_array_class_caches):
+    def str_ref_to_c_call(self, var_name, str_len):
+        return "str_ref_to_java(env, " + var_name + ", " + str_len + ")"
+
+    def c_fn_name_define_pfx(self, fn_name, has_args):
+        if has_args:
+            return "JNICALL Java_org_ldk_impl_bindings_" + fn_name.replace("_", "_1") + "(JNIEnv *env, jclass clz, "
+        return "JNICALL Java_org_ldk_impl_bindings_" + fn_name.replace("_", "_1") + "(JNIEnv *env, jclass clz"
+
+    def init_str(self):
         res = ""
-        for ty in c_array_class_caches:
+        for ty in self.c_array_class_caches:
             res = res + "static jclass " + ty + "_clz = NULL;\n"
         res = res + "JNIEXPORT void Java_org_ldk_impl_bindings_init_1class_1cache(JNIEnv * env, jclass clz) {\n"
-        for ty in c_array_class_caches:
+        for ty in self.c_array_class_caches:
             res = res + "\t" + ty + "_clz = (*env)->FindClass(env, \"" + ty.replace("arr_of_", "[") + "\");\n"
             res = res + "\tCHECK(" + ty + "_clz != NULL);\n"
             res = res + "\t" + ty + "_clz = (*env)->NewGlobalRef(env, " + ty + "_clz);\n"
@@ -511,7 +577,6 @@ import java.util.Arrays;
             else:
                 out_java = out_java + ", " + var[0] + " " + var[1]
         out_java = out_java + ");\n"
-        out_java = out_java + "\tpublic static native " + struct_name + " " + struct_name + "_get_obj_from_jcalls(long val);\n"
 
         # Now that we've written out our java code (and created java_meths), generate C
         out_c = "typedef struct " + struct_name + "_JCalls {\n"
@@ -642,7 +707,7 @@ import java.util.Arrays;
         out_c = out_c + "\treturn ret;\n"
         out_c = out_c + "}\n"
 
-        out_c = out_c + self.c_fn_ty_pfx + "long " + self.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1new (" + self.c_fn_args_pfx + ", jobject o"
+        out_c = out_c + self.c_fn_ty_pfx + "long " + self.c_fn_name_define_pfx(struct_name + "_new", True) + "jobject o"
         for var in field_vars:
             if isinstance(var, ConvInfo):
                 out_c = out_c + ", " + var.c_ty + " " + var.arg_name
@@ -660,14 +725,14 @@ import java.util.Arrays;
         out_c = out_c + "\treturn (long)res_ptr;\n"
         out_c = out_c + "}\n"
 
-        out_c = out_c + self.c_fn_ty_pfx + "jobject " + self.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1get_1obj_1from_1jcalls (" + self.c_fn_args_pfx + ", " + self.ptr_c_ty + " val) {\n"
-        out_c = out_c + "\tjobject ret = (*env)->NewLocalRef(env, ((" + struct_name + "_JCalls*)val)->o);\n"
-        out_c = out_c + "\tCHECK(ret != NULL);\n"
-        out_c = out_c + "\treturn ret;\n"
-        out_c = out_c + "}\n"
-
         return (out_java, out_java_trait, out_c)
 
+    def trait_struct_inc_refcnt(self, ty_info):
+        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}"
+        return base_conv
+
     def map_complex_enum(self, struct_name, variant_list, camel_to_snake):
         java_hu_type = struct_name.replace("LDK", "")
         out_java_enum = ""
@@ -731,7 +796,7 @@ import java.util.Arrays;
 
         out_c += (self.c_complex_enum_pfx(struct_name, [x.var_name for x in variant_list], init_meth_jty_strs))
 
-        out_c += (self.c_fn_ty_pfx + self.c_complex_enum_pass_ty(struct_name) + " " + self.c_fn_name_pfx + struct_name.replace("_", "_1") + "_1ref_1from_1ptr (" + self.c_fn_args_pfx + ", " + self.ptr_c_ty + " ptr) {\n")
+        out_c += (self.c_fn_ty_pfx + self.c_complex_enum_pass_ty(struct_name) + " " + self.c_fn_name_define_pfx(struct_name + "_ref_from_ptr", True) + self.ptr_c_ty + " ptr) {\n")
         out_c += ("\t" + struct_name + " *obj = (" + struct_name + "*)ptr;\n")
         out_c += ("\tswitch(obj->tag) {\n")
         for var in variant_list:
@@ -751,3 +816,169 @@ import java.util.Arrays;
         out_c += ("\t}\n}\n")
         out_java_enum += ("}\n")
         return (out_java, out_java_enum, out_c)
+
+    def map_opaque_struct(self, struct_name):
+        out_opaque_struct_human = ""
+        out_opaque_struct_human += self.hu_struct_file_prefix
+        out_opaque_struct_human += ("public class " + struct_name.replace("LDK","") + " extends CommonBase")
+        if struct_name.startswith("LDKLocked"):
+            out_opaque_struct_human += (" implements AutoCloseable")
+        out_opaque_struct_human += (" {\n")
+        out_opaque_struct_human += ("\t" + struct_name.replace("LDK", "") + "(Object _dummy, long ptr) { super(ptr); }\n")
+        if struct_name.startswith("LDKLocked"):
+            out_opaque_struct_human += ("\t@Override public void close() {\n")
+        else:
+            out_opaque_struct_human += ("\t@Override @SuppressWarnings(\"deprecation\")\n")
+            out_opaque_struct_human += ("\tprotected void finalize() throws Throwable {\n")
+            out_opaque_struct_human += ("\t\tsuper.finalize();\n")
+        out_opaque_struct_human += ("\t\tif (ptr != 0) { bindings." + struct_name.replace("LDK","") + "_free(ptr); }\n")
+        out_opaque_struct_human += ("\t}\n\n")
+        return out_opaque_struct_human
+
+
+    def map_function(self, 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: bool, type_mapping_generator):
+        out_java = ""
+        out_c = ""
+        out_java_struct = None
+
+        out_java += ("\tpublic static native ")
+        out_c += (self.c_fn_ty_pfx)
+        out_c += (return_type_info.c_ty)
+        out_java += (return_type_info.java_ty)
+        if return_type_info.ret_conv is not None:
+            ret_conv_pfx, ret_conv_sfx = return_type_info.ret_conv
+        out_java += (" " + method_name + "(")
+        have_args = len(argument_types) > 1 or (len(argument_types) > 0 and argument_types[0].c_ty != "void")
+        out_c += (" " + self.c_fn_name_define_pfx(method_name, have_args))
+
+        for idx, arg_conv_info in enumerate(argument_types):
+            if idx != 0:
+                out_java += (", ")
+                out_c += (", ")
+            if arg_conv_info.c_ty != "void":
+                out_c += (arg_conv_info.c_ty + " " + arg_conv_info.arg_name)
+                out_java += (arg_conv_info.java_ty + " " + arg_conv_info.arg_name)
+
+        if has_out_java_struct:
+            out_java_struct = ""
+            if not args_known:
+                out_java_struct += ("\t// Skipped " + method_name + "\n")
+                has_out_java_struct = False
+            else:
+                meth_n = method_name[len(struct_meth) + 1:]
+                if not takes_self:
+                    out_java_struct += (
+                        "\tpublic static " + return_type_info.java_hu_ty + " constructor_" + meth_n + "(")
+                else:
+                    out_java_struct += ("\tpublic " + return_type_info.java_hu_ty + " " + meth_n + "(")
+                for idx, arg in enumerate(argument_types):
+                    if idx != 0:
+                        if not takes_self or idx > 1:
+                            out_java_struct += (", ")
+                    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 += (", ")
+                                out_java_struct += (
+                                    explode_arg.java_hu_ty + " " + arg.arg_name + "_" + explode_arg.arg_name)
+                        else:
+                            out_java_struct += (arg.java_hu_ty + " " + arg.arg_name)
+        out_java += (");\n")
+        out_c += (") {\n")
+        if out_java_struct is not None:
+            out_java_struct += (") {\n")
+        for info in argument_types:
+            if info.arg_conv is not None:
+                out_c += ("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
+        if return_type_info.ret_conv is not None:
+            out_c += ("\t" + ret_conv_pfx.replace('\n', '\n\t'))
+        elif return_type_info.c_ty != "void":
+            out_c += ("\t" + return_type_info.c_ty + " ret_val = ")
+        else:
+            out_c += ("\t")
+        if c_call_string is None:
+            out_c += (method_name + "(")
+        else:
+            out_c += (c_call_string)
+        for idx, info in enumerate(argument_types):
+            if info.arg_conv_name is not None:
+                if idx != 0:
+                    out_c += (", ")
+                elif c_call_string is not None:
+                    continue
+                out_c += (info.arg_conv_name)
+        out_c += (")")
+        if return_type_info.ret_conv is not None:
+            out_c += (ret_conv_sfx.replace('\n', '\n\t'))
+        else:
+            out_c += (";")
+        for info in argument_types:
+            if info.arg_conv_cleanup is not None:
+                out_c += ("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
+        if return_type_info.ret_conv is not None:
+            out_c += ("\n\treturn " + return_type_info.ret_conv_name + ";")
+        elif return_type_info.c_ty != "void":
+            out_c += ("\n\treturn ret_val;")
+        out_c += ("\n}\n\n")
+
+        if has_out_java_struct:
+            out_java_struct += ("\t\t")
+            if return_type_info.java_ty != "void":
+                out_java_struct += (return_type_info.java_ty + " ret = ")
+            out_java_struct += ("bindings." + method_name + "(")
+            for idx, info in enumerate(argument_types):
+                if idx != 0:
+                    out_java_struct += (", ")
+                if idx == 0 and takes_self:
+                    out_java_struct += ("this.ptr")
+                elif info.arg_name in default_constructor_args:
+                    out_java_struct += ("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 += (", ")
+                        expl_arg_name = info.arg_name + "_" + explode_arg.arg_name
+                        if explode_arg.from_hu_conv is not None:
+                            out_java_struct += (
+                                explode_arg.from_hu_conv[0].replace(explode_arg.arg_name, expl_arg_name))
+                        else:
+                            out_java_struct += (expl_arg_name)
+                    out_java_struct += (")")
+                elif info.from_hu_conv is not None:
+                    out_java_struct += (info.from_hu_conv[0])
+                else:
+                    out_java_struct += (info.arg_name)
+            out_java_struct += (");\n")
+            if return_type_info.to_hu_conv is not None:
+                if not takes_self:
+                    out_java_struct += ("\t\t" + return_type_info.to_hu_conv.replace("\n", "\n\t\t").replace("this",
+                                                                                                               return_type_info.to_hu_conv_name) + "\n")
+                else:
+                    out_java_struct += ("\t\t" + return_type_info.to_hu_conv.replace("\n", "\n\t\t") + "\n")
+
+            for idx, info in enumerate(argument_types):
+                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 return_type_info.to_hu_conv_name:
+                            out_java_struct += ("\t\t" + explode_arg.from_hu_conv[1].replace(explode_arg.arg_name,
+                                                                                               expl_arg_name).replace(
+                                "this", return_type_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 return_type_info.to_hu_conv_name is not None:
+                        out_java_struct += (
+                            "\t\t" + info.from_hu_conv[1].replace("this", return_type_info.to_hu_conv_name) + ";\n")
+                    else:
+                        out_java_struct += ("\t\t" + info.from_hu_conv[1] + ";\n")
+
+            if return_type_info.to_hu_conv_name is not None:
+                out_java_struct += ("\t\treturn " + return_type_info.to_hu_conv_name + ";\n")
+            elif return_type_info.java_ty != "void" and return_type_info.rust_obj != "LDK" + struct_meth:
+                out_java_struct += ("\t\treturn ret;\n")
+            out_java_struct += ("\t}\n\n")
+
+        return (out_java, out_c, out_java_struct)