com.taobao.arthas.common.ReflectUtils Maven / Gradle / Ivy
The newest version!
package com.taobao.arthas.common;
import java.beans.BeanInfo;
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;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* from spring
* @version $Id: ReflectUtils.java,v 1.30 2009/01/11 19:47:49 herbyderby Exp $
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ReflectUtils {
private ReflectUtils() {
}
private static final Map primitives = new HashMap(8);
private static final Map transforms = new HashMap(8);
private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader();
// SPRING PATCH BEGIN
private static final Method privateLookupInMethod;
private static final Method lookupDefineClassMethod;
private static final Method classLoaderDefineClassMethod;
private static final ProtectionDomain PROTECTION_DOMAIN;
private static final Throwable THROWABLE;
private static final List OBJECT_METHODS = new ArrayList();
static {
Method privateLookupIn;
Method lookupDefineClass;
Method classLoaderDefineClass;
ProtectionDomain protectionDomain;
Throwable throwable = null;
try {
privateLookupIn = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
try {
return MethodHandles.class.getMethod("privateLookupIn", Class.class,
MethodHandles.Lookup.class);
} catch (NoSuchMethodException ex) {
return null;
}
}
});
lookupDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
try {
return MethodHandles.Lookup.class.getMethod("defineClass", byte[].class);
} catch (NoSuchMethodException ex) {
return null;
}
}
});
classLoaderDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE,
Integer.TYPE, ProtectionDomain.class);
}
});
protectionDomain = getProtectionDomain(ReflectUtils.class);
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
Method[] methods = Object.class.getDeclaredMethods();
for (Method method : methods) {
if ("finalize".equals(method.getName())
|| (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) {
continue;
}
OBJECT_METHODS.add(method);
}
return null;
}
});
} catch (Throwable t) {
privateLookupIn = null;
lookupDefineClass = null;
classLoaderDefineClass = null;
protectionDomain = null;
throwable = t;
}
privateLookupInMethod = privateLookupIn;
lookupDefineClassMethod = lookupDefineClass;
classLoaderDefineClassMethod = classLoaderDefineClass;
PROTECTION_DOMAIN = protectionDomain;
THROWABLE = throwable;
}
// SPRING PATCH END
private static final String[] CGLIB_PACKAGES = { "java.lang", };
static {
primitives.put("byte", Byte.TYPE);
primitives.put("char", Character.TYPE);
primitives.put("double", Double.TYPE);
primitives.put("float", Float.TYPE);
primitives.put("int", Integer.TYPE);
primitives.put("long", Long.TYPE);
primitives.put("short", Short.TYPE);
primitives.put("boolean", Boolean.TYPE);
transforms.put("byte", "B");
transforms.put("char", "C");
transforms.put("double", "D");
transforms.put("float", "F");
transforms.put("int", "I");
transforms.put("long", "J");
transforms.put("short", "S");
transforms.put("boolean", "Z");
}
public static ProtectionDomain getProtectionDomain(final Class source) {
if (source == null) {
return null;
}
return (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return source.getProtectionDomain();
}
});
}
public static Constructor findConstructor(String desc) {
return findConstructor(desc, defaultLoader);
}
public static Constructor findConstructor(String desc, ClassLoader loader) {
try {
int lparen = desc.indexOf('(');
String className = desc.substring(0, lparen).trim();
return getClass(className, loader).getConstructor(parseTypes(desc, loader));
} catch (ClassNotFoundException ex) {
throw new ReflectException(ex);
} catch (NoSuchMethodException ex) {
throw new ReflectException(ex);
}
}
public static Method findMethod(String desc) {
return findMethod(desc, defaultLoader);
}
public static Method findMethod(String desc, ClassLoader loader) {
try {
int lparen = desc.indexOf('(');
int dot = desc.lastIndexOf('.', lparen);
String className = desc.substring(0, dot).trim();
String methodName = desc.substring(dot + 1, lparen).trim();
return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader));
} catch (ClassNotFoundException ex) {
throw new ReflectException(ex);
} catch (NoSuchMethodException ex) {
throw new ReflectException(ex);
}
}
private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException {
int lparen = desc.indexOf('(');
int rparen = desc.indexOf(')', lparen);
List params = new ArrayList();
int start = lparen + 1;
for (;;) {
int comma = desc.indexOf(',', start);
if (comma < 0) {
break;
}
params.add(desc.substring(start, comma).trim());
start = comma + 1;
}
if (start < rparen) {
params.add(desc.substring(start, rparen).trim());
}
Class[] types = new Class[params.size()];
for (int i = 0; i < types.length; i++) {
types[i] = getClass((String) params.get(i), loader);
}
return types;
}
private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException {
return getClass(className, loader, CGLIB_PACKAGES);
}
private static Class getClass(String className, ClassLoader loader, String[] packages)
throws ClassNotFoundException {
String save = className;
int dimensions = 0;
int index = 0;
while ((index = className.indexOf("[]", index) + 1) > 0) {
dimensions++;
}
StringBuilder brackets = new StringBuilder(className.length() - dimensions);
for (int i = 0; i < dimensions; i++) {
brackets.append('[');
}
className = className.substring(0, className.length() - 2 * dimensions);
String prefix = (dimensions > 0) ? brackets + "L" : "";
String suffix = (dimensions > 0) ? ";" : "";
try {
return Class.forName(prefix + className + suffix, false, loader);
} catch (ClassNotFoundException ignore) {
}
for (int i = 0; i < packages.length; i++) {
try {
return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader);
} catch (ClassNotFoundException ignore) {
}
}
if (dimensions == 0) {
Class c = (Class) primitives.get(className);
if (c != null) {
return c;
}
} else {
String transform = (String) transforms.get(className);
if (transform != null) {
try {
return Class.forName(brackets + transform, false, loader);
} catch (ClassNotFoundException ignore) {
}
}
}
throw new ClassNotFoundException(save);
}
public static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
public static Object newInstance(Class type) {
return newInstance(type, EMPTY_CLASS_ARRAY, null);
}
public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {
return newInstance(getConstructor(type, parameterTypes), args);
}
public static Object newInstance(final Constructor cstruct, final Object[] args) {
boolean flag = cstruct.isAccessible();
try {
if (!flag) {
cstruct.setAccessible(true);
}
Object result = cstruct.newInstance(args);
return result;
} catch (InstantiationException e) {
throw new ReflectException(e);
} catch (IllegalAccessException e) {
throw new ReflectException(e);
} catch (InvocationTargetException e) {
throw new ReflectException(e.getTargetException());
} finally {
if (!flag) {
cstruct.setAccessible(flag);
}
}
}
public static Constructor getConstructor(Class type, Class[] parameterTypes) {
try {
Constructor constructor = type.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
return constructor;
} catch (NoSuchMethodException e) {
throw new ReflectException(e);
}
}
public static String[] getNames(Class[] classes) {
if (classes == null)
return null;
String[] names = new String[classes.length];
for (int i = 0; i < names.length; i++) {
names[i] = classes[i].getName();
}
return names;
}
public static Class[] getClasses(Object[] objects) {
Class[] classes = new Class[objects.length];
for (int i = 0; i < objects.length; i++) {
classes[i] = objects[i].getClass();
}
return classes;
}
public static Method findNewInstance(Class iface) {
Method m = findInterfaceMethod(iface);
if (!m.getName().equals("newInstance")) {
throw new IllegalArgumentException(iface + " missing newInstance method");
}
return m;
}
public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) {
Set methods = new HashSet();
for (int i = 0; i < properties.length; i++) {
PropertyDescriptor pd = properties[i];
if (read) {
methods.add(pd.getReadMethod());
}
if (write) {
methods.add(pd.getWriteMethod());
}
}
methods.remove(null);
return (Method[]) methods.toArray(new Method[methods.size()]);
}
public static PropertyDescriptor[] getBeanProperties(Class type) {
return getPropertiesHelper(type, true, true);
}
public static PropertyDescriptor[] getBeanGetters(Class type) {
return getPropertiesHelper(type, true, false);
}
public static PropertyDescriptor[] getBeanSetters(Class type) {
return getPropertiesHelper(type, false, true);
}
private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) {
try {
BeanInfo info = Introspector.getBeanInfo(type, Object.class);
PropertyDescriptor[] all = info.getPropertyDescriptors();
if (read && write) {
return all;
}
List properties = new ArrayList(all.length);
for (int i = 0; i < all.length; i++) {
PropertyDescriptor pd = all[i];
if ((read && pd.getReadMethod() != null) || (write && pd.getWriteMethod() != null)) {
properties.add(pd);
}
}
return (PropertyDescriptor[]) properties.toArray(new PropertyDescriptor[properties.size()]);
} catch (IntrospectionException e) {
throw new ReflectException(e);
}
}
public static Method findDeclaredMethod(final Class type, final String methodName, final Class[] parameterTypes)
throws NoSuchMethodException {
Class cl = type;
while (cl != null) {
try {
return cl.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
cl = cl.getSuperclass();
}
}
throw new NoSuchMethodException(methodName);
}
public static List addAllMethods(final Class type, final List list) {
if (type == Object.class) {
list.addAll(OBJECT_METHODS);
} else
list.addAll(java.util.Arrays.asList(type.getDeclaredMethods()));
Class superclass = type.getSuperclass();
if (superclass != null) {
addAllMethods(superclass, list);
}
Class[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
addAllMethods(interfaces[i], list);
}
return list;
}
public static List addAllInterfaces(Class type, List list) {
Class superclass = type.getSuperclass();
if (superclass != null) {
list.addAll(Arrays.asList(type.getInterfaces()));
addAllInterfaces(superclass, list);
}
return list;
}
public static Method findInterfaceMethod(Class iface) {
if (!iface.isInterface()) {
throw new IllegalArgumentException(iface + " is not an interface");
}
Method[] methods = iface.getDeclaredMethods();
if (methods.length != 1) {
throw new IllegalArgumentException("expecting exactly 1 method in " + iface);
}
return methods[0];
}
// SPRING PATCH BEGIN
public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception {
return defineClass(className, b, loader, null, null);
}
public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain)
throws Exception {
return defineClass(className, b, loader, protectionDomain, null);
}
public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain,
Class> contextClass) throws Exception {
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) {
try {
MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupInMethod.invoke(null, contextClass,
MethodHandles.lookup());
c = (Class) lookupDefineClassMethod.invoke(lookup, b);
} catch (InvocationTargetException ex) {
Throwable target = ex.getTargetException();
if (target.getClass() != LinkageError.class && target.getClass() != IllegalArgumentException.class) {
throw new ReflectException(target);
}
// in case of plain LinkageError (class already defined)
// or IllegalArgumentException (class in different package):
// fall through to traditional ClassLoader.defineClass below
} catch (Throwable ex) {
throw new ReflectException(ex);
}
}
// Classic option: protected ClassLoader.defineClass method
if (c == null && classLoaderDefineClassMethod != null) {
if (protectionDomain == null) {
protectionDomain = PROTECTION_DOMAIN;
}
Object[] args = new Object[] { className, b, 0, b.length, protectionDomain };
try {
if (!classLoaderDefineClassMethod.isAccessible()) {
classLoaderDefineClassMethod.setAccessible(true);
}
c = (Class) classLoaderDefineClassMethod.invoke(loader, args);
} 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);
}
}
}
// Fallback option: JDK 9+ Lookup.defineClass API even if ClassLoader does not
// match
if (c == null && contextClass != null && contextClass.getClassLoader() != loader
&& privateLookupInMethod != null && lookupDefineClassMethod != null) {
try {
MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupInMethod.invoke(null, contextClass,
MethodHandles.lookup());
c = (Class) lookupDefineClassMethod.invoke(lookup, b);
} catch (InvocationTargetException ex) {
throw new ReflectException(ex.getTargetException());
} catch (Throwable ex) {
throw new ReflectException(ex);
}
}
// No defineClass variant available at all?
if (c == null) {
throw new ReflectException(THROWABLE);
}
// Force static initializers to run.
Class.forName(className, true, loader);
return c;
}
// SPRING PATCH END
public static int findPackageProtected(Class[] classes) {
for (int i = 0; i < classes.length; i++) {
if (!Modifier.isPublic(classes[i].getModifiers())) {
return i;
}
}
return 0;
}
}