@@ -106,6 +106,7 @@ def __init__(self, DEBUG: bool, target: Target, **kwargs):
106106import org.ldk.enums.*;
107107import org.ldk.util.*;
108108import java.util.Arrays;
109+ import java.lang.ref.Reference;
109110import javax.annotation.Nullable;
110111
111112public class UtilMethods {
@@ -129,6 +130,9 @@ class CommonBase {
129130#include <stdatomic.h>
130131#include <stdlib.h>
131132
133+ #define LIKELY(v) __builtin_expect(!!(v), 1)
134+ #define UNLIKELY(v) __builtin_expect(!!(v), 0)
135+
132136"""
133137
134138 if self .target == Target .ANDROID :
@@ -512,6 +516,7 @@ class CommonBase {
512516import org.ldk.enums.*;
513517import org.ldk.util.*;
514518import java.util.Arrays;
519+ import java.lang.ref.Reference;
515520import javax.annotation.Nullable;
516521
517522"""
@@ -616,8 +621,13 @@ def native_c_unitary_enum_map(self, struct_name, variants, enum_doc_comment):
616621 out_java_enum = "package org.ldk.enums;\n \n "
617622 out_java = ""
618623 out_c = ""
619- out_c = out_c + "static inline LDK" + struct_name + " LDK" + struct_name + "_from_java(" + self .c_fn_args_pfx + ") {\n "
620- out_c = out_c + "\t switch ((*env)->CallIntMethod(env, clz, ordinal_meth)) {\n "
624+ out_c += "static inline LDK" + struct_name + " LDK" + struct_name + "_from_java(" + self .c_fn_args_pfx + ") {\n "
625+ out_c += "\t jint ord = (*env)->CallIntMethod(env, clz, ordinal_meth);\n "
626+ out_c += "\t if (UNLIKELY((*env)->ExceptionCheck(env))) {\n "
627+ out_c += "\t \t (*env)->ExceptionDescribe(env);\n "
628+ out_c += "\t \t (*env)->FatalError(env, \" A call to " + struct_name + ".ordinal() from rust threw an exception.\" );\n "
629+ out_c += "\t }\n "
630+ out_c += "\t switch (ord) {\n "
621631
622632 if enum_doc_comment is not None :
623633 out_java_enum += "/**\n * " + enum_doc_comment .replace ("\n " , "\n * " ) + "\n */\n "
@@ -633,9 +643,10 @@ def native_c_unitary_enum_map(self, struct_name, variants, enum_doc_comment):
633643 out_java_enum = out_java_enum + "\t static { init(); }\n "
634644 out_java_enum = out_java_enum + "}"
635645 out_java = out_java + "\t static { " + struct_name + ".values(); /* Force enum statics to run */ }\n "
636- out_c = out_c + "\t }\n "
637- out_c = out_c + "\t abort();\n "
638- out_c = out_c + "}\n "
646+ out_c += "\t }\n "
647+ out_c += "\t (*env)->FatalError(env, \" A call to " + struct_name + ".ordinal() from rust returned an invalid value.\" );\n "
648+ out_c += "\t abort(); // Unreachable, but will let the compiler know we don't return here\n "
649+ out_c += "}\n "
639650
640651 out_c = out_c + "static jclass " + struct_name + "_class = NULL;\n "
641652 for var , _ in variants :
@@ -672,14 +683,11 @@ def c_complex_enum_pfx(self, struct_name, variants, init_meth_jty_strs):
672683 out_c = out_c + "static jmethodID " + struct_name + "_" + var + "_meth = NULL;\n "
673684 out_c = out_c + self .c_fn_ty_pfx + "void JNICALL Java_org_ldk_impl_bindings_00024" + struct_name .replace ("_" , "_1" ) + "_init (" + self .c_fn_args_pfx + ") {\n "
674685 for var_name in variants :
675- out_c = out_c + "\t " + struct_name + "_" + var_name + "_class =\n "
676- if self .target == Target .ANDROID :
677- out_c = out_c + "\t \t (*env)->NewGlobalRef(env, (*env)->FindClass(env, \" org/ldk/impl/bindings$" + struct_name + "$" + var_name + "\" ));\n "
678- else :
679- out_c = out_c + "\t \t (*env)->NewGlobalRef(env, (*env)->FindClass(env, \" Lorg/ldk/impl/bindings$" + struct_name + "$" + var_name + ";\" ));\n "
680- out_c = out_c + "\t CHECK(" + struct_name + "_" + var_name + "_class != NULL);\n "
681- out_c = out_c + "\t " + struct_name + "_" + var_name + "_meth = (*env)->GetMethodID(env, " + struct_name + "_" + var_name + "_class, \" <init>\" , \" (" + init_meth_jty_strs [var_name ] + ")V\" );\n "
682- out_c = out_c + "\t CHECK(" + struct_name + "_" + var_name + "_meth != NULL);\n "
686+ out_c += "\t " + struct_name + "_" + var_name + "_class =\n "
687+ out_c += "\t \t (*env)->NewGlobalRef(env, (*env)->FindClass(env, \" org/ldk/impl/bindings$" + struct_name + "$" + var_name + "\" ));\n "
688+ out_c += "\t CHECK(" + struct_name + "_" + var_name + "_class != NULL);\n "
689+ out_c += "\t " + struct_name + "_" + var_name + "_meth = (*env)->GetMethodID(env, " + struct_name + "_" + var_name + "_class, \" <init>\" , \" (" + init_meth_jty_strs [var_name ] + ")V\" );\n "
690+ out_c += "\t CHECK(" + struct_name + "_" + var_name + "_meth != NULL);\n "
683691 out_c = out_c + "}\n "
684692 return out_c
685693
@@ -901,7 +909,7 @@ def native_c_map_trait(self, struct_name, field_vars, flattened_field_vars, fiel
901909 out_c = out_c + ", " + arg_info .arg_name
902910 out_c = out_c + ");\n "
903911
904- out_c += "\t if (( *env)->ExceptionCheck(env)) {\n "
912+ out_c += "\t if (UNLIKELY(( *env)->ExceptionCheck(env) )) {\n "
905913 out_c += "\t \t (*env)->ExceptionDescribe(env);\n "
906914 out_c += "\t \t (*env)->FatalError(env, \" A call to " + fn_line .fn_name + " in " + struct_name + " from rust threw an exception.\" );\n "
907915 out_c += "\t }\n "
@@ -1187,6 +1195,7 @@ def map_function(self, argument_types, c_call_string, method_name, meth_n, retur
11871195 out_java += (arg_conv_info .java_ty + " " + arg_conv_info .arg_name )
11881196
11891197 out_java_struct = ""
1198+ extra_java_struct_out = ""
11901199 if not args_known :
11911200 out_java_struct += ("\t // Skipped " + method_name + "\n " )
11921201 else :
@@ -1204,6 +1213,15 @@ def map_function(self, argument_types, c_call_string, method_name, meth_n, retur
12041213 elif meth_n == "clone_ptr" :
12051214 out_java_struct += ("\t " + return_type_info .java_hu_ty + " " + meth_n + "(" )
12061215 else :
1216+ if meth_n == "hash" and return_type_info .java_hu_ty == "long" :
1217+ extra_java_struct_out = "\t @Override public int hashCode() {\n "
1218+ extra_java_struct_out += "\t \t return (int)this.hash();\n "
1219+ extra_java_struct_out += "\t }\n "
1220+ elif meth_n == "eq" and return_type_info .java_hu_ty == "boolean" :
1221+ extra_java_struct_out = "\t @Override public boolean equals(Object o) {\n "
1222+ extra_java_struct_out += "\t \t if (!(o instanceof " + struct_meth + ")) return false;\n "
1223+ extra_java_struct_out += "\t \t return this.eq((" + struct_meth + ")o);\n "
1224+ extra_java_struct_out += "\t }\n "
12071225 out_java_struct += ("\t public " + return_type_info .java_hu_ty + " " + meth_n + "(" )
12081226 for idx , arg in enumerate (argument_types ):
12091227 if idx != 0 :
@@ -1291,6 +1309,45 @@ def map_function(self, argument_types, c_call_string, method_name, meth_n, retur
12911309 else :
12921310 out_java_struct += (info .arg_name )
12931311 out_java_struct += (");\n " )
1312+
1313+ # This is completely nuts. The OpenJDK JRE JIT will optimize out a object which is on
1314+ # the stack, calling its finalizer immediately even if member methods are *actively
1315+ # executing* on the same object, as long as said object is on the stack. There is no
1316+ # concrete specification for when the optimizer is allowed to do this, and when it is
1317+ # not, so there is absolutely no way to be certain that this fix suffices.
1318+ #
1319+ # Instead, the "Java Language Specification" says only that an object is reachable
1320+ # (i.e. will not yet be finalized) if it "can be accessed in any potential continuing
1321+ # computation from any live thread". To any sensible reader this would mean actively
1322+ # executing a member function on an object would make it not eligible for finalization.
1323+ # But, no, dear reader, this statement does not say that. Well, okay, it says that,
1324+ # very explicitly in fact, but those are just, like, words, man.
1325+ #
1326+ # In the seemingly non-normative text further down, a few examples of things the
1327+ # optimizer can do are given, including "if the values in an object's fields are
1328+ # stored in registers[, t]he may then access the registers instead of the object, and
1329+ # never access the object again[, implying] that the object is garbage". This appears
1330+ # to fully contradict both the above statement, the API documentation in java.lang.ref
1331+ # regarding when a reference is "strongly reachable", and basic common sense. There is
1332+ # no concrete set of limitations stated, however, seemingly implying the JIT could
1333+ # decide your code would run faster by simply garbage collecting everything
1334+ # immediately, ensuring your code finishes soon, just by SEGFAULT. Thus, we're really
1335+ # entirely flying blind here. We add some fences and hope that its sufficient, but
1336+ # with no specification to rely on, we cannot be certain of anything.
1337+ #
1338+ # TL;DR: The Java Language "Specification" provides no real guarantees on when an
1339+ # object will be considered available for garbage collection once the JIT kicks in, so
1340+ # we put in some fences and hope to god the JIT doesn't get smarter/more broken.
1341+ for idx , info in enumerate (argument_types ):
1342+ if idx == 0 and takes_self :
1343+ out_java_struct += ("\t \t Reference.reachabilityFence(this);\n " )
1344+ elif info .arg_name in default_constructor_args :
1345+ for explode_idx , explode_arg in enumerate (default_constructor_args [info .arg_name ]):
1346+ expl_arg_name = info .arg_name + "_" + explode_arg .arg_name
1347+ out_java_struct += ("\t \t Reference.reachabilityFence(" + expl_arg_name + ");\n " )
1348+ elif info .c_ty != "void" :
1349+ out_java_struct += ("\t \t Reference.reachabilityFence(" + info .arg_name + ");\n " )
1350+
12941351 if return_type_info .java_ty == "long" and return_type_info .java_hu_ty != "long" :
12951352 out_java_struct += "\t \t if (ret >= 0 && ret <= 4096) { return null; }\n "
12961353
@@ -1326,4 +1383,4 @@ def map_function(self, argument_types, c_call_string, method_name, meth_n, retur
13261383 out_java_struct += ("\t \t return ret;\n " )
13271384 out_java_struct += ("\t }\n \n " )
13281385
1329- return (out_java , out_c , out_java_struct )
1386+ return (out_java , out_c , out_java_struct + extra_java_struct_out )
0 commit comments