Update CI references to 0.0.122
[ldk-java] / java_strings.py
index c6905d1359f5f2f03e31e1c58b85b9f319d79c36..b0c100e7fa6fc82e89eb1823593e7dbdab4305a2 100644 (file)
@@ -5,16 +5,20 @@ import sys
 class Target(Enum):
     JAVA = 1,
     ANDROID = 2
+    MACOS = 3
 
 class Consts:
     def __init__(self, DEBUG: bool, target: Target, **kwargs):
         self.target = target
         self.c_array_class_caches = set()
         self.c_type_map = dict(
+            bool = ['boolean'],
             uint8_t = ['byte'],
             uint16_t = ['short'],
             uint32_t = ['int'],
             uint64_t = ['long'],
+            int64_t = ['long'],
+            double = ['double'],
         )
         self.java_type_map = dict(
             String = "String"
@@ -68,6 +72,7 @@ public class bindings {
                // Fetching the LDK versions from C also checks that the header and binaries match
                System.err.println("Loaded LDK-Java Bindings " + version.get_ldk_java_bindings_version() + " with LDK " + get_ldk_version() + " and LDK-C-Bindings " + get_ldk_c_bindings_version());
        }
+       public static void run_statics() { /* Useful to force the statics to run */ }
        static native void init(java.lang.Class c);
        static native void init_class_cache();
        static native String get_lib_version_string();
@@ -84,8 +89,6 @@ public class version {
        }
 }"""
 
-        self.bindings_footer = "}\n"
-
         self.util_fn_pfx = """package org.ldk.structs;
 import org.ldk.impl.bindings;
 import org.ldk.enums.*;
@@ -130,6 +133,38 @@ class CommonBase {
        }
 }"""
 
+        self.txin_defn = """public class TxIn extends CommonBase {
+       /** The witness in this input, in serialized form */
+       public final byte[] witness;
+       /** The script_sig in this input */
+       public final byte[] script_sig;
+       /** The transaction output's sequence number */
+       public final int sequence;
+       /** The txid this input is spending */
+       public final byte[] previous_txid;
+       /** The output index within the spent transaction of the output this input is spending */
+       public final int previous_vout;
+
+       TxIn(java.lang.Object _dummy, long ptr) {
+               super(ptr);
+               this.witness = bindings.TxIn_get_witness(ptr);
+               this.script_sig = bindings.TxIn_get_script_sig(ptr);
+               this.sequence = bindings.TxIn_get_sequence(ptr);
+               this.previous_txid = bindings.TxIn_get_previous_txid(ptr);
+               this.previous_vout = bindings.TxIn_get_previous_vout(ptr);
+       }
+       /** Constructs a new TxIn, note that previous_txid must be exactly 32 bytes */
+       public TxIn(byte[] witness, byte[] script_sig, int sequence, byte[] previous_txid, int previous_vout) {
+               this(null, bindings.TxIn_new(witness, script_sig, sequence, previous_txid, previous_vout));
+       }
+
+       @Override @SuppressWarnings(\"deprecation\")
+       protected void finalize() throws Throwable {
+               super.finalize();
+               if (ptr != 0) { bindings.TxIn_free(ptr); }
+       }
+}"""
+
         self.scalar_defn = """public class BigEndianScalar extends CommonBase {
        /** The bytes of the scalar value, in big endian */
        public final byte[] scalar_bytes;
@@ -150,6 +185,34 @@ class CommonBase {
        }
 }"""
 
+        self.witness_program_defn = """public class WitnessProgram extends CommonBase {
+       /** The witness program bytes themselves */
+       public final byte[] program;
+       /** The witness version */
+       public final WitnessVersion version;
+
+       WitnessProgram(java.lang.Object _dummy, long ptr) {
+               super(ptr);
+               this.program = bindings.WitnessProgram_get_program(ptr);
+               this.version = new WitnessVersion(bindings.WitnessProgram_get_version(ptr));
+       }
+       static byte check_args(byte[] program, WitnessVersion version) {
+               if (program.length < 2 || program.length > 40) throw new IllegalArgumentException();
+               if (version.getVal() == 0 && program.length != 20 && program.length != 32) throw new IllegalArgumentException();
+               return version.getVal();
+       }
+       public WitnessProgram(byte[] program, WitnessVersion version) {
+               super(bindings.WitnessProgram_new(check_args(program, version), program));
+               this.program = bindings.WitnessProgram_get_program(ptr);
+               this.version = new WitnessVersion(bindings.WitnessProgram_get_version(ptr));
+       }
+
+       @Override @SuppressWarnings(\"deprecation\")
+       protected void finalize() throws Throwable {
+               super.finalize();
+               if (ptr != 0) { bindings.WitnessProgram_free(ptr); }
+       }
+}"""
 
         self.c_file_pfx = """#include <jni.h>
 // On OSX jlong (ie long long) is not equivalent to int64_t, so we override here
@@ -194,7 +257,7 @@ void __attribute__((constructor)) spawn_stderr_redirection() {
         else:
             self.c_file_pfx = self.c_file_pfx + "#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)\n"
 
-        if not DEBUG or sys.platform == "darwin":
+        if not DEBUG or self.target == Target.MACOS:
             self.c_file_pfx = self.c_file_pfx + """#define MALLOC(a, _) malloc(a)
 #define FREE(p) if ((uint64_t)(p) > 4096) { free(p); }
 #define CHECK_ACCESS(p)
@@ -219,7 +282,7 @@ void __attribute__((constructor)) debug_log_version() {
 }
 """
 
-            if sys.platform != "darwin":
+            if self.target != Target.MACOS:
                 self.c_file_pfx += """
 // Running a leak check across all the allocations and frees of the JDK is a mess,
 // so instead we implement our own naive leak checker here, relying on the -wrap
@@ -427,26 +490,135 @@ _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* conv_buf = MALLOC(len + 1, "str conv buf");
-       memcpy(conv_buf, chars, len);
-       conv_buf[len] = 0;
-       jstring ret = (*env)->NewStringUTF(env, conv_buf);
-       FREE(conv_buf);
+typedef jshortArray int16_tArray;
+
+static inline jstring str_ref_to_java(JNIEnv *env, const unsigned char* chars, size_t len) {
+       // Java uses "Modified UTF-8" rather than UTF-8. This requires special
+       // handling for codepoints above 0xFFFF, which get converted from four
+       // bytes to six. We don't know upfront how many codepoints in the string
+       // are above 0xFFFF, so we just allocate an extra 33% up front and waste a
+       // bit of space.
+       unsigned char* java_chars = MALLOC(len * 3 / 2 + 1, "str conv buf");
+       unsigned char* next_java_char = java_chars;
+       const unsigned char* next_in_char = chars;
+       const unsigned char* end = chars + len;
+       #define COPY_CHAR_TO_JAVA do { *next_java_char = *next_in_char; next_java_char++; next_in_char++; } while (0)
+
+       while (next_in_char < end) {
+               if (!*next_in_char) break;
+               if (!(*next_in_char & 0b10000000)) {
+                       COPY_CHAR_TO_JAVA;
+               } else if ((*next_in_char & 0b11100000) == 0b11000000) {
+                       if (next_in_char + 2 > end) { CHECK(false); break; } // bad string
+                       COPY_CHAR_TO_JAVA;
+                       COPY_CHAR_TO_JAVA;
+               } else if ((*next_in_char & 0b11110000) == 0b11100000) {
+                       if (next_in_char + 3 > end) { CHECK(false); break; } // bad string
+                       COPY_CHAR_TO_JAVA;
+                       COPY_CHAR_TO_JAVA;
+                       COPY_CHAR_TO_JAVA;
+               } else if ((*next_in_char & 0b11111000) == 0b11110000) {
+                       if (next_in_char + 4 > end) { CHECK(false); break; } // bad string
+                       uint32_t codepoint = 0;
+                       codepoint |= (((uint32_t)*(next_in_char    )) & 0b00000111) << 18;
+                       codepoint |= (((uint32_t)*(next_in_char + 1)) & 0b00111111) << 12;
+                       codepoint |= (((uint32_t)*(next_in_char + 2)) & 0b00111111) << 6;
+                       codepoint |= (((uint32_t)*(next_in_char + 3)) & 0b00111111) << 0;
+                       codepoint -= 0x10000;
+                       *next_java_char = 0b11101101;
+                       next_java_char++;
+                       *next_java_char = 0b10100000 | ((codepoint >> 16) & 0b00001111);
+                       next_java_char++;
+                       *next_java_char = 0b10000000 | ((codepoint >> 10) & 0b00111111);
+                       next_java_char++;
+                       *next_java_char = 0b11101101;
+                       next_java_char++;
+                       *next_java_char = 0b10110000 | ((codepoint >>  6) & 0b00001111);
+                       next_java_char++;
+                       *next_java_char = 0b10000000 | ((codepoint >>  0) & 0b00111111);
+                       next_java_char++;
+                       next_in_char += 4;
+               } else {
+                       // Bad string
+                       CHECK(false);
+                       break;
+               }
+       }
+       *next_java_char = 0;
+       jstring ret = (*env)->NewStringUTF(env, java_chars);
+       FREE(java_chars);
        return ret;
 }
 static inline LDKStr java_to_owned_str(JNIEnv *env, jstring str) {
        uint64_t str_len = (*env)->GetStringUTFLength(env, str);
-       char* newchars = MALLOC(str_len + 1, "String chars");
-       const char* jchars = (*env)->GetStringUTFChars(env, str, NULL);
-       memcpy(newchars, jchars, str_len);
-       newchars[str_len] = 0;
+       // Java uses "Modified UTF-8" rather than UTF-8. This requires special
+       // handling for codepoints above 0xFFFF, which we implement below.
+       unsigned char* newchars = MALLOC(str_len, "String chars");
+       unsigned char* next_newchar = newchars;
+       uint64_t utf8_len = 0;
+
+       const unsigned char* jchars = (*env)->GetStringUTFChars(env, str, NULL);
+       const unsigned char* next_char = jchars;
+       const unsigned char* end = jchars + str_len;
+
+       #define COPY_CHAR_FROM_JAVA do { *next_newchar = *next_char; next_newchar++; next_char++; utf8_len++; } while (0)
+
+       while (next_char < end) {
+               if (!(*next_char & 0b10000000)) {
+                       CHECK(*next_char != 0); // Bad Modified UTF-8 string, but we'll just cut here
+                       COPY_CHAR_FROM_JAVA;
+               } else if ((*next_char & 0b11100000) == 0b11000000) {
+                       if (next_char + 2 > end) { CHECK(false); break; } // bad string
+                       uint16_t codepoint = 0;
+                       codepoint |= (((uint16_t)(*next_char & 0x1f)) << 6);
+                       codepoint |= *(next_char + 1) & 0x3f;
+                       if (codepoint == 0) {
+                               // We should really never get null codepoints, but java allows them.
+                               // Just skip it.
+                               next_char += 2;
+                       } else {
+                               COPY_CHAR_FROM_JAVA;
+                               COPY_CHAR_FROM_JAVA;
+                       }
+               } else if ((*next_char & 0b11110000) == 0b11100000) {
+                       if (next_char + 3 > end) { CHECK(false); break; } // bad string
+                       if (*next_char == 0b11101101 && (*(next_char + 1) & 0b11110000) == 0b10100000) {
+                               // Surrogate code unit shoul indicate we have a codepoint above
+                               // 0xFFFF, which is where Modified UTF-8 and UTF-8 diverge.
+                               if (next_char + 6 > end) { CHECK(false); break; } // bad string
+                               CHECK(*(next_char + 3) == 0b11101101);
+                               CHECK((*(next_char + 4) & 0b11110000) == 0b10110000);
+                               // Calculate the codepoint per https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp16542
+                               uint32_t codepoint = 0x10000;
+                               codepoint += ((((uint32_t)*(next_char + 1)) & 0x0f) << 16);
+                               codepoint += ((((uint32_t)*(next_char + 2)) & 0x3f) << 10);
+                               codepoint += ((((uint32_t)*(next_char + 4)) & 0x0f) <<  6);
+                               codepoint +=  (((uint32_t)*(next_char + 5)) & 0x3f);
+                               *next_newchar = 0b11110000 | ((codepoint >> 18) &    0b111);
+                               next_newchar++;
+                               *next_newchar = 0b10000000 | ((codepoint >> 12) & 0b111111);
+                               next_newchar++;
+                               *next_newchar = 0b10000000 | ((codepoint >>  6) & 0b111111);
+                               next_newchar++;
+                               *next_newchar = 0b10000000 | ( codepoint        & 0b111111);
+                               next_newchar++;
+                               next_char += 6;
+                               utf8_len += 4;
+                       } else {
+                               COPY_CHAR_FROM_JAVA;
+                               COPY_CHAR_FROM_JAVA;
+                               COPY_CHAR_FROM_JAVA;
+                       }
+               } else {
+                       // Bad string
+                       CHECK(false);
+                       break;
+               }
+       }
        (*env)->ReleaseStringUTFChars(env, str, jchars);
        LDKStr res = {
                .chars = newchars,
-               .len = str_len,
+               .len = utf8_len,
                .chars_is_owned = true
        };
        return res;
@@ -474,6 +646,7 @@ import java.lang.ref.Reference;
 import javax.annotation.Nullable;
 
 """
+        self.hu_struct_file_suffix = ""
         self.c_fn_ty_pfx = "JNIEXPORT "
         self.c_fn_args_pfx = "JNIEnv *env, jclass clz"
         self.file_ext = ".java"
@@ -483,11 +656,14 @@ import javax.annotation.Nullable;
         self.usize_c_ty = "int64_t"
         self.usize_native_ty = "long"
         self.native_zero_ptr = "0"
-        self.result_c_ty = "jclass"
+        self.unitary_enum_c_ty = "jclass"
         self.ptr_arr = "jobjectArray"
         self.is_arr_some_check = ("", " != NULL")
         self.get_native_arr_len_call = ("(*env)->GetArrayLength(env, ", ")")
 
+    def bindings_footer(self):
+        return "}\n"
+
     def construct_jenv(self):
         res =  "JNIEnv *env;\n"
         res += "jint get_jenv_res = (*j_calls->vm)->GetEnv(j_calls->vm, (void**)&env, JNI_VERSION_1_6);\n"
@@ -517,19 +693,26 @@ import javax.annotation.Nullable;
             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"
+        elif ty_info.subty.c_ty == "jstring":
+            return "(*env)->NewObjectArray(env, " + arr_len + ", String_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):
         if ty_info.c_ty == "int8_tArray":
             return ("(*env)->SetByteArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", ", ")")
+        elif ty_info.c_ty == "int16_tArray":
+            return ("(*env)->SetShortArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", ", ")")
         else:
             assert False
     def get_native_arr_contents(self, arr_name, dest_name, arr_len, ty_info, copy):
-        if ty_info.c_ty == "int8_tArray":
+        if "String" in ty_info.java_ty:
+            return None
+        if ty_info.c_ty == "int8_tArray" or ty_info.c_ty == "int16_tArray":
+            fn_ty = "Byte" if ty_info.c_ty == "int8_tArray" else "Short"
             if copy:
-                return "(*env)->GetByteArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", " + dest_name + ")"
+                return "(*env)->Get" + fn_ty + "ArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", " + dest_name + ")"
             else:
-                return "(*env)->GetByteArrayElements (env, " + arr_name + ", NULL)"
+                return "(*env)->Get" + fn_ty + "ArrayElements (env, " + arr_name + ", NULL)"
         elif not ty_info.java_ty[:len(ty_info.java_ty) - 2].endswith("[]"):
             return "(*env)->Get" + ty_info.subty.java_ty.title() + "ArrayElements (env, " + arr_name + ", NULL)"
         else:
@@ -553,7 +736,7 @@ import javax.annotation.Nullable;
         else:
             return "(*env)->Release" + ty_info.java_ty.strip("[]").title() + "ArrayElements(env, " + arr_name + ", " + dest_name + ", 0)"
 
-    def map_hu_array_elems(self, arr_name, conv_name, arr_ty, elem_ty):
+    def map_hu_array_elems(self, arr_name, conv_name, arr_ty, elem_ty, is_nullable):
         if elem_ty.java_ty == "long" and elem_ty.java_hu_ty != "long":
             return arr_name + " != null ? Arrays.stream(" + arr_name + ").mapToLong(" + conv_name + " -> " + elem_ty.from_hu_conv[0] + ").toArray() : null"
         elif elem_ty.java_ty == "long":
@@ -583,11 +766,16 @@ import javax.annotation.Nullable;
         res = ""
         for ty in sorted(self.c_array_class_caches):
             res = res + "static jclass " + ty + "_clz = NULL;\n"
+        res = res + "static jclass String_clz = NULL;\n"
         res = res + "JNIEXPORT void Java_org_ldk_impl_bindings_init_1class_1cache(JNIEnv * env, jclass clz) {\n"
         for ty in sorted(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"
+        res = res + "\tString_clz = (*env)->FindClass(env, \"java/lang/String\");\n"
+        res = res + "\tCHECK(String_clz != NULL);\n"
+        res = res + "\tString_clz = (*env)->NewGlobalRef(env, String_clz);\n"
+
         res = res + "}\n"
         return res
 
@@ -613,7 +801,13 @@ import javax.annotation.Nullable;
         if arr_ty.rust_obj == "LDKU128":
             return ("" + arr_name + ".getLEBytes()", "")
         if fixed_len is not None:
-            return ("InternalUtils.check_arr_len(" + arr_name + ", " + fixed_len + ")", "")
+            if mapped_ty.c_ty == "int8_t" or mapped_ty.c_ty == "uint8_t":
+                return ("InternalUtils.check_arr_len(" + arr_name + ", " + fixed_len + ")", "")
+            elif mapped_ty.c_ty == "int16_t" or mapped_ty.c_ty == "uint16_t":
+                return ("InternalUtils.check_arr_16_len(" + arr_name + ", " + fixed_len + ")", "")
+            else:
+                print(arr_ty.c_ty)
+                assert False
         return None
     def primitive_arr_to_hu(self, arr_ty, fixed_len, arr_name, conv_name):
         if arr_ty.rust_obj == "LDKU128":
@@ -668,7 +862,7 @@ import javax.annotation.Nullable;
             out_c = out_c + "\t\tcase %d: return %s;\n" % (ord_v, var)
             ord_v = ord_v + 1
         out_java_enum = out_java_enum + "\t; static native void init();\n"
-        out_java_enum = out_java_enum + "\tstatic { init(); }\n"
+        out_java_enum = out_java_enum + "\tstatic { org.ldk.impl.bindings.run_statics(); init(); }\n"
         out_java_enum = out_java_enum + "}"
         out_java = out_java + "\tstatic { " + struct_name + ".values(); /* Force enum statics to run */ }\n"
         out_c += "\t}\n"
@@ -764,12 +958,26 @@ import javax.annotation.Nullable;
                     out_java_trait = out_java_trait + "\t\t" + var.from_hu_conv[1].replace("\n", "\n\t\t") + ";\n"
             else:
                 out_java_trait = out_java_trait + "\t\tthis.ptrs_to.add(" + var[1] + ");\n"
-        out_java_trait = out_java_trait + "\t\tthis.bindings_instance = arg;\n"
-        out_java_trait = out_java_trait + "\t}\n"
-        out_java_trait = out_java_trait + "\t@Override @SuppressWarnings(\"deprecation\")\n"
-        out_java_trait = out_java_trait + "\tprotected void finalize() throws Throwable {\n"
-        out_java_trait = out_java_trait + "\t\tif (ptr != 0) { bindings." + struct_name.replace("LDK","") + "_free(ptr); } super.finalize();\n"
-        out_java_trait = out_java_trait + "\t}\n\n"
+        out_java_trait += f"""         this.bindings_instance = arg;
+       }}
+       @Override @SuppressWarnings("deprecation")
+       protected void finalize() throws Throwable {{
+               if (ptr != 0) {{ bindings.{struct_name.replace("LDK","")}_free(ptr); }} super.finalize();
+       }}
+       /**
+        * Destroys the object, freeing associated resources. After this call, any access
+        * to this object may result in a SEGFAULT or worse.
+        *
+        * You should generally NEVER call this method. You should let the garbage collector
+        * do this for you when it finalizes objects. However, it may be useful for types
+        * which represent locks and should be closed immediately to avoid holding locks
+        * until the GC runs.
+        */
+       public void destroy() {{
+               if (ptr != 0) {{ bindings.{struct_name.replace("LDK","")}_free(ptr); }}
+               ptr = 0;
+       }}
+"""
 
         java_trait_constr = "\tprivate static class " + struct_name + "Holder { " + struct_name.replace("LDK", "") + " held; }\n"
         java_trait_constr = java_trait_constr + "\tpublic static " + struct_name.replace("LDK", "") + " new_impl(" + struct_name.replace("LDK", "") + "Interface arg"
@@ -844,17 +1052,21 @@ import javax.annotation.Nullable;
                 java_trait_constr = java_trait_constr + ", " + var.arg_name
             else:
                 java_trait_constr += ", " + var[1] + ".new_impl(" + var[1] + "_impl"
+                suptrait_constr = ""
                 for suparg in var[2]:
                     if isinstance(suparg, ConvInfo):
-                        java_trait_constr += ", " + suparg.arg_name
+                        suptrait_constr += ", " + suparg.arg_name
                     else:
-                        java_trait_constr += ", " + suparg[1]
-                java_trait_constr += ").bindings_instance"
+                        suptrait_constr += ", " + suparg[1] + "_impl"
+                java_trait_constr += suptrait_constr + ").bindings_instance"
                 for suparg in var[2]:
                     if isinstance(suparg, ConvInfo):
                         java_trait_constr += ", " + suparg.arg_name
                     else:
-                        java_trait_constr += ", " + suparg[1]
+                        java_trait_constr += ", " + suparg[1] + ".new_impl("
+                        # Blindly assume that we can just strip the first arg to build the args for the supertrait
+                        java_trait_constr += suptrait_constr.split(", ", 1)[1]
+                        java_trait_constr += ").bindings_instance"
         out_java_trait = out_java_trait + "\t}\n"
         out_java_trait = out_java_trait + java_trait_constr + ");\n\t\treturn impl_holder.held;\n\t}\n"
 
@@ -963,9 +1175,9 @@ import javax.annotation.Nullable;
             out_c = out_c + "static void " + struct_name + "_JCalls_cloned(" + struct_name + "* new_obj) {\n"
             out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) new_obj->this_arg;\n"
             out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->refcnt, 1, memory_order_release);\n"
-            for var in field_vars:
+            for var in flattened_field_vars:
                 if not isinstance(var, ConvInfo):
-                    out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->" + var[1] + "->refcnt, 1, memory_order_release);\n"
+                    out_c = out_c + "\tatomic_fetch_add_explicit(&j_calls->" + var[2].replace(".", "->") + "->refcnt, 1, memory_order_release);\n"
             out_c = out_c + "}\n"
 
         out_c = out_c + "static inline " + struct_name + " " + struct_name + "_init (" + self.c_fn_args_pfx + ", jobject o"
@@ -1019,7 +1231,7 @@ import javax.annotation.Nullable;
         out_c = out_c + "\t};\n"
         for var in flattened_field_vars:
             if not isinstance(var, ConvInfo):
-                out_c = out_c + "\tcalls->" + var[1] + " = ret." + var[1] + ".this_arg;\n"
+                out_c = out_c + "\tcalls->" + var[1] + " = ret." + var[2] + ".this_arg;\n"
         out_c = out_c + "\treturn ret;\n"
         out_c = out_c + "}\n"
 
@@ -1049,7 +1261,7 @@ import javax.annotation.Nullable;
                 underscore_name = ''.join('_' + c.lower() if c.isupper() else c for c in var[1]).strip('_')
                 out_java_trait += "\tpublic " + var[1] + " get_" + underscore_name + "() {\n"
                 out_java_trait += "\t\t" + var[1] + " res = new " + var[1] + "(null, bindings." + struct_name + "_get_" + var[1] + "(this.ptr));\n"
-                out_java_trait += "\t\tthis.ptrs_to.add(res);\n"
+                out_java_trait += "\t\tres.ptrs_to.add(this);\n"
                 out_java_trait += "\t\treturn res;\n"
                 out_java_trait += "\t}\n"
                 out_java_trait += "\n"
@@ -1058,7 +1270,7 @@ import javax.annotation.Nullable;
 
                 out_c += "JNIEXPORT int64_t JNICALL Java_org_ldk_impl_bindings_" + struct_name + "_1get_1" + var[1] + "(JNIEnv *env, jclass clz, int64_t arg) {\n"
                 out_c += "\t" + struct_name + " *inp = (" + struct_name + " *)untag_ptr(arg);\n"
-                out_c += "\treturn tag_ptr(&inp->" + var[1] + ", false);\n"
+                out_c += "\treturn tag_ptr(&inp->" + var[2] + ", false);\n"
                 out_c += "}\n"
 
         return (out_java, out_java_trait, out_c)
@@ -1300,6 +1512,8 @@ import javax.annotation.Nullable;
                     extra_java_struct_out += "\t\tif (!(o instanceof " + struct_meth + ")) return false;\n"
                     extra_java_struct_out += "\t\treturn this.eq((" + struct_meth + ")o);\n"
                     extra_java_struct_out += "\t}\n"
+                if meth_n == "wait":
+                    meth_n = "wait_indefinite"
                 out_java_struct += ("\tpublic " + return_type_info.java_hu_ty + " " + meth_n + "(")
             for idx, arg in enumerate(argument_types):
                 if idx != 0:
@@ -1318,9 +1532,7 @@ import javax.annotation.Nullable;
                     else:
                         if arg.nullable:
                             out_java_struct += "@Nullable "
-                        ty_string = arg.java_hu_ty
-                        if arg.java_fn_ty_arg[0] == "L" and arg.java_fn_ty_arg[len(arg.java_fn_ty_arg) - 1] == ";":
-                            ty_string = arg.java_fn_ty_arg.strip("L;").replace("/", ".")
+                        ty_string = self.fully_qualified_hu_ty_path(arg)
                         out_java_struct += ty_string + " " + arg.arg_name
         out_java += (");\n")
         out_c += (") {\n")