explode to constructor args when we cant clone but need to
authorMatt Corallo <git@bluematt.me>
Tue, 13 Oct 2020 23:47:13 +0000 (19:47 -0400)
committerMatt Corallo <git@bluematt.me>
Tue, 13 Oct 2020 23:47:13 +0000 (19:47 -0400)
genbindings.py
src/main/java/org/ldk/structs/PeerManager.java
src/test/java/org/ldk/HumanObjectPeerTest.java

index 73ef5f92d42d2360debe5330f1492eb4b21e5de4..a0d0ab629c6dfe3dfa9cae8e8aba1ac7517984f8 100755 (executable)
@@ -46,176 +46,185 @@ class ConvInfo:
         else:
             out_java.write(" arg")
             out_c.write(" arg")
+
+def camel_to_snake(s):
+    # Convert camel case to snake case, in a way that appears to match cbindgen
+    con = "_"
+    ret = ""
+    lastchar = ""
+    lastund = False
+    for char in s:
+        if lastchar.isupper():
+            if not char.isupper() and not lastund:
+                ret = ret + "_"
+                lastund = True
+            else:
+                lastund = False
+            ret = ret + lastchar.lower()
+        else:
+            ret = ret + lastchar
+            if char.isupper() and not lastund:
+                ret = ret + "_"
+                lastund = True
+            else:
+                lastund = False
+        lastchar = char
+        if char.isnumeric():
+            lastund = True
+    return (ret + lastchar.lower()).strip("_")
+
+unitary_enums = set()
+var_is_arr_regex = re.compile("\(\*([A-za-z0-9_]*)\)\[([a-z0-9]*)\]")
+var_ty_regex = re.compile("([A-za-z_0-9]*)(.*)")
+def java_c_types(fn_arg, ret_arr_len):
+    fn_arg = fn_arg.strip()
+    if fn_arg.startswith("MUST_USE_RES "):
+        fn_arg = fn_arg[13:]
+    is_const = False
+    if fn_arg.startswith("const "):
+        fn_arg = fn_arg[6:]
+        is_const = True
+
+    is_ptr = False
+    take_by_ptr = False
+    rust_obj = None
+    arr_access = None
+    if fn_arg.startswith("LDKThirtyTwoBytes"):
+        fn_arg = "uint8_t (*" + fn_arg[18:] + ")[32]"
+        assert var_is_arr_regex.match(fn_arg[8:])
+        rust_obj = "LDKThirtyTwoBytes"
+        arr_access = "data"
+    if fn_arg.startswith("LDKPublicKey"):
+        fn_arg = "uint8_t (*" + fn_arg[13:] + ")[33]"
+        assert var_is_arr_regex.match(fn_arg[8:])
+        rust_obj = "LDKPublicKey"
+        arr_access = "compressed_form"
+    if fn_arg.startswith("LDKSecretKey"):
+        fn_arg = "uint8_t (*" + fn_arg[13:] + ")[32]"
+        assert var_is_arr_regex.match(fn_arg[8:])
+        rust_obj = "LDKSecretKey"
+        arr_access = "bytes"
+    if fn_arg.startswith("LDKSignature"):
+        fn_arg = "uint8_t (*" + fn_arg[13:] + ")[64]"
+        assert var_is_arr_regex.match(fn_arg[8:])
+        rust_obj = "LDKSignature"
+        arr_access = "compact_form"
+    if fn_arg.startswith("LDKThreeBytes"):
+        fn_arg = "uint8_t (*" + fn_arg[14:] + ")[3]"
+        assert var_is_arr_regex.match(fn_arg[8:])
+        rust_obj = "LDKThreeBytes"
+        arr_access = "data"
+    if fn_arg.startswith("LDKu8slice"):
+        fn_arg = "uint8_t (*" + fn_arg[11:] + ")[datalen]"
+        assert var_is_arr_regex.match(fn_arg[8:])
+        rust_obj = "LDKu8slice"
+        arr_access = "data"
+
+    if fn_arg.startswith("void"):
+        java_ty = "void"
+        c_ty = "void"
+        fn_ty_arg = "V"
+        fn_arg = fn_arg[4:].strip()
+    elif fn_arg.startswith("bool"):
+        java_ty = "boolean"
+        c_ty = "jboolean"
+        fn_ty_arg = "Z"
+        fn_arg = fn_arg[4:].strip()
+    elif fn_arg.startswith("uint8_t"):
+        java_ty = "byte"
+        c_ty = "jbyte"
+        fn_ty_arg = "B"
+        fn_arg = fn_arg[7:].strip()
+    elif fn_arg.startswith("uint16_t"):
+        java_ty = "short"
+        c_ty = "jshort"
+        fn_ty_arg = "S"
+        fn_arg = fn_arg[8:].strip()
+    elif fn_arg.startswith("uint32_t"):
+        java_ty = "int"
+        c_ty = "jint"
+        fn_ty_arg = "I"
+        fn_arg = fn_arg[8:].strip()
+    elif fn_arg.startswith("uint64_t") or fn_arg.startswith("uintptr_t"):
+        java_ty = "long"
+        c_ty = "jlong"
+        fn_ty_arg = "J"
+        if fn_arg.startswith("uint64_t"):
+            fn_arg = fn_arg[8:].strip()
+        else:
+            fn_arg = fn_arg[9:].strip()
+    elif is_const and fn_arg.startswith("char *"):
+        java_ty = "String"
+        c_ty = "const char*"
+        fn_ty_arg = "Ljava/lang/String;"
+        fn_arg = fn_arg[6:].strip()
+    else:
+        ma = var_ty_regex.match(fn_arg)
+        if ma.group(1).strip() in unitary_enums:
+            java_ty = ma.group(1).strip()
+            c_ty = "jclass"
+            fn_ty_arg = "Lorg/ldk/enums/" + ma.group(1).strip() + ";"
+            fn_arg = ma.group(2).strip()
+            rust_obj = ma.group(1).strip()
+            take_by_ptr = True
+        else:
+            java_ty = "long"
+            c_ty = "jlong"
+            fn_ty_arg = "J"
+            fn_arg = ma.group(2).strip()
+            rust_obj = ma.group(1).strip()
+            take_by_ptr = True
+
+    if fn_arg.startswith(" *") or fn_arg.startswith("*"):
+        fn_arg = fn_arg.replace("*", "").strip()
+        is_ptr = True
+        c_ty = "jlong"
+        java_ty = "long"
+        fn_ty_arg = "J"
+
+    var_is_arr = var_is_arr_regex.match(fn_arg)
+    if var_is_arr is not None or ret_arr_len is not None:
+        assert(not take_by_ptr)
+        assert(not is_ptr)
+        java_ty = java_ty + "[]"
+        c_ty = c_ty + "Array"
+        if var_is_arr is not None:
+            if var_is_arr.group(1) == "":
+                return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty,
+                    passed_as_ptr=False, is_ptr=False, var_name="arg", arr_len=var_is_arr.group(2), arr_access=arr_access)
+            return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty,
+                passed_as_ptr=False, is_ptr=False, var_name=var_is_arr.group(1), arr_len=var_is_arr.group(2), arr_access=arr_access)
+    return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_fn_ty_arg=fn_ty_arg, c_ty=c_ty, passed_as_ptr=is_ptr or take_by_ptr,
+        is_ptr=is_ptr, var_name=fn_arg, arr_len=None, arr_access=None)
+
+
+
 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
 clone_fns = set()
+constructor_fns = {}
 with open(sys.argv[1]) as in_h:
     for line in in_h:
         reg_fn = reg_fn_regex.match(line)
         if reg_fn is not None:
             if reg_fn.group(2).endswith("_clone"):
                 clone_fns.add(reg_fn.group(2))
+            else:
+                rty = java_c_types(reg_fn.group(1), None)
+                if rty.rust_obj is not None and reg_fn.group(2) == rty.rust_obj.replace("LDK", "") + "_new":
+                    constructor_fns[rty.rust_obj] = reg_fn.group(3)
             continue
         arr_fn = fn_ret_arr_regex.match(line)
         if arr_fn is not None:
             if arr_fn.group(2).endswith("_clone"):
                 clone_fns.add(arr_fn.group(2))
+            # No object constructors return arrays, as then they wouldn't be an object constructor
             continue
 
 with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java, open(sys.argv[4], "w") as out_c:
     opaque_structs = set()
     trait_structs = set()
-    unitary_enums = set()
-
-    def camel_to_snake(s):
-        # Convert camel case to snake case, in a way that appears to match cbindgen
-        con = "_"
-        ret = ""
-        lastchar = ""
-        lastund = False
-        for char in s:
-            if lastchar.isupper():
-                if not char.isupper() and not lastund:
-                    ret = ret + "_"
-                    lastund = True
-                else:
-                    lastund = False
-                ret = ret + lastchar.lower()
-            else:
-                ret = ret + lastchar
-                if char.isupper() and not lastund:
-                    ret = ret + "_"
-                    lastund = True
-                else:
-                    lastund = False
-            lastchar = char
-            if char.isnumeric():
-                lastund = True
-        return (ret + lastchar.lower()).strip("_")
-
-    var_is_arr_regex = re.compile("\(\*([A-za-z0-9_]*)\)\[([a-z0-9]*)\]")
-    var_ty_regex = re.compile("([A-za-z_0-9]*)(.*)")
-    def java_c_types(fn_arg, ret_arr_len):
-        fn_arg = fn_arg.strip()
-        if fn_arg.startswith("MUST_USE_RES "):
-            fn_arg = fn_arg[13:]
-        is_const = False
-        if fn_arg.startswith("const "):
-            fn_arg = fn_arg[6:]
-            is_const = True
-
-        is_ptr = False
-        take_by_ptr = False
-        rust_obj = None
-        arr_access = None
-        if fn_arg.startswith("LDKThirtyTwoBytes"):
-            fn_arg = "uint8_t (*" + fn_arg[18:] + ")[32]"
-            assert var_is_arr_regex.match(fn_arg[8:])
-            rust_obj = "LDKThirtyTwoBytes"
-            arr_access = "data"
-        if fn_arg.startswith("LDKPublicKey"):
-            fn_arg = "uint8_t (*" + fn_arg[13:] + ")[33]"
-            assert var_is_arr_regex.match(fn_arg[8:])
-            rust_obj = "LDKPublicKey"
-            arr_access = "compressed_form"
-        if fn_arg.startswith("LDKSecretKey"):
-            fn_arg = "uint8_t (*" + fn_arg[13:] + ")[32]"
-            assert var_is_arr_regex.match(fn_arg[8:])
-            rust_obj = "LDKSecretKey"
-            arr_access = "bytes"
-        if fn_arg.startswith("LDKSignature"):
-            fn_arg = "uint8_t (*" + fn_arg[13:] + ")[64]"
-            assert var_is_arr_regex.match(fn_arg[8:])
-            rust_obj = "LDKSignature"
-            arr_access = "compact_form"
-        if fn_arg.startswith("LDKThreeBytes"):
-            fn_arg = "uint8_t (*" + fn_arg[14:] + ")[3]"
-            assert var_is_arr_regex.match(fn_arg[8:])
-            rust_obj = "LDKThreeBytes"
-            arr_access = "data"
-        if fn_arg.startswith("LDKu8slice"):
-            fn_arg = "uint8_t (*" + fn_arg[11:] + ")[datalen]"
-            assert var_is_arr_regex.match(fn_arg[8:])
-            rust_obj = "LDKu8slice"
-            arr_access = "data"
-
-        if fn_arg.startswith("void"):
-            java_ty = "void"
-            c_ty = "void"
-            fn_ty_arg = "V"
-            fn_arg = fn_arg[4:].strip()
-        elif fn_arg.startswith("bool"):
-            java_ty = "boolean"
-            c_ty = "jboolean"
-            fn_ty_arg = "Z"
-            fn_arg = fn_arg[4:].strip()
-        elif fn_arg.startswith("uint8_t"):
-            java_ty = "byte"
-            c_ty = "jbyte"
-            fn_ty_arg = "B"
-            fn_arg = fn_arg[7:].strip()
-        elif fn_arg.startswith("uint16_t"):
-            java_ty = "short"
-            c_ty = "jshort"
-            fn_ty_arg = "S"
-            fn_arg = fn_arg[8:].strip()
-        elif fn_arg.startswith("uint32_t"):
-            java_ty = "int"
-            c_ty = "jint"
-            fn_ty_arg = "I"
-            fn_arg = fn_arg[8:].strip()
-        elif fn_arg.startswith("uint64_t") or fn_arg.startswith("uintptr_t"):
-            java_ty = "long"
-            c_ty = "jlong"
-            fn_ty_arg = "J"
-            if fn_arg.startswith("uint64_t"):
-                fn_arg = fn_arg[8:].strip()
-            else:
-                fn_arg = fn_arg[9:].strip()
-        elif is_const and fn_arg.startswith("char *"):
-            java_ty = "String"
-            c_ty = "const char*"
-            fn_ty_arg = "Ljava/lang/String;"
-            fn_arg = fn_arg[6:].strip()
-        else:
-            ma = var_ty_regex.match(fn_arg)
-            if ma.group(1).strip() in unitary_enums:
-                java_ty = ma.group(1).strip()
-                c_ty = "jclass"
-                fn_ty_arg = "Lorg/ldk/enums/" + ma.group(1).strip() + ";"
-                fn_arg = ma.group(2).strip()
-                rust_obj = ma.group(1).strip()
-                take_by_ptr = True
-            else:
-                java_ty = "long"
-                c_ty = "jlong"
-                fn_ty_arg = "J"
-                fn_arg = ma.group(2).strip()
-                rust_obj = ma.group(1).strip()
-                take_by_ptr = True
-
-        if fn_arg.startswith(" *") or fn_arg.startswith("*"):
-            fn_arg = fn_arg.replace("*", "").strip()
-            is_ptr = True
-            c_ty = "jlong"
-            java_ty = "long"
-            fn_ty_arg = "J"
-
-        var_is_arr = var_is_arr_regex.match(fn_arg)
-        if var_is_arr is not None or ret_arr_len is not None:
-            assert(not take_by_ptr)
-            assert(not is_ptr)
-            java_ty = java_ty + "[]"
-            c_ty = c_ty + "Array"
-            if var_is_arr is not None:
-                if var_is_arr.group(1) == "":
-                    return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty,
-                        passed_as_ptr=False, is_ptr=False, var_name="arg", arr_len=var_is_arr.group(2), arr_access=arr_access)
-                return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty,
-                    passed_as_ptr=False, is_ptr=False, var_name=var_is_arr.group(1), arr_len=var_is_arr.group(2), arr_access=arr_access)
-        return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_fn_ty_arg=fn_ty_arg, c_ty=c_ty, passed_as_ptr=is_ptr or take_by_ptr,
-            is_ptr=is_ptr, var_name=fn_arg, arr_len=None, arr_access=None)
 
     def map_type(fn_arg, print_void, ret_arr_len, is_free):
         ty_info = java_c_types(fn_arg, ret_arr_len)
@@ -392,6 +401,7 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java, open(sys.arg
         out_c.write(" JNICALL Java_org_ldk_impl_bindings_" + re_match.group(2).replace('_', '_1') + "(JNIEnv * _env, jclass _b")
 
         arg_names = []
+        default_constructor_args = {}
         takes_self = False
         args_known = not ret_info.passed_as_ptr or ret_info.rust_obj in opaque_structs or ret_info.rust_obj in trait_structs
         for idx, arg in enumerate(re_match.group(3).split(',')):
@@ -407,10 +417,24 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java, open(sys.arg
                 takes_self = True
             if arg_conv_info.passed_as_ptr and not arg_conv_info.rust_obj in opaque_structs:
                 if not arg_conv_info.rust_obj in trait_structs and not arg_conv_info.rust_obj in unitary_enums:
-                    print(re_match.group(2) + " bad - " + arg_conv_info.rust_obj)
                     args_known = False
             if arg_conv_info.arg_conv is not None and "Warning" in arg_conv_info.arg_conv:
-                args_known = False
+                if arg_conv_info.rust_obj in constructor_fns:
+                    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)
+                        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
+                        assert explode_arg_conv.arg_name != "this_arg"
+                        if explode_arg_conv.passed_as_ptr and not explode_arg_conv.rust_obj in trait_structs:
+                            args_known = False
+                        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)
+                else:
+                    args_known = False
             arg_names.append(arg_conv_info)
 
         out_java_struct = None
@@ -434,7 +458,13 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java, open(sys.arg
                         if not takes_self or idx > 1:
                             out_java_struct.write(", ")
                     if arg.java_ty != "void" and arg.arg_name != "this_arg":
-                        if arg.passed_as_ptr:
+                        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(", ")
+                                assert explode_arg.rust_obj in opaque_structs or explode_arg.rust_obj in trait_structs
+                                out_java_struct.write(explode_arg.rust_obj.replace("LDK", "") + " " + arg.arg_name + "_" + explode_arg.arg_name)
+                        elif arg.passed_as_ptr:
                             if arg.rust_obj in opaque_structs or arg.rust_obj in trait_structs:
                                 out_java_struct.write(arg.rust_obj.replace("LDK", "") + " " + arg.arg_name)
                             else:
@@ -499,6 +529,15 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java, open(sys.arg
                     out_java_struct.write(", ")
                 if info.arg_name == "this_arg":
                     out_java_struct.write("this.ptr")
+                elif info.arg_name in default_constructor_args:
+                    out_java_struct.write("bindings." + info.rust_obj.replace("LDK", "") + "_new(")
+                    for explode_idx, explode_arg in enumerate(default_constructor_args[info.arg_name]):
+                        if explode_idx != 0:
+                            out_java_struct.write(", ")
+                        assert explode_arg.passed_as_ptr and explode_arg.rust_obj in trait_structs
+                        expl_arg_name = info.arg_name + "_" + explode_arg.arg_name
+                        out_java_struct.write(expl_arg_name + " == null ? 0 : " + expl_arg_name + ".ptr")
+                    out_java_struct.write(")")
                 elif info.passed_as_ptr and info.rust_obj in opaque_structs:
                     out_java_struct.write(info.arg_name + " == null ? 0 : " + info.arg_name + ".ptr & ~1")
                 elif info.passed_as_ptr and info.rust_obj in trait_structs:
@@ -518,6 +557,9 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java, open(sys.arg
             for info in arg_names:
                 if info.arg_name == "this_arg":
                     pass
+                elif info.arg_name in default_constructor_args:
+                    for explode_arg in default_constructor_args[info.arg_name]:
+                        out_java_struct.write("\t\tthis.ptrs_to.add(" + info.arg_name + "_" + explode_arg.arg_name + ");\n")
                 elif info.passed_as_ptr and (info.rust_obj in opaque_structs or info.rust_obj in trait_structs):
                     out_java_struct.write("\t\tthis.ptrs_to.add(" + info.arg_name + ");\n")
 
index d225690023ccd3ae6b3383d2b85cd33d0330581e..54b4e723658576cd5cf6e2700b1fcf9130098917 100644 (file)
@@ -10,7 +10,13 @@ public class PeerManager extends CommonBase {
                bindings.PeerManager_free(ptr); super.finalize();
        }
 
-       // Skipped PeerManager_new
+       public PeerManager(ChannelMessageHandler message_handler_chan_handler_arg, RoutingMessageHandler message_handler_route_handler_arg, byte[] our_node_secret, byte[] ephemeral_random_data, Logger logger) {
+               super(bindings.PeerManager_new(bindings.MessageHandler_new(message_handler_chan_handler_arg == null ? 0 : message_handler_chan_handler_arg.ptr, message_handler_route_handler_arg == null ? 0 : message_handler_route_handler_arg.ptr), our_node_secret, ephemeral_random_data, logger == null ? 0 : logger.ptr));
+               this.ptrs_to.add(message_handler_chan_handler_arg);
+               this.ptrs_to.add(message_handler_route_handler_arg);
+               this.ptrs_to.add(logger);
+       }
+
        // Skipped PeerManager_get_peer_node_ids
        // Skipped PeerManager_new_outbound_connection
        // Skipped PeerManager_new_inbound_connection
index cc305da98da9cc057db7938b84b0581579dae003..935eadb9daaa868777f838b1a96b591521b45d2d 100644 (file)
@@ -22,11 +22,8 @@ public class HumanObjectPeerTest {
         final KeysInterface keys_interface;
         final ChannelManager chan_manager;
         final EventsProvider chan_manager_events;
-        final long chan_handler;
-        final long router;
-        final long route_handler;
-        final long message_handler;
-        final long peer_manager;
+        final NetGraphMsgHandler router;
+        final PeerManager peer_manager;
         HashMap<String, Long> monitors; // Wow I forgot just how terrible Java is - we can't put a byte array here.
         byte[] node_id;
 
@@ -85,17 +82,13 @@ public class HumanObjectPeerTest {
                     }), new Logger(log_trait), keys.as_KeysInterface(), new UserConfig(), 1);
             this.node_id = chan_manager.get_our_node_id();
             this.chan_manager_events = chan_manager.as_EventsProvider();
-
-            this.chan_handler = bindings.ChannelManager_as_ChannelMessageHandler(chan_manager._test_only_get_ptr());
-            this.router = bindings.NetGraphMsgHandler_new(0, logger);
-            this.route_handler = bindings.NetGraphMsgHandler_as_RoutingMessageHandler(router);
-            this.message_handler = bindings.MessageHandler_new(chan_handler, route_handler);
+            this.router = new NetGraphMsgHandler(null, new Logger(log_trait));
 
             byte[] random_data = new byte[32];
             for (byte i = 0; i < 32; i++) {
                 random_data[i] = (byte) ((i ^ seed) ^ 0xf0);
             }
-            this.peer_manager = bindings.PeerManager_new(message_handler, keys_interface.get_node_secret(), random_data, logger);
+            this.peer_manager = new PeerManager(chan_manager.as_ChannelMessageHandler(), router.as_RoutingMessageHandler(), keys_interface.get_node_secret(), random_data, new Logger(log_trait));
             System.gc();
         }
 
@@ -126,25 +119,28 @@ public class HumanObjectPeerTest {
             bindings.Logger_free(logger);
             bindings.FeeEstimator_free(fee_estimator);
             bindings.BroadcasterInterface_free(tx_broadcaster);
-            bindings.ChannelMessageHandler_free(chan_handler);
-            bindings.NetGraphMsgHandler_free(router);
-            bindings.RoutingMessageHandler_free(route_handler);
-            //MessageHandler was actually moved into the route_handler!: bindings.MessageHandler_free(message_handler);
-            bindings.PeerManager_free(peer_manager);
             synchronized (monitors) {
                 for (Long mon : monitors.values()) {
                     bindings.ChannelMonitor_free(mon);
                 }
             }
         }
+
+        long get_route(byte[] dest_node, long our_chans) {
+            LockedNetworkGraph netgraph = this.router.read_locked_graph();
+            //r = new WeakReference(netgraph);
+            NetworkGraph graph = netgraph.graph();
+            return bindings.get_route(this.node_id, graph._test_only_get_ptr(), dest_node, our_chans,
+                    bindings.LDKCVecTempl_RouteHint_new(new long[0]), 1000, 42, this.logger);
+        }
     }
 
     class LongHolder { long val; }
 
     java.util.LinkedList<WeakReference> must_free_objs = new java.util.LinkedList();
-    void do_read_event(ConcurrentLinkedQueue<Thread> list, long pm, long descriptor, byte[] data) {
+    void do_read_event(ConcurrentLinkedQueue<Thread> list, PeerManager pm, long descriptor, byte[] data) {
         Thread thread = new Thread(() -> {
-            long res = bindings.PeerManager_read_event(pm, descriptor, data);
+            long res = bindings.PeerManager_read_event(pm._test_only_get_ptr(), descriptor, data);
             assert bindings.LDKCResult_boolPeerHandleErrorZ_result_ok(res);
             //assert bindings.deref_bool(bindings.LDKCResult_boolPeerHandleErrorZ_get_inner(res));
             bindings.CResult_boolPeerHandleErrorZ_free(res);
@@ -196,10 +192,10 @@ public class HumanObjectPeerTest {
         };
         descriptor1.val = bindings.LDKSocketDescriptor_new(sock2);
 
-        long init_vec = bindings.PeerManager_new_outbound_connection(peer1.peer_manager, peer2.node_id, descriptor1.val);
+        long init_vec = bindings.PeerManager_new_outbound_connection(peer1.peer_manager._test_only_get_ptr(), peer2.node_id, descriptor1.val);
         assert (bindings.LDKCResult_CVec_u8ZPeerHandleErrorZ_result_ok(init_vec));
 
-        long con_res = bindings.PeerManager_new_inbound_connection(peer2.peer_manager, descriptor2);
+        long con_res = bindings.PeerManager_new_inbound_connection(peer2.peer_manager._test_only_get_ptr(), descriptor2);
         assert (bindings.LDKCResult_NonePeerHandleErrorZ_result_ok(con_res));
         bindings.CResult_NonePeerHandleErrorZ_free(con_res);
         do_read_event(list, peer2.peer_manager, descriptor2, bindings.get_u8_slice_bytes(bindings.LDKCResult_CVec_u8ZPeerHandleErrorZ_get_inner(init_vec)));
@@ -211,9 +207,9 @@ public class HumanObjectPeerTest {
         assert bindings.LDKCResult_NoneAPIErrorZ_result_ok(cc_res);
         bindings.CResult_NoneAPIErrorZ_free(cc_res);
 
-        bindings.PeerManager_process_events(peer1.peer_manager);
+        peer1.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
-        bindings.PeerManager_process_events(peer2.peer_manager);
+        peer2.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
 
         long events = bindings.EventsProvider_get_and_clear_pending_events(peer1.chan_manager_events._test_only_get_ptr());
@@ -235,9 +231,9 @@ public class HumanObjectPeerTest {
         funding.addOutput(Coin.SATOSHI.multiply(10000), new Script(funding_spk));
         peer1.chan_manager.funding_transaction_generated(chan_id, new OutPoint(funding.getTxId().getReversedBytes(), (short) 0));
 
-        bindings.PeerManager_process_events(peer1.peer_manager);
+        peer1.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
-        bindings.PeerManager_process_events(peer2.peer_manager);
+        peer2.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
 
         events = bindings.EventsProvider_get_and_clear_pending_events(peer1.chan_manager_events._test_only_get_ptr());
@@ -257,8 +253,8 @@ public class HumanObjectPeerTest {
             peer2.connect_block(b, null, height);
         }
 
-        bindings.PeerManager_process_events(peer1.peer_manager);
-        bindings.PeerManager_process_events(peer2.peer_manager);
+        peer1.peer_manager.process_events();
+        peer2.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
 
         long peer1_chans = bindings.ChannelManager_list_channels(peer1.chan_manager._test_only_get_ptr());
@@ -276,22 +272,21 @@ public class HumanObjectPeerTest {
         byte[] payment_preimage = new byte[32];
         for (int i = 0; i < 32; i++) payment_preimage[i] = (byte) (i ^ 0x0f);
         byte[] payment_hash = Sha256Hash.hash(payment_preimage);
-        long netgraph = bindings.NetGraphMsgHandler_read_locked_graph(peer1.router);
-        long route = bindings.get_route(peer1.node_id, bindings.LockedNetworkGraph_graph(netgraph), peer2.node_id, peer1_chans,
-                bindings.LDKCVecTempl_RouteHint_new(new long[0]), 1000, 42, peer1.logger);
+        long route = peer1.get_route(peer2.node_id, peer1_chans);
+        System.gc(); // Force the lock to release that we took in get_route - we need something better here!
+        System.runFinalization();
         bindings.CVec_ChannelDetailsZ_free(peer1_chans);
         assert bindings.LDKCResult_RouteLightningErrorZ_result_ok(route);
-        bindings.LockedNetworkGraph_free(netgraph);
         long payment_res = bindings.ChannelManager_send_payment(peer1.chan_manager._test_only_get_ptr(), bindings.LDKCResult_RouteLightningErrorZ_get_inner(route), payment_hash, new byte[32]);
         bindings.CResult_RouteLightningErrorZ_free(route);
         assert bindings.LDKCResult_NonePaymentSendFailureZ_result_ok(payment_res);
         bindings.CResult_NonePaymentSendFailureZ_free(payment_res);
 
-        bindings.PeerManager_process_events(peer1.peer_manager);
+        peer1.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
-        bindings.PeerManager_process_events(peer2.peer_manager);
+        peer2.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
-        bindings.PeerManager_process_events(peer1.peer_manager);
+        peer1.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
 
         long peer2_events = bindings.EventsProvider_get_and_clear_pending_events(peer2.chan_manager_events._test_only_get_ptr());
@@ -310,9 +305,9 @@ public class HumanObjectPeerTest {
         peer2.chan_manager.claim_funds(payment_preimage, new byte[32], ((bindings.LDKEvent.PaymentReceived) payment_recvd).amt);
         bindings.CVec_EventZ_free(peer2_events);
 
-        bindings.PeerManager_process_events(peer2.peer_manager);
+        peer2.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
-        bindings.PeerManager_process_events(peer1.peer_manager);
+        peer1.peer_manager.process_events();
         while (!list.isEmpty()) { list.poll().join(); }
 
         long peer1_events = bindings.EventsProvider_get_and_clear_pending_events(peer1.chan_manager_events._test_only_get_ptr());