Skip to content

Instantly share code, notes, and snippets.

@dimzon
Created April 5, 2014 21:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dimzon/9998535 to your computer and use it in GitHub Desktop.
Save dimzon/9998535 to your computer and use it in GitHub Desktop.
FastReflection.java
/******************************************************************************
* Inspired by https://github.com/EsotericSoftware/reflectasm
* the main idea is to take away introspection from ReflectASM
*
* You must obtain reflection member (Constructor,Method or Field)
* yourself then pass it to FastReflection.of() method to obtain
* code generated access-object...
*
* Also (since it operate per-member basis) it takes away ugly switches
* into generated bytecode, supports parametrized constructors and
* static fields/members...
*/
package com.esotericsoftware.reflectasm.dimzon;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import java.io.File;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Objects;
import static org.objectweb.asm.Opcodes.*;
@SuppressWarnings("UnusedDeclaration")
public final class FastReflection {
public static void main(String[] args) throws Throwable {
Class ps = System.out.getClass();
java.lang.reflect.Method psMethod = ps.getMethod("println", Object.class);
Method println = of(psMethod);
println.invoke(System.out, "System.out");
println.invoke(System.err, "System.err");
java.lang.reflect.Method nanoTime = System.class.getMethod("nanoTime");
println.invoke(System.out, of(nanoTime).invokeStatic());
println.invoke(System.err, of(nanoTime).invokeStatic());
java.lang.reflect.Constructor<File> fileConstructor = File.class.getConstructor(String.class);
Object o = of(fileConstructor).newInstance("c:\\bla.txt");
System.out.println(o);
java.lang.reflect.Field field = System.class.getField("out");
Object o1 = of(field).get(null);
System.out.println(o1);
}
private static final Object[] EMPTY_VARARGS = new Object[0];
private static final ThreadLocal<MessageDigest> md5 = new ThreadLocal<MessageDigest>() {
@Override
protected MessageDigest initialValue() {
try {
return MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsae) {
throw new RuntimeException(nsae);
}
}
};
private static String memberAccessClassName(Member member) {
byte[] md5Bytes = md5.get().digest(member.toString().getBytes(StandardCharsets.UTF_8));
String sign = member.getDeclaringClass().getName()
+ "__"
+ javax.xml.bind.DatatypeConverter.printHexBinary(md5Bytes);
if (sign.startsWith("java.")) sign = "reflectasm." + sign;
return sign;
}
public static Constructor of(java.lang.reflect.Constructor ctor) {
Objects.requireNonNull(ctor);
Class accessClass;
String accessClassName = memberAccessClassName(ctor);
AccessClassLoader_2 loader = AccessClassLoader_2.get(ctor.getDeclaringClass());
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (loader) {
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored) {
byte[] byteCode = createAccessor(accessClassName, ctor);
accessClass = loader.defineClass(accessClassName, byteCode);
}
}
try {
Constructor m = (Constructor) accessClass.newInstance();
m._member = ctor;
return m;
} catch (Throwable e) {
throw new RuntimeException("Error constructing constructor access class: " + accessClassName, e);
}
}
public static Method of(java.lang.reflect.Method method) {
Objects.requireNonNull(method);
Class accessClass;
String accessClassName = memberAccessClassName(method);
AccessClassLoader_2 loader = AccessClassLoader_2.get(method.getDeclaringClass());
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (loader) {
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored) {
byte[] byteCode = createAccessor(accessClassName, method);
accessClass = loader.defineClass(accessClassName, byteCode);
}
}
try {
Method m = (Method) accessClass.newInstance();
m._member = method;
return m;
} catch (Throwable e) {
throw new RuntimeException("Error constructing method access class: " + accessClassName, e);
}
}
public static Field of(java.lang.reflect.Field field) {
Objects.requireNonNull(field);
Class accessClass;
String accessClassName = memberAccessClassName(field);
AccessClassLoader_2 loader = AccessClassLoader_2.get(field.getDeclaringClass());
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (loader) {
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored) {
byte[] byteCode = createAccessor(accessClassName, field);
accessClass = loader.defineClass(accessClassName, byteCode);
}
}
try {
Field m = (Field) accessClass.newInstance();
m._member = field;
return m;
} catch (Throwable e) {
throw new RuntimeException("Error constructing field access class: " + accessClassName, e);
}
}
private static byte[] createAccessor(String accessClassName, java.lang.reflect.Constructor<?> member) {
String className = member.getDeclaringClass().getName();
String classNameInternal = internalClassName(className);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
injectConstructor(accessClassName, Constructor.class, cw);
{
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "newInstance",
"([Ljava/lang/Object;)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, classNameInternal);
mv.visitInsn(DUP);
StringBuilder buffer = new StringBuilder(128);
buffer.setLength(0);
buffer.append('(');
injectVarArgs(mv, buffer, member.getParameterTypes(), 1);
buffer.append(")V");
mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "<init>", buffer.toString());
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
private static String internalClassName(String classicClassName) {
return classicClassName.replace('.', '/');
}
private static byte[] createAccessor(String accessClassName, java.lang.reflect.Field member) {
String className = member.getDeclaringClass().getName();
String classNameInternal = internalClassName(className);
boolean isStatic = Modifier.isStatic(member.getModifiers());
Class<?> memberType = member.getType();
Type fieldType = Type.getType(memberType);
int fieldTypeSort = fieldType.getSort();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
injectConstructor(accessClassName, Field.class, cw);
MethodVisitor mv;
{
mv = cw.visitMethod(ACC_PUBLIC, "get",
"(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
mv.visitCode();
if (!isStatic) {
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
}
mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, classNameInternal, member.getName(), Type.getDescriptor(memberType));
injectBoxing(mv, fieldTypeSort);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "set",
"(Ljava/lang/Object;Ljava/lang/Object;)V", null, null);
mv.visitCode();
if (!isStatic) {
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
}
mv.visitVarInsn(ALOAD, 2);
injectUnBoxing(mv, fieldType);
mv.visitFieldInsn(isStatic ? PUTSTATIC : PUTFIELD, classNameInternal, member.getName(), fieldType.getDescriptor());
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
switch (fieldTypeSort) {
case Type.BOOLEAN:
injectPrimitiveGetterSetter("Boolean", IRETURN, ILOAD,
fieldType, fieldTypeSort, cw, member, isStatic, classNameInternal);
break;
case Type.BYTE:
injectPrimitiveGetterSetter("Byte", IRETURN, ILOAD,
fieldType, fieldTypeSort, cw, member, isStatic, classNameInternal);
break;
case Type.CHAR:
injectPrimitiveGetterSetter("Char", IRETURN, ILOAD,
fieldType, fieldTypeSort, cw, member, isStatic, classNameInternal);
break;
case Type.SHORT:
injectPrimitiveGetterSetter("Short", IRETURN, ILOAD,
fieldType, fieldTypeSort, cw, member, isStatic, classNameInternal);
break;
case Type.INT:
injectPrimitiveGetterSetter("Int", IRETURN, ILOAD,
fieldType, fieldTypeSort, cw, member, isStatic, classNameInternal);
break;
case Type.FLOAT:
injectPrimitiveGetterSetter("Float", FRETURN, FLOAD,
fieldType, fieldTypeSort, cw, member, isStatic, classNameInternal);
break;
case Type.LONG:
injectPrimitiveGetterSetter("Long", LRETURN, LLOAD,
fieldType, fieldTypeSort, cw, member, isStatic, classNameInternal);
break;
case Type.DOUBLE:
injectPrimitiveGetterSetter("Double", DRETURN, DLOAD,
fieldType, fieldTypeSort, cw, member, isStatic, classNameInternal);
break;
}
cw.visitEnd();
return cw.toByteArray();
}
private static void injectPrimitiveGetterSetter(String name, int returnValueInstruction, int loadValueInstruction,
Type fieldType, int fieldTypeSort, ClassWriter cw,
java.lang.reflect.Field field,
boolean isStatic, String classNameInternal) {
final String typeNameInternal = fieldType.getDescriptor();
MethodVisitor mv;
{
mv = cw.visitMethod(ACC_PUBLIC, "get" + name, "(Ljava/lang/Object;)" + typeNameInternal, null, null);
mv.visitCode();
if (!isStatic) {
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
}
mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, classNameInternal, field.getName(), typeNameInternal);
mv.visitInsn(returnValueInstruction);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "set" + name, "(Ljava/lang/Object;" + typeNameInternal + ")V", null, null);
mv.visitCode();
if (!isStatic) {
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
}
mv.visitVarInsn(loadValueInstruction,2);
mv.visitFieldInsn(isStatic ? PUTSTATIC : PUTFIELD, classNameInternal, field.getName(), typeNameInternal);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
private static byte[] createAccessor(String accessClassName, java.lang.reflect.Method member) {
String className = member.getDeclaringClass().getName();
String classNameInternal = internalClassName(className);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
injectConstructor(accessClassName, Method.class, cw);
{
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "invoke",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", null, null);
mv.visitCode();
boolean isStatic = Modifier.isStatic(member.getModifiers());
if (!isStatic) {
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
}
StringBuilder buffer = new StringBuilder(128);
buffer.setLength(0);
buffer.append('(');
Class[] paramTypes = member.getParameterTypes();
injectVarArgs(mv, buffer, paramTypes, 2);
buffer.append(')');
buffer.append(Type.getDescriptor(member.getReturnType()));
mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, classNameInternal, member.getName(), buffer.toString());
int retValType = Type.getType(member.getReturnType()).getSort();
injectBoxing(mv, retValType);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
private static void injectBoxing(MethodVisitor mv, int retValType) {
switch (retValType) {
case Type.VOID:
mv.visitInsn(ACONST_NULL);
break;
case Type.BOOLEAN:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
break;
case Type.BYTE:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
break;
case Type.CHAR:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
break;
case Type.SHORT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
break;
case Type.INT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
break;
case Type.FLOAT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
break;
case Type.LONG:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
break;
case Type.DOUBLE:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
break;
}
}
private static void injectVarArgs(MethodVisitor mv, StringBuilder buffer, Class[] paramTypes, int varArgsPosition) {
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
mv.visitVarInsn(ALOAD, varArgsPosition);
mv.visitIntInsn(BIPUSH, paramIndex);
mv.visitInsn(AALOAD);
Type paramType = Type.getType(paramTypes[paramIndex]);
injectUnBoxing(mv, paramType);
buffer.append(paramType.getDescriptor());
}
}
private static void injectUnBoxing(MethodVisitor mv, Type paramType) {
int paramTypeSort = paramType.getSort();
switch (paramTypeSort) {
case Type.BOOLEAN:
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
break;
case Type.BYTE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "byteValue", "()B");
break;
case Type.CHAR:
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
break;
case Type.SHORT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "shortValue", "()S");
break;
case Type.INT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "intValue", "()I");
break;
case Type.FLOAT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "floatValue", "()F");
break;
case Type.LONG:
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "longValue", "()J");
break;
case Type.DOUBLE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Number", "doubleValue", "()D");
break;
case Type.ARRAY:
mv.visitTypeInsn(CHECKCAST, paramType.getDescriptor());
break;
case Type.OBJECT:
mv.visitTypeInsn(CHECKCAST, paramType.getInternalName());
break;
}
}
private static void injectConstructor(String accessClassName, Class<?> baseClass, ClassWriter cw) {
String accessClassNameInternal = internalClassName(accessClassName);
String baseClassNameInternal = internalClassName(baseClass.getName());
cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, baseClassNameInternal,
null);
{
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, baseClassNameInternal, "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
public static abstract class Constructor {
private java.lang.reflect.Constructor<?> _member;
public java.lang.reflect.Constructor<?> member() {
return _member;
}
public final Object newInstance() {
return newInstance(EMPTY_VARARGS);
}
public abstract Object newInstance(Object... args);
@Override
public String toString() {
return member().toString();
}
}
public static abstract class Method {
private java.lang.reflect.Method _member;
public java.lang.reflect.Method member() {
return _member;
}
public final Object invokeStatic() {
return invoke(null, EMPTY_VARARGS);
}
public final Object invokeStatic(Object... args) {
return invoke(null, args);
}
public final Object invoke(Object instance) {
return invoke(instance, EMPTY_VARARGS);
}
public abstract Object invoke(Object instance, Object... args);
@Override
public String toString() {
return member().toString();
}
}
public static abstract class Field {
abstract public void set(Object instance, Object value);
public void setBoolean(Object instance, boolean value) {
throw new UnsupportedOperationException();
}
public void setByte(Object instance, byte value) {
throw new UnsupportedOperationException();
}
public void setShort(Object instance, short value) {
throw new UnsupportedOperationException();
}
public void setInt(Object instance, int value) {
throw new UnsupportedOperationException();
}
public void setLong(Object instance, long value) {
throw new UnsupportedOperationException();
}
public void setDouble(Object instance, double value) {
throw new UnsupportedOperationException();
}
public void setFloat(Object instance, float value) {
throw new UnsupportedOperationException();
}
public void setChar(Object instance, char value) {
throw new UnsupportedOperationException();
}
abstract public Object get(Object instance);
public char getChar(Object instance) {
throw new UnsupportedOperationException();
}
public boolean getBoolean(Object instance) {
throw new UnsupportedOperationException();
}
public byte getByte(Object instance) {
throw new UnsupportedOperationException();
}
public short getShort(Object instance) {
throw new UnsupportedOperationException();
}
public int getInt(Object instance) {
throw new UnsupportedOperationException();
}
public long getLong(Object instance) {
throw new UnsupportedOperationException();
}
public double getDouble(Object instance) {
throw new UnsupportedOperationException();
}
public float getFloat(Object instance) {
throw new UnsupportedOperationException();
}
private java.lang.reflect.Field _member;
public java.lang.reflect.Field member() {
return _member;
}
@Override
public String toString() {
return member().toString();
}
}
/*************************************************************
* taken from https://github.com/EsotericSoftware/reflectasm
*/
@SuppressWarnings("unchecked")
private static class AccessClassLoader_2 extends ClassLoader {
static private final ArrayList<AccessClassLoader_2> accessClassLoaders = new ArrayList();
static AccessClassLoader_2 get(Class type) {
ClassLoader parent = type.getClassLoader();
synchronized (accessClassLoaders) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = accessClassLoaders.size(); i < n; i++) {
AccessClassLoader_2 accessClassLoader = accessClassLoaders.get(i);
if (accessClassLoader.getParent() == parent) return accessClassLoader;
}
AccessClassLoader_2 accessClassLoader = new AccessClassLoader_2(parent);
accessClassLoaders.add(accessClassLoader);
return accessClassLoader;
}
}
private AccessClassLoader_2(ClassLoader parent) {
super(parent);
}
private static final Class[] wellKnownClasses = new Class[]{
FastReflection.class,
FastReflection.Constructor.class,
FastReflection.Method.class,
FastReflection.Field.class
};
protected synchronized java.lang.Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// These classes come from the classloader that loaded AccessClassLoader_2.
for (Class wellKnownClass : wellKnownClasses) {
if (name.equals(wellKnownClass.getName())) return wellKnownClass;
}
// All other classes come from the classloader that loaded the type we are accessing.
return super.loadClass(name, resolve);
}
Class<?> defineClass(String name, byte[] bytes) throws ClassFormatError {
try {
// Attempt to load the access class in the same loader, which makes protected and default access members accessible.
java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class,
int.class});
method.setAccessible(true);
//noinspection RedundantArrayCreation,UnnecessaryBoxing
return (Class) method.invoke(getParent(), new Object[]{name, bytes, Integer.valueOf(0), Integer.valueOf(bytes.length)});
} catch (Exception ignored) {
}
return defineClass(name, bytes, 0, bytes.length);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment