Skip to content

Commit 82ab95c

Browse files
committed
use MethodHandles.Lookup IMPL_LOOKUP to support define class after jdk 17. alibaba#2659
1 parent 6a43273 commit 82ab95c

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

common/src/main/java/com/taobao/arthas/common/ReflectException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public class ReflectException extends RuntimeException {
66
private Throwable cause;
77

88
public ReflectException(Throwable cause) {
9-
super(cause.getClass().getName() + "-->" + cause.getMessage());
9+
super(cause != null ? cause.getClass().getName() + "-->" + cause.getMessage() : "");
1010
this.cause = cause;
1111
}
1212

common/src/main/java/com/taobao/arthas/common/ReflectUtils.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import java.beans.IntrospectionException;
44
import java.beans.Introspector;
55
import java.beans.PropertyDescriptor;
6+
import java.lang.invoke.MethodHandle;
67
import java.lang.invoke.MethodHandles;
8+
import java.lang.invoke.MethodHandles.Lookup;
79
import java.lang.reflect.Constructor;
810
import java.lang.reflect.InvocationTargetException;
911
import java.lang.reflect.Method;
@@ -429,6 +431,29 @@ public static Class defineClass(String className, byte[] b, ClassLoader loader,
429431

430432
Class c = null;
431433

434+
// 在 jdk 17之后,需要hack方式来调用 #2659
435+
if (c == null && classLoaderDefineClassMethod != null) {
436+
Lookup implLookup = UnsafeUtils.implLookup();
437+
MethodHandle unreflect = implLookup.unreflect(classLoaderDefineClassMethod);
438+
439+
if (protectionDomain == null) {
440+
protectionDomain = PROTECTION_DOMAIN;
441+
}
442+
try {
443+
c = (Class) unreflect.invoke(loader, className, b, 0, b.length, protectionDomain);
444+
} catch (InvocationTargetException ex) {
445+
throw new ReflectException(ex.getTargetException());
446+
} catch (Throwable ex) {
447+
// Fall through if setAccessible fails with InaccessibleObjectException on JDK
448+
// 9+
449+
// (on the module path and/or with a JVM bootstrapped with
450+
// --illegal-access=deny)
451+
if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) {
452+
throw new ReflectException(ex);
453+
}
454+
}
455+
}
456+
432457
// Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches
433458
if (contextClass != null && contextClass.getClassLoader() == loader && privateLookupInMethod != null
434459
&& lookupDefineClassMethod != null) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.taobao.arthas.common;
2+
3+
4+
import java.lang.invoke.MethodHandles;
5+
import java.lang.reflect.Field;
6+
7+
import sun.misc.Unsafe;
8+
9+
/**
10+
*
11+
* @author hengyunabc 2023-09-21
12+
*
13+
*/
14+
public class UnsafeUtils {
15+
public static final Unsafe UNSAFE;
16+
private static MethodHandles.Lookup IMPL_LOOKUP;
17+
18+
static {
19+
Unsafe unsafe = null;
20+
try {
21+
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
22+
theUnsafeField.setAccessible(true);
23+
unsafe = (Unsafe) theUnsafeField.get(null);
24+
} catch (Throwable ignored) {
25+
// ignored
26+
}
27+
UNSAFE = unsafe;
28+
}
29+
30+
public static MethodHandles.Lookup implLookup() {
31+
if (IMPL_LOOKUP == null) {
32+
Class<MethodHandles.Lookup> lookupClass = MethodHandles.Lookup.class;
33+
34+
try {
35+
Field implLookupField = lookupClass.getDeclaredField("IMPL_LOOKUP");
36+
long offset = UNSAFE.staticFieldOffset(implLookupField);
37+
IMPL_LOOKUP = (MethodHandles.Lookup) UNSAFE.getObject(UNSAFE.staticFieldBase(implLookupField), offset);
38+
} catch (Throwable e) {
39+
// ignored
40+
}
41+
}
42+
return IMPL_LOOKUP;
43+
}
44+
}

0 commit comments

Comments
 (0)