Skip to content

Commit

Permalink
use MethodHandles.Lookup IMPL_LOOKUP to support define class after jd…
Browse files Browse the repository at this point in the history
…k 17. #2659
  • Loading branch information
hengyunabc authored and lujiajing1126 committed Feb 1, 2024
1 parent 2cf87ea commit 820fb97
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class ReflectException extends RuntimeException {
private Throwable cause;

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

Expand Down
25 changes: 25 additions & 0 deletions common/src/main/java/com/taobao/arthas/common/ReflectUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -429,6 +431,29 @@ public static Class defineClass(String className, byte[] b, ClassLoader loader,

Class c = null;

// 在 jdk 17之后,需要hack方式来调用 #2659
if (c == null && classLoaderDefineClassMethod != null) {
Lookup implLookup = UnsafeUtils.implLookup();
MethodHandle unreflect = implLookup.unreflect(classLoaderDefineClassMethod);

if (protectionDomain == null) {
protectionDomain = PROTECTION_DOMAIN;
}
try {
c = (Class) unreflect.invoke(loader, className, b, 0, b.length, protectionDomain);
} catch (InvocationTargetException ex) {
throw new ReflectException(ex.getTargetException());
} catch (Throwable ex) {
// Fall through if setAccessible fails with InaccessibleObjectException on JDK
// 9+
// (on the module path and/or with a JVM bootstrapped with
// --illegal-access=deny)
if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) {
throw new ReflectException(ex);
}
}
}

// Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches
if (contextClass != null && contextClass.getClassLoader() == loader && privateLookupInMethod != null
&& lookupDefineClassMethod != null) {
Expand Down
44 changes: 44 additions & 0 deletions common/src/main/java/com/taobao/arthas/common/UnsafeUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.taobao.arthas.common;


import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;

import sun.misc.Unsafe;

/**
*
* @author hengyunabc 2023-09-21
*
*/
public class UnsafeUtils {
public static final Unsafe UNSAFE;
private static MethodHandles.Lookup IMPL_LOOKUP;

static {
Unsafe unsafe = null;
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unsafe = (Unsafe) theUnsafeField.get(null);
} catch (Throwable ignored) {
// ignored
}
UNSAFE = unsafe;
}

public static MethodHandles.Lookup implLookup() {
if (IMPL_LOOKUP == null) {
Class<MethodHandles.Lookup> lookupClass = MethodHandles.Lookup.class;

try {
Field implLookupField = lookupClass.getDeclaredField("IMPL_LOOKUP");
long offset = UNSAFE.staticFieldOffset(implLookupField);
IMPL_LOOKUP = (MethodHandles.Lookup) UNSAFE.getObject(UNSAFE.staticFieldBase(implLookupField), offset);
} catch (Throwable e) {
// ignored
}
}
return IMPL_LOOKUP;
}
}

0 comments on commit 820fb97

Please sign in to comment.