mirror of
https://github.com/EsotericSoftware/reflectasm.git
synced 2025-03-26 12:04:51 +08:00
This commit is contained in:
parent
78d9fbb214
commit
bf78c3f3dc
9
.classpath
Normal file
9
.classpath
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry excluding="**/.svn/*" kind="src" path="src"/>
|
||||
<classpathentry excluding="**/.svn/*" kind="src" path="test"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="lib/asm-3.2.jar"/>
|
||||
<classpathentry kind="lib" path="build/junit-4.6.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
17
.project
Normal file
17
.project
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>reflectasm</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
BIN
build/junit-4.6.jar
Normal file
BIN
build/junit-4.6.jar
Normal file
Binary file not shown.
BIN
lib/asm-3.2.jar
Normal file
BIN
lib/asm-3.2.jar
Normal file
Binary file not shown.
@ -0,0 +1,8 @@
|
||||
|
||||
package com.esotericsoftware.reflectasm;
|
||||
|
||||
class AccessClassLoader extends ClassLoader {
|
||||
Class<?> defineClass (String name, byte[] bytes) throws ClassFormatError {
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
}
|
230
src/com/esotericsoftware/reflectasm/FieldAccess.java
Normal file
230
src/com/esotericsoftware/reflectasm/FieldAccess.java
Normal file
@ -0,0 +1,230 @@
|
||||
|
||||
package com.esotericsoftware.reflectasm;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
public abstract class FieldAccess {
|
||||
static private AccessClassLoader loader = new AccessClassLoader();
|
||||
|
||||
static public FieldAccess get (Class type) {
|
||||
Field[] fields = type.getFields();
|
||||
String className = type.getName();
|
||||
String accessClassName = className + "FieldAccess";
|
||||
Class accessClass = null;
|
||||
try {
|
||||
accessClass = loader.loadClass(accessClassName);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
if (accessClass == null) {
|
||||
String accessClassNameInternal = accessClassName.replace('.', '/');
|
||||
String classNameInternal = className.replace('.', '/');
|
||||
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, "com/esotericsoftware/reflectasm/FieldAccess",
|
||||
null);
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ILOAD, 2);
|
||||
|
||||
Label[] labels = new Label[fields.length];
|
||||
for (int i = 0, n = fields.length; i < n; i++)
|
||||
labels[i] = new Label();
|
||||
Label defaultLabel = new Label();
|
||||
mv.visitTableSwitchInsn(0, fields.length - 1, defaultLabel, labels);
|
||||
|
||||
for (int i = 0, n = fields.length; i < n; i++) {
|
||||
Field field = fields[i];
|
||||
Type fieldType = Type.getType(field.getType());
|
||||
|
||||
mv.visitLabel(labels[i]);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitTypeInsn(CHECKCAST, classNameInternal);
|
||||
mv.visitVarInsn(ALOAD, 3);
|
||||
|
||||
switch (fieldType.getSort()) {
|
||||
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/Byte");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "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/Short");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
|
||||
break;
|
||||
case Type.INT:
|
||||
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
|
||||
break;
|
||||
case Type.FLOAT:
|
||||
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
|
||||
break;
|
||||
case Type.LONG:
|
||||
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
|
||||
break;
|
||||
case Type.DOUBLE:
|
||||
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
|
||||
break;
|
||||
case Type.ARRAY:
|
||||
mv.visitTypeInsn(CHECKCAST, fieldType.getDescriptor());
|
||||
break;
|
||||
case Type.OBJECT:
|
||||
mv.visitTypeInsn(CHECKCAST, fieldType.getInternalName());
|
||||
break;
|
||||
}
|
||||
|
||||
mv.visitFieldInsn(PUTFIELD, classNameInternal, field.getName(), fieldType.getDescriptor());
|
||||
mv.visitInsn(RETURN);
|
||||
}
|
||||
|
||||
mv.visitLabel(defaultLabel);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn("Field not found: ");
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn(ILOAD, 2);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn(ATHROW);
|
||||
mv.visitMaxs(5, 4);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ILOAD, 2);
|
||||
|
||||
Label[] labels = new Label[fields.length];
|
||||
for (int i = 0, n = fields.length; i < n; i++)
|
||||
labels[i] = new Label();
|
||||
Label defaultLabel = new Label();
|
||||
mv.visitTableSwitchInsn(0, fields.length - 1, defaultLabel, labels);
|
||||
|
||||
for (int i = 0, n = fields.length; i < n; i++) {
|
||||
Field field = fields[i];
|
||||
|
||||
mv.visitLabel(labels[i]);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitTypeInsn(CHECKCAST, classNameInternal);
|
||||
mv.visitFieldInsn(GETFIELD, classNameInternal, field.getName(), Type.getDescriptor(field.getType()));
|
||||
|
||||
Type fieldType = Type.getType(field.getType());
|
||||
switch (fieldType.getSort()) {
|
||||
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;
|
||||
}
|
||||
|
||||
mv.visitInsn(ARETURN);
|
||||
}
|
||||
|
||||
mv.visitLabel(defaultLabel);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn("Field not found: ");
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn(ILOAD, 2);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn(ATHROW);
|
||||
mv.visitMaxs(5, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
byte[] data = cw.toByteArray();
|
||||
accessClass = loader.defineClass(accessClassName, data);
|
||||
}
|
||||
try {
|
||||
FieldAccess access = (FieldAccess)accessClass.newInstance();
|
||||
access.fields = fields;
|
||||
return access;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Error constructing field access class: " + accessClassName, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Field[] fields;
|
||||
|
||||
abstract public void set (Object object, int fieldIndex, Object value);
|
||||
|
||||
abstract public Object get (Object object, int fieldIndex);
|
||||
|
||||
public int getIndex (String fieldName) {
|
||||
for (int i = 0, n = fields.length; i < n; i++) {
|
||||
Field field = fields[i];
|
||||
if (field.getName().equals(fieldName)) return i;
|
||||
}
|
||||
throw new IllegalArgumentException("Unable to find public field: " + fieldName);
|
||||
}
|
||||
|
||||
public void set (Object object, String fieldName, Object value) {
|
||||
set(object, getIndex(fieldName), value);
|
||||
}
|
||||
|
||||
public Object get (Object object, String fieldName) {
|
||||
return get(object, getIndex(fieldName));
|
||||
}
|
||||
}
|
47
test/com/esotericsoftware/reflectasm/FieldAccessTest.java
Normal file
47
test/com/esotericsoftware/reflectasm/FieldAccessTest.java
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
package com.esotericsoftware.reflectasm;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class FieldAccessTest extends TestCase {
|
||||
public void testNameSetAndGet () {
|
||||
FieldAccess access = FieldAccess.get(SomeClass.class);
|
||||
SomeClass someClass = new SomeClass();
|
||||
|
||||
assertEquals(null, someClass.name);
|
||||
access.set(someClass, "name", "first");
|
||||
assertEquals("first", someClass.name);
|
||||
assertEquals("first", access.get(someClass, "name"));
|
||||
|
||||
assertEquals(0, someClass.intValue);
|
||||
access.set(someClass, "intValue", 1234);
|
||||
assertEquals(1234, someClass.intValue);
|
||||
assertEquals(1234, access.get(someClass, "intValue"));
|
||||
}
|
||||
|
||||
public void testIndexSetAndGet () {
|
||||
FieldAccess access = FieldAccess.get(SomeClass.class);
|
||||
SomeClass someClass = new SomeClass();
|
||||
int index;
|
||||
|
||||
assertEquals(null, someClass.name);
|
||||
index = access.getIndex("name");
|
||||
access.set(someClass, index, "first");
|
||||
assertEquals("first", someClass.name);
|
||||
assertEquals("first", access.get(someClass, index));
|
||||
|
||||
index = access.getIndex("intValue");
|
||||
assertEquals(0, someClass.intValue);
|
||||
access.set(someClass, index, 1234);
|
||||
assertEquals(1234, someClass.intValue);
|
||||
assertEquals(1234, access.get(someClass, index));
|
||||
}
|
||||
|
||||
static public class SomeClass {
|
||||
public String name;
|
||||
public int intValue;
|
||||
protected float test1;
|
||||
Float test2;
|
||||
private String test3;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user