Skip to content

Commit 29144f3

Browse files
authored
Merge pull request #1637 from harawata/default-method-jdk9
Avoid illegal reflective access on default method invocation
2 parents 8760383 + 961b002 commit 29144f3

File tree

1 file changed

+40
-18
lines changed

1 file changed

+40
-18
lines changed

src/main/java/org/apache/ibatis/binding/MapperProxy.java

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.io.Serializable;
1919
import java.lang.invoke.MethodHandles;
2020
import java.lang.invoke.MethodHandles.Lookup;
21+
import java.lang.invoke.MethodType;
2122
import java.lang.reflect.Constructor;
2223
import java.lang.reflect.InvocationHandler;
2324
import java.lang.reflect.Method;
@@ -35,7 +36,8 @@ public class MapperProxy<T> implements InvocationHandler, Serializable {
3536
private static final long serialVersionUID = -6424540398559729838L;
3637
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
3738
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
38-
private static Constructor<Lookup> lookupConstructor;
39+
private static final Constructor<Lookup> lookupConstructor;
40+
private static final Method privateLookupInMethod;
3941
private final SqlSession sqlSession;
4042
private final Class<T> mapperInterface;
4143
private final Map<Method, MapperMethod> methodCache;
@@ -47,17 +49,29 @@ public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method,
4749
}
4850

4951
static {
52+
Method privateLookupIn;
5053
try {
51-
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
54+
privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
5255
} catch (NoSuchMethodException e) {
56+
privateLookupIn = null;
57+
}
58+
privateLookupInMethod = privateLookupIn;
59+
60+
Constructor<Lookup> lookup = null;
61+
if (privateLookupInMethod == null) {
62+
// JDK 1.8
5363
try {
54-
// Since Java 14+8
55-
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Class.class, int.class);
56-
} catch (NoSuchMethodException e2) {
57-
throw new IllegalStateException("No known constructor found in java.lang.invoke.MethodHandles.Lookup.", e2);
64+
lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
65+
lookup.setAccessible(true);
66+
} catch (NoSuchMethodException e) {
67+
throw new IllegalStateException(
68+
"There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
69+
e);
70+
} catch (Throwable t) {
71+
lookup = null;
5872
}
5973
}
60-
lookupConstructor.setAccessible(true);
74+
lookupConstructor = lookup;
6175
}
6276

6377
@Override
@@ -66,7 +80,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
6680
if (Object.class.equals(method.getDeclaringClass())) {
6781
return method.invoke(this, args);
6882
} else if (method.isDefault()) {
69-
return invokeDefaultMethod(proxy, method, args);
83+
if (privateLookupInMethod == null) {
84+
return invokeDefaultMethodJava8(proxy, method, args);
85+
} else {
86+
return invokeDefaultMethodJava9(proxy, method, args);
87+
}
7088
}
7189
} catch (Throwable t) {
7290
throw ExceptionUtil.unwrapThrowable(t);
@@ -76,19 +94,23 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
7694
}
7795

7896
private MapperMethod cachedMapperMethod(Method method) {
79-
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
97+
return methodCache.computeIfAbsent(method,
98+
k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
8099
}
81100

82-
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
101+
private Object invokeDefaultMethodJava9(Object proxy, Method method, Object[] args)
83102
throws Throwable {
84103
final Class<?> declaringClass = method.getDeclaringClass();
85-
final Lookup lookup;
86-
if (lookupConstructor.getParameterCount() == 2) {
87-
lookup = lookupConstructor.newInstance(declaringClass, ALLOWED_MODES);
88-
} else {
89-
// SInce JDK 14+8
90-
lookup = lookupConstructor.newInstance(declaringClass, null, ALLOWED_MODES);
91-
}
92-
return lookup.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
104+
return ((Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup()))
105+
.findSpecial(declaringClass, method.getName(),
106+
MethodType.methodType(method.getReturnType(), method.getParameterTypes()), declaringClass)
107+
.bindTo(proxy).invokeWithArguments(args);
108+
}
109+
110+
private Object invokeDefaultMethodJava8(Object proxy, Method method, Object[] args)
111+
throws Throwable {
112+
final Class<?> declaringClass = method.getDeclaringClass();
113+
return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass)
114+
.bindTo(proxy).invokeWithArguments(args);
93115
}
94116
}

0 commit comments

Comments
 (0)