Skip to content

Commit d092e64

Browse files
LeviticusMBjoeferner
authored andcommitted
Use a Java trampoline when calling Java code from JNI.
This prevents the "null caller frame" error in recent JDKs. Additionally, if running on JDK 9+, search class hierarchy for a accessible method to invoke. This makes Arrays.asList(...).toArray() work.
1 parent 1c33679 commit d092e64

File tree

11 files changed

+69
-12
lines changed

11 files changed

+69
-12
lines changed

src-java/node/CastingUtils.class

-4 Bytes
Binary file not shown.
1.82 KB
Binary file not shown.

src-java/node/MethodCallBaton.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package node;
2+
3+
import java.lang.reflect.Constructor;
4+
import java.lang.reflect.Method;
5+
6+
public abstract class MethodCallBaton {
7+
public static Object newInstance(Constructor<?> ctor, Object[] args) throws Exception {
8+
return ctor.newInstance(args);
9+
}
10+
11+
public static Object invokeMethod(Method func, Object obj, Object[] args) throws Exception {
12+
func.setAccessible(true);
13+
return func.invoke(obj, args);
14+
}
15+
16+
public static Object invokeMethod9(Method func, Object obj, Object[] args) throws Exception {
17+
if (!func.canAccess(obj)) {
18+
Method accessible = findAccessible(func, obj, func.getDeclaringClass());
19+
20+
if (accessible != null) {
21+
func = accessible;
22+
}
23+
}
24+
25+
return func.invoke(obj, args);
26+
}
27+
28+
private static Method findAccessible(Method func, Object obj, Class<?> clazz) {
29+
Method accessible = null;
30+
31+
if (clazz != null) {
32+
try {
33+
accessible = clazz.getMethod(func.getName(), func.getParameterTypes());
34+
accessible = accessible.canAccess(obj) ? accessible : null;
35+
} catch (Exception ignored) {
36+
accessible = null;
37+
}
38+
39+
if (accessible == null) {
40+
accessible = findAccessible(func, obj, clazz.getSuperclass());
41+
}
42+
43+
if (accessible == null) {
44+
for (Class<?> iface : clazz.getInterfaces()) {
45+
accessible = findAccessible(func, obj, iface);
46+
47+
if (accessible != null) {
48+
break;
49+
}
50+
}
51+
}
52+
}
53+
54+
return accessible;
55+
}
56+
}
-18 Bytes
Binary file not shown.

src-java/node/VarArgs.class

1 Byte
Binary file not shown.

src/methodCallBaton.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,12 @@ v8::Local<v8::Value> MethodCallBaton::resultsToV8(JNIEnv *env) {
119119
}
120120

121121
void NewInstanceBaton::ExecuteInternal(JNIEnv* env) {
122-
jclass constructorClazz = env->FindClass("java/lang/reflect/Constructor");
123-
jmethodID constructor_newInstance = env->GetMethodID(constructorClazz, "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");
124-
125-
//printf("invoke: %s\n", javaMethodCallToString(env, m_method, constructor_newInstance, m_args).c_str());
122+
jclass batonClazz = env->FindClass("node/MethodCallBaton");
123+
jmethodID newInstance = env->GetStaticMethodID(batonClazz, "newInstance", "(Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object;");
126124

127125
jarray args = javaGetArgsForConstructor(env, m_method, m_args);
128-
jobject result = env->CallObjectMethod(m_method, constructor_newInstance, args);
126+
jobject result = env->CallStaticObjectMethod(batonClazz, newInstance, m_method, args);
127+
129128
if(env->ExceptionCheck()) {
130129
jthrowable ex = env->ExceptionOccurred();
131130
env->ExceptionClear();
@@ -138,8 +137,6 @@ void NewInstanceBaton::ExecuteInternal(JNIEnv* env) {
138137
}
139138

140139
void StaticMethodCallBaton::ExecuteInternal(JNIEnv* env) {
141-
jmethodID method_invoke = getMethodInvokeMethodId(env);
142-
143140
/*
144141
printf("calling %s\n", javaObjectToString(env, m_method).c_str());
145142
printf("arguments\n");
@@ -150,8 +147,11 @@ void StaticMethodCallBaton::ExecuteInternal(JNIEnv* env) {
150147
}
151148
*/
152149

150+
jclass batonClazz = env->FindClass("node/MethodCallBaton");
151+
jmethodID invokeMethod = env->GetStaticMethodID(batonClazz, env->GetVersion() >= 0x90000 ? "invokeMethod9" : "invokeMethod", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
152+
153153
jarray args = javaGetArgsForMethod(env, m_method, m_args);
154-
jobject result = env->CallObjectMethod(m_method, method_invoke, NULL, args);
154+
jobject result = env->CallStaticObjectMethod(batonClazz, invokeMethod, m_method, NULL, args);
155155

156156
if(env->ExceptionCheck()) {
157157
jthrowable ex = env->ExceptionOccurred();
@@ -165,18 +165,19 @@ void StaticMethodCallBaton::ExecuteInternal(JNIEnv* env) {
165165
}
166166

167167
void InstanceMethodCallBaton::ExecuteInternal(JNIEnv* env) {
168-
jmethodID method_invoke = getMethodInvokeMethodId(env);
169-
170168
/*
171169
printf("calling %s\n", javaObjectToString(env, m_method).c_str());
172170
printf("arguments\n");
173171
for(int i=0; i<env->GetArrayLength(m_args); i++) {
174172
printf(" %s\n", javaObjectToString(env, env->GetObjectArrayElement((jobjectArray)m_args, i)).c_str());
175173
}
176174
*/
177-
175+
176+
jclass batonClazz = env->FindClass("node/MethodCallBaton");
177+
jmethodID invokeMethod = env->GetStaticMethodID(batonClazz, env->GetVersion() >= 0x90000 ? "invokeMethod9" : "invokeMethod", "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
178+
178179
jarray args = javaGetArgsForMethod(env, m_method, m_args);
179-
jobject result = env->CallObjectMethod(m_method, method_invoke, m_javaObject->getObject(), args);
180+
jobject result = env->CallStaticObjectMethod(batonClazz, invokeMethod, m_method, m_javaObject->getObject(), args);
180181

181182
if(env->ExceptionCheck()) {
182183
jthrowable ex = env->ExceptionOccurred();

test/RunInterface$1.class

-3 Bytes
Binary file not shown.

test/RunInterface.class

-10 Bytes
Binary file not shown.

test/Test$Enum.class

-6 Bytes
Binary file not shown.

test/Test$StaticEnum.class

-6 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)