/*
* Copyright 2013 Christopher Pheby
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jadira.reflection.access.asm;
import static org.objectweb.asm.Opcodes.AALOAD;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ACC_VARARGS;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.F_APPEND;
import static org.objectweb.asm.Opcodes.F_SAME;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_7;
import static org.objectweb.asm.Type.BOOLEAN;
import static org.objectweb.asm.Type.BYTE;
import static org.objectweb.asm.Type.CHAR;
import static org.objectweb.asm.Type.DOUBLE;
import static org.objectweb.asm.Type.FLOAT;
import static org.objectweb.asm.Type.INT;
import static org.objectweb.asm.Type.LONG;
import static org.objectweb.asm.Type.SHORT;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import org.jadira.reflection.access.AbstractClassAccess;
import org.jadira.reflection.access.api.ClassAccess;
import org.jadira.reflection.access.api.FieldAccess;
import org.jadira.reflection.access.api.MethodAccess;
import org.jadira.reflection.access.classloader.AccessClassLoader;
import org.jadira.reflection.access.portable.PortableFieldAccess;
import org.jadira.reflection.core.misc.ClassUtils;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
/**
* ClassAccess implementation which uses ASM to generate accessors
* @param <C> The Class to be accessed
*/
public abstract class AsmClassAccess<C> extends AbstractClassAccess<C> implements ClassAccess<C> {
private static final ConcurrentHashMap<Class<?>, AsmClassAccess<?>> CLASS_ACCESSES = new ConcurrentHashMap<Class<?>, AsmClassAccess<?>>();
private static final String CLASS_ACCESS_NM = ClassAccess.class.getName().replace('.', '/');
private static final String ASM_CLASS_ACCESS_NM = AsmClassAccess.class.getName().replace('.', '/');
private boolean isNonStaticMemberClass;
/**
* Constructor, intended for use by generated subclasses
* @param clazz The Class to be accessed
*/
protected AsmClassAccess(Class<C> clazz) {
super(clazz);
}
/**
* Indicates if the class being accessed is a non-static member class
* @return True if the class is a non-static member class
*/
public boolean isNonStaticMemberClass() {
return isNonStaticMemberClass;
}
/**
* Get a new instance that can access the given Class. If the ClassAccess for this class
* has not been obtained before, then the specific AsmClassAccess is created by generating
* a specialised subclass of this class and returning it.
* @param clazz Class to be accessed
* @param <C> The type of class
* @return New AsmClassAccess instance
*/
public static <C> AsmClassAccess<C> get(Class<C> clazz) {
@SuppressWarnings("unchecked")
AsmClassAccess<C> access = (AsmClassAccess<C>) CLASS_ACCESSES.get(clazz);
if (access != null) {
return access;
}
Class<?> enclosingType = clazz.getEnclosingClass();
final boolean isNonStaticMemberClass = determineNonStaticMemberClass(clazz, enclosingType);
String clazzName = clazz.getName();
Field[] fields = ClassUtils.collectInstanceFields(clazz, false, false, true);
Method[] methods = ClassUtils.collectMethods(clazz);
String accessClassName = constructAccessClassName(clazzName);
Class<?> accessClass = null;
AccessClassLoader loader = AccessClassLoader.get(clazz);
synchronized (loader) {
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored) {
String accessClassNm = accessClassName.replace('.', '/');
String clazzNm = clazzName.replace('.', '/');
String enclosingClassNm = determineEnclosingClassNm(clazz, enclosingType, isNonStaticMemberClass);
String signatureString = "L" + ASM_CLASS_ACCESS_NM + "<L" + clazzNm + ";>;L" + CLASS_ACCESS_NM + "<L" + clazzNm + ";>;";
ClassWriter cw = new ClassWriter(0);
// TraceClassVisitor tcv = new TraceClassVisitor(cv, new PrintWriter(System.err));
// CheckClassAdapter cw = new CheckClassAdapter(tcv);
cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, accessClassNm, signatureString, ASM_CLASS_ACCESS_NM, null);
enhanceForConstructor(cw, accessClassNm, clazzNm);
if (isNonStaticMemberClass) {
enhanceForNewInstanceInner(cw, clazzNm, enclosingClassNm);
} else {
enhanceForNewInstance(cw, clazzNm);
}
enhanceForGetValueObject(cw, accessClassNm, clazzNm, fields);
enhanceForPutValueObject(cw, accessClassNm, clazzNm, fields);
enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.BOOLEAN_TYPE);
enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.BOOLEAN_TYPE);
enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.BYTE_TYPE);
enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.BYTE_TYPE);
enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.SHORT_TYPE);
enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.SHORT_TYPE);
enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.INT_TYPE);
enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.INT_TYPE);
enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.LONG_TYPE);
enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.LONG_TYPE);
enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.DOUBLE_TYPE);
enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.DOUBLE_TYPE);
enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.FLOAT_TYPE);
enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.FLOAT_TYPE);
enhanceForGetValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.CHAR_TYPE);
enhanceForPutValuePrimitive(cw, accessClassNm, clazzNm, fields, Type.CHAR_TYPE);
enhanceForInvokeMethod(cw, accessClassNm, clazzNm, methods);
cw.visitEnd();
loader.registerClass(accessClassName, cw.toByteArray());
try {
accessClass = loader.findClass(accessClassName);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("AccessClass unexpectedly could not be found", e);
}
}
}
try {
@SuppressWarnings("unchecked")
Constructor<AsmClassAccess<C>> c = (Constructor<AsmClassAccess<C>>) accessClass.getConstructor(new Class[] { Class.class });
access = c.newInstance(clazz);
access.isNonStaticMemberClass = isNonStaticMemberClass;
CLASS_ACCESSES.putIfAbsent(clazz, access);
return access;
} catch (Exception ex) {
throw new RuntimeException("Error constructing constructor access class: " + accessClassName + "{ " + ex.getMessage() + " }", ex);
}
}
private static Label[] constructLabels(Field[] fields) {
Label[] labels = new Label[fields.length];
for (int i = 0, n = labels.length; i < n; i++) {
labels[i] = new Label();
}
return labels;
}
private static Label[] constructLabels(Method[] methods) {
Label[] labels = new Label[methods.length];
for (int i = 0, n = labels.length; i < n; i++) {
labels[i] = new Label();
}
return labels;
}
private static <C> String determineEnclosingClassNm(Class<C> clazz, Class<?> enclosingType, final boolean isNonStaticMemberClass) {
final String enclosingClassNm;
if (!isNonStaticMemberClass) {
enclosingClassNm = null;
} else {
enclosingClassNm = enclosingType.getName().replace('.', '/');
}
return enclosingClassNm;
}
private static boolean determineNonStaticMemberClass(Class<?> clazz, Class<?> enclosingType) {
final boolean isNonStaticMemberClass;
if (enclosingType != null && clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {
isNonStaticMemberClass = true;
} else {
isNonStaticMemberClass = false;
}
;
return isNonStaticMemberClass;
}
private static String constructAccessClassName(String clazzName) {
String accessClassName = clazzName + AsmClassAccess.class.getSimpleName();
if (accessClassName.startsWith("java.")) {
accessClassName = AsmClassAccess.class.getSimpleName().toLowerCase() + accessClassName;
}
return accessClassName;
}
private static void enhanceForConstructor(ClassVisitor cw, String accessClassNm, String clazzNm) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Class;)V", "(L" + clazzNm + ";)V", null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, ASM_CLASS_ACCESS_NM, "<init>", "(Ljava/lang/Class;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
private static void enhanceForNewInstance(ClassVisitor cw, String classNm) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, classNm);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, classNm, "<init>", "()V");
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
private static void enhanceForNewInstanceInner(ClassVisitor cw, String classNm, String enclosingClassNm) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()LLjava/lang/Object;", null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, classNm);
mv.visitInsn(DUP);
mv.visitTypeInsn(NEW, enclosingClassNm);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, enclosingClassNm, "<init>", "()V");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKEVIRTUAL, classNm, "getClass", "()Ljava/lang/Class;");
mv.visitInsn(POP);
mv.visitMethodInsn(INVOKESPECIAL, classNm, "<init>", "(L" + enclosingClassNm + ";)V");
mv.visitInsn(ARETURN);
mv.visitMaxs(4, 1);
mv.visitEnd();
}
private static void enhanceForGetValueObject(ClassVisitor cw, String accessClassNm, String clazzNm, Field[] fields) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getValue", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, accessClassNm, "fieldNames", "[Ljava/lang/String;");
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "binarySearch", "([Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitVarInsn(ISTORE, 3);
mv.visitVarInsn(ILOAD, 3);
final int maxStack;
if (fields.length > 0) {
maxStack = 5;
Label[] labels = constructLabels(fields);
Label defaultLabel = new Label();
mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
for (int i = 0, n = labels.length; i < n; i++) {
Field field = fields[i];
mv.visitLabel(labels[i]);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, clazzNm);
mv.visitFieldInsn(GETFIELD, clazzNm, 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(F_SAME, 0, null, 0, null);
} else {
maxStack = 6;
}
enhanceForThrowingException(mv, IllegalArgumentException.class, "Field was not found", "Ljava/lang/Object;", ALOAD, 2);
mv.visitMaxs(maxStack, 4);
mv.visitEnd();
}
private static void enhanceForPutValueObject(ClassVisitor cw, String accessClassNm, String clazzNm, Field[] fields) {
int maxStack = 6;
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "putValue", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, accessClassNm, "fieldNames", "[Ljava/lang/String;");
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "binarySearch", "([Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitVarInsn(ISTORE, 4);
mv.visitVarInsn(ILOAD, 4);
if (fields.length > 0) {
maxStack = 5;
Label[] labels = constructLabels(fields);
Label defaultLabel = new Label();
mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
for (int i = 0, n = labels.length; i < n; i++) {
Field field = fields[i];
mv.visitLabel(labels[i]);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, clazzNm);
mv.visitVarInsn(ALOAD, 3);
Type fieldType = Type.getType(field.getType());
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, clazzNm, field.getName(), fieldType.getDescriptor());
mv.visitInsn(RETURN);
}
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
} else {
maxStack = 6;
}
enhanceForThrowingException(mv, IllegalArgumentException.class, "Field was not found", "Ljava/lang/Object;", ALOAD, 2);
mv.visitMaxs(maxStack, 5);
mv.visitEnd();
}
private static void enhanceForPutValuePrimitive(ClassVisitor cw, String accessClassNm, String clazzNm, Field[] fields, Type type) {
final String methodName;
final String typeNm = type.getDescriptor();
final int instruction;
switch (type.getSort()) {
case BOOLEAN:
methodName = "putBooleanValue";
instruction = ILOAD;
break;
case BYTE:
methodName = "putByteValue";
instruction = ILOAD;
break;
case CHAR:
methodName = "putCharValue";
instruction = ILOAD;
break;
case SHORT:
methodName = "putShortValue";
instruction = ILOAD;
break;
case INT:
methodName = "putIntValue";
instruction = ILOAD;
break;
case FLOAT:
methodName = "putFloatValue";
instruction = FLOAD;
break;
case LONG:
methodName = "putLongValue";
instruction = LLOAD;
break;
case DOUBLE:
methodName = "putDoubleValue";
instruction = DLOAD;
break;
default:
methodName = "put" + type.getInternalName().lastIndexOf('/') + "Value";
instruction = ALOAD;
break;
}
int offset = (instruction == LLOAD || instruction == DLOAD) ? 1 : 0;
int maxStack = 6;
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, "(Ljava/lang/Object;Ljava/lang/String;" + typeNm + ")V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, accessClassNm, "fieldNames", "[Ljava/lang/String;");
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "binarySearch", "([Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitVarInsn(ISTORE, 4 + offset);
mv.visitVarInsn(ILOAD, 4 + offset);
if (fields.length > 0) {
maxStack = 6;
Label[] labels = new Label[fields.length];
Label labelForInvalidTypes = new Label();
boolean hasAnyBadTypeLabel = false;
for (int i = 0, n = labels.length; i < n; i++) {
if (Type.getType(fields[i].getType()).equals(type))
labels[i] = new Label();
else {
labels[i] = labelForInvalidTypes;
hasAnyBadTypeLabel = true;
}
}
Label defaultLabel = new Label();
mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
for (int i = 0, n = labels.length; i < n; i++) {
if (!labels[i].equals(labelForInvalidTypes)) {
Field field = fields[i];
mv.visitLabel(labels[i]);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, clazzNm);
mv.visitVarInsn(instruction, 3);
mv.visitFieldInsn(PUTFIELD, clazzNm, field.getName(), typeNm);
mv.visitInsn(RETURN);
}
}
if (hasAnyBadTypeLabel) {
mv.visitLabel(labelForInvalidTypes);
mv.visitFrame(F_SAME, 0, null, 0, null);
enhanceForThrowingException(mv, IllegalArgumentException.class, type.getClassName(), typeNm, instruction, 3);
}
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
}
final int maxLocals = 5 + offset;
enhanceForThrowingException(mv, IllegalArgumentException.class, "Field was not found", typeNm, instruction, 3);
mv.visitMaxs(maxStack, maxLocals);
mv.visitEnd();
}
private static void enhanceForGetValuePrimitive(ClassVisitor cw, String accessClassNm, String clazzNm, Field[] fields, Type type) {
String methodName;
final String typeNm = type.getDescriptor();
final int instruction;
switch (type.getSort()) {
case Type.BOOLEAN:
methodName = "getBooleanValue";
instruction = IRETURN;
break;
case Type.BYTE:
methodName = "getByteValue";
instruction = IRETURN;
break;
case Type.CHAR:
methodName = "getCharValue";
instruction = IRETURN;
break;
case Type.SHORT:
methodName = "getShortValue";
instruction = IRETURN;
break;
case Type.INT:
methodName = "getIntValue";
instruction = IRETURN;
break;
case Type.FLOAT:
methodName = "getFloatValue";
instruction = FRETURN;
break;
case Type.LONG:
methodName = "getLongValue";
instruction = LRETURN;
break;
case Type.DOUBLE:
methodName = "getDoubleValue";
instruction = DRETURN;
break;
default:
methodName = "getValue";
instruction = ARETURN;
break;
}
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, methodName, "(Ljava/lang/Object;Ljava/lang/String;)" + typeNm, null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, accessClassNm, "fieldNames", "[Ljava/lang/String;");
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "binarySearch", "([Ljava/lang/Object;Ljava/lang/Object;)I");
mv.visitVarInsn(ISTORE, 3);
mv.visitVarInsn(ILOAD, 3);
final int maxStack;
if (fields.length > 0) {
maxStack = 5;
Label[] labels = constructLabels(fields);
Label defaultLabel = new Label();
mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
for (int i = 0, n = labels.length; i < n; i++) {
Field field = fields[i];
mv.visitLabel(labels[i]);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, clazzNm);
mv.visitFieldInsn(GETFIELD, clazzNm, field.getName(), typeNm);
mv.visitInsn(instruction);
}
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
} else {
maxStack = 6;
}
enhanceForThrowingException(mv, IllegalArgumentException.class, "Field was not found", "Ljava/lang/Object;", ALOAD, 2);
mv.visitMaxs(maxStack, 4);
mv.visitEnd();
}
private static void enhanceForThrowingException(MethodVisitor mv, Class<? extends Exception> exceptionClass, String msg, String argType, int instruction, int slot) {
String exceptionClassNm = Type.getInternalName(exceptionClass);
mv.visitTypeInsn(NEW, exceptionClassNm);
mv.visitInsn(DUP);
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitLdcInsn(msg);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(instruction, slot);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + argType + ")Ljava/lang/StringBuilder;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn(INVOKESPECIAL, exceptionClassNm, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(ATHROW);
}
private static void enhanceForInvokeMethod(ClassVisitor cw, String accessClassNm, String clazzNm, Method[] methods) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "invokeMethod", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", null, null);
mv.visitCode();
if (methods.length > 0) {
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, clazzNm);
mv.visitVarInsn(ASTORE, 4);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESPECIAL, "org/jadira/reflection/access/asm/AsmClassAccess", "getMethodIndex", "(Ljava/lang/reflect/Method;)I");
mv.visitVarInsn(ISTORE, 5);
mv.visitVarInsn(ILOAD, 5);
Label[] labels = constructLabels(methods);
Label defaultLabel = new Label();
mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
for (int i = 0; i < labels.length; i++) {
Method method = methods[i];
Class<?>[] parameterTypes = method.getParameterTypes();
mv.visitLabel(labels[i]);
if (i == 0) {
mv.visitFrame(F_APPEND, 1, new Object[] { clazzNm }, 0, null);
} else {
mv.visitFrame(F_SAME, 0, null, 0, null);
}
mv.visitVarInsn(ALOAD, 4);
StringBuilder methodDescriptor = new StringBuilder("(");
for (int parameterIdx = 0; parameterIdx < parameterTypes.length; parameterIdx++) {
Type type = Type.getType(parameterTypes[parameterIdx]);
mv.visitVarInsn(ALOAD, 3);
mv.visitIntInsn(BIPUSH, parameterIdx);
mv.visitInsn(AALOAD);
switch (type.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, type.getDescriptor());
break;
case Type.OBJECT:
mv.visitTypeInsn(CHECKCAST, type.getInternalName());
break;
}
methodDescriptor.append(type.getDescriptor());
}
methodDescriptor.append(')');
methodDescriptor.append(Type.getDescriptor(method.getReturnType()));
mv.visitMethodInsn(INVOKEVIRTUAL, clazzNm, method.getName(), methodDescriptor.toString());
switch (Type.getType(method.getReturnType()).getSort()) {
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;
}
mv.visitInsn(ARETURN);
}
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
}
enhanceForThrowingException(mv, IllegalArgumentException.class, "Method not found", "Ljava/lang/Object;", ALOAD, 2);
mv.visitMaxs(8, 6);
mv.visitEnd();
}
@Override
public abstract C newInstance();
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as an object
*/
public abstract Object getValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new value
*/
public abstract void putValue(C object, String fieldName, Object value);
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as a boolean
*/
public abstract boolean getBooleanValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new boolean value
*/
public abstract void putBooleanValue(C object, String fieldName, boolean value);
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as a byte
*/
public abstract byte getByteValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new byte value
*/
public abstract void putByteValue(C object, String fieldName, byte value);
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as a char
*/
public abstract char getCharValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new char value
*/
public abstract void putCharValue(C object, String fieldName, char value);
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as a short
*/
public abstract short getShortValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new short value
*/
public abstract void putShortValue(C object, String fieldName, short value);
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as a int
*/
public abstract int getIntValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new int value
*/
public abstract void putIntValue(C object, String fieldName, int value);
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as a long
*/
public abstract long getLongValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new long value
*/
public abstract void putLongValue(C object, String fieldName, long value);
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as a float
*/
public abstract float getFloatValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new float value
*/
public abstract void putFloatValue(C object, String fieldName, float value);
/**
* Retrieve the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @return The field value as a double
*/
public abstract double getDoubleValue(C object, String fieldName);
/**
* Update the value of the field for the given instance
* @param object The instance to access the field for
* @param fieldName Name of the field to access
* @param value The new double value
*/
public abstract void putDoubleValue(C object, String fieldName, double value);
/**
* Invoke the given method on the given instance
* @param target The target object
* @param method The method to invoke
* @param args The parameters to pass
* @return The result of the invocation (null for a void method)
*/
public abstract Object invokeMethod(Object target, Method method, Object[] args);
String foo = new String();
public Object invokeMethod2(Object target, Method method, Object[] args) {
return getMethodIndex(method);
}
private int getMethodIndex(Method m) {
int idx = Arrays.binarySearch(getDeclaredMethodNames(), m.getName());
if (getDeclaredMethodAccessors()[idx].method().equals(m)) {
return idx;
}
int backTrack = idx;
while (true) {
if (getDeclaredMethodAccessors()[backTrack].method().equals(m)) {
return idx;
}
if (!(getDeclaredMethodAccessors()[backTrack].method().getName()
.equals(m.getName()))) {
break;
}
backTrack = backTrack - 1;
}
while (true) {
idx = idx + 1;
if (getDeclaredMethodAccessors()[idx].method().equals(m)) {
return idx;
}
if (!(getDeclaredMethodAccessors()[idx].method().getName().equals(m.getName()))) {
break;
}
}
return -1;
}
@Override
protected MethodAccess<C> constructMethodAccess(Method method) {
return AsmMethodAccess.get(this, method);
}
@Override
protected FieldAccess<C> constructFieldAccess(Field field) {
if ((field.getModifiers() & Modifier.PRIVATE) != 0) {
return PortableFieldAccess.get(field);
} else {
return AsmFieldAccess.get(this, field);
}
}
@Override
protected <X> ClassAccess<X> constructClassAccess(Class<X> clazz) {
return AsmClassAccess.get(clazz);
}
}