/*
* Copyright 2011-2013 the original author or authors.
*
* 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 kr.debop4j.core.reflect;
import com.google.common.collect.Lists;
import kr.debop4j.core.Guard;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import static org.objectweb.asm.Opcodes.*;
/**
* 객체의 속성에 접근하기 위한 Accessor 입니다. (속성에 대해 get, set을 수행할 수 있습니다)
*
* @author 배성혁 ( sunghyouk.bae@gmail.com )
* @since 13. 1. 21
*/
@Slf4j
public abstract class FieldAccess {
private String[] fieldNames;
/**
* Gets index.
*
* @param fieldName the field name
* @return the index
*/
public int getIndex(String fieldName) {
for (int i = 0, n = fieldNames.length; i < n; i++)
if (fieldNames[i].equals(fieldName)) return i;
throw new IllegalArgumentException("Unable to find public field: " + fieldName);
}
/**
* Set void.
*
* @param instance the instance
* @param fieldName the field name
* @param value the value
*/
public void set(Object instance, String fieldName, Object value) {
log.trace("객체[{}]의 속성[{}]에 값[{}] 을 설정합니다.", instance, fieldName, value);
set(instance, getIndex(fieldName), value);
}
/**
* Get object.
*
* @param instance the instance
* @param fieldName the field name
* @return the object
*/
public Object get(Object instance, String fieldName) {
log.trace("객체[{}]의 속성[{}] 값을 조회합니다.", instance, fieldName);
return get(instance, getIndex(fieldName));
}
/**
* Get field names.
*
* @return the string [ ]
*/
public String[] getFieldNames() {
return Arrays.copyOf(fieldNames, fieldNames.length);
}
/**
* Set void.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void set(Object instance, int fieldIndex, Object value);
/**
* Sets boolean.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void setBoolean(Object instance, int fieldIndex, boolean value);
/**
* Sets byte.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void setByte(Object instance, int fieldIndex, byte value);
/**
* Sets short.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void setShort(Object instance, int fieldIndex, short value);
/**
* Sets int.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void setInt(Object instance, int fieldIndex, int value);
/**
* Sets long.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void setLong(Object instance, int fieldIndex, long value);
/**
* Sets double.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void setDouble(Object instance, int fieldIndex, double value);
/**
* Sets float.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void setFloat(Object instance, int fieldIndex, float value);
/**
* Sets char.
*
* @param instance the instance
* @param fieldIndex the field index
* @param value the value
*/
abstract public void setChar(Object instance, int fieldIndex, char value);
/**
* Get object.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the object
*/
abstract public Object get(Object instance, int fieldIndex);
/**
* Gets string.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the string
*/
abstract public String getString(Object instance, int fieldIndex);
/**
* Gets char.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the char
*/
abstract public char getChar(Object instance, int fieldIndex);
/**
* Gets boolean.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the boolean
*/
abstract public boolean getBoolean(Object instance, int fieldIndex);
/**
* Gets byte.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the byte
*/
abstract public byte getByte(Object instance, int fieldIndex);
/**
* Gets short.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the short
*/
abstract public short getShort(Object instance, int fieldIndex);
/**
* Gets int.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the int
*/
abstract public int getInt(Object instance, int fieldIndex);
/**
* Gets long.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the long
*/
abstract public long getLong(Object instance, int fieldIndex);
/**
* Gets double.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the double
*/
abstract public double getDouble(Object instance, int fieldIndex);
/**
* Gets float.
*
* @param instance the instance
* @param fieldIndex the field index
* @return the float
*/
abstract public float getFloat(Object instance, int fieldIndex);
/**
* Get field access.
*
* @param type the type
* @return the field access
*/
static public FieldAccess get(Class type) {
Guard.shouldNotBeNull(type, "type");
List<Field> fields = Lists.newArrayList();
Class nextClass = type;
while (nextClass != Object.class) {
Field[] declaredFields = nextClass.getDeclaredFields();
for (Field field : declaredFields) {
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers)) continue;
if (Modifier.isPrivate(modifiers)) continue;
fields.add(field);
}
nextClass = nextClass.getSuperclass();
}
String[] fieldNames = new String[fields.size()];
for (int i = 0, n = fieldNames.length; i < n; i++)
fieldNames[i] = fields.get(i).getName();
String className = type.getName();
String accessClassName = className + "FieldAccess";
if (accessClassName.startsWith("java.")) accessClassName = ReflectConsts.BASE_PACKAGE + "." + accessClassName;
Class accessClass = null;
AccessClassLoader loader = AccessClassLoader.get(type);
synchronized (loader) {
try {
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored) {
String accessClassNameInternal = accessClassName.replace('.', '/');
String classNameInternal = className.replace('.', '/');
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, ReflectConsts.FIELD_ACCESS_PATH,
null);
insertConstructor(cw);
insertGetObject(cw, classNameInternal, fields);
insertSetObject(cw, classNameInternal, fields);
insertGetPrimitive(cw, classNameInternal, fields, Type.BOOLEAN_TYPE);
insertSetPrimitive(cw, classNameInternal, fields, Type.BOOLEAN_TYPE);
insertGetPrimitive(cw, classNameInternal, fields, Type.BYTE_TYPE);
insertSetPrimitive(cw, classNameInternal, fields, Type.BYTE_TYPE);
insertGetPrimitive(cw, classNameInternal, fields, Type.SHORT_TYPE);
insertSetPrimitive(cw, classNameInternal, fields, Type.SHORT_TYPE);
insertGetPrimitive(cw, classNameInternal, fields, Type.INT_TYPE);
insertSetPrimitive(cw, classNameInternal, fields, Type.INT_TYPE);
insertGetPrimitive(cw, classNameInternal, fields, Type.LONG_TYPE);
insertSetPrimitive(cw, classNameInternal, fields, Type.LONG_TYPE);
insertGetPrimitive(cw, classNameInternal, fields, Type.DOUBLE_TYPE);
insertSetPrimitive(cw, classNameInternal, fields, Type.DOUBLE_TYPE);
insertGetPrimitive(cw, classNameInternal, fields, Type.FLOAT_TYPE);
insertSetPrimitive(cw, classNameInternal, fields, Type.FLOAT_TYPE);
insertGetPrimitive(cw, classNameInternal, fields, Type.CHAR_TYPE);
insertSetPrimitive(cw, classNameInternal, fields, Type.CHAR_TYPE);
insertGetString(cw, classNameInternal, fields);
cw.visitEnd();
accessClass = loader.defineClass(accessClassName, cw.toByteArray());
}
}
try {
FieldAccess access = (FieldAccess) accessClass.newInstance();
access.fieldNames = fieldNames;
return access;
} catch (Exception ex) {
throw new RuntimeException("Error constructing field access class: " + accessClassName, ex);
}
}
static private void insertConstructor(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, ReflectConsts.FIELD_ACCESS_PATH, "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
static private void insertSetObject(ClassWriter cw, String classNameInternal, List<Field> fields) {
int maxStack = 6;
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 2);
if (!fields.isEmpty()) {
maxStack--;
Label[] labels = new Label[fields.size()];
for (int i = 0, n = labels.length; i < n; i++)
labels[i] = new Label();
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.get(i);
Type fieldType = Type.getType(field.getType());
mv.visitLabel(labels[i]);
mv.visitFrame(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;
default:
}
mv.visitFieldInsn(PUTFIELD, classNameInternal, field.getName(), fieldType.getDescriptor());
mv.visitInsn(RETURN);
}
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
}
mv = insertThrowExceptionForFieldNotFound(mv);
mv.visitMaxs(maxStack, 4);
mv.visitEnd();
}
static private void insertGetObject(ClassWriter cw, String classNameInternal, List<Field> fields) {
int maxStack = 6;
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 2);
if (!fields.isEmpty()) {
maxStack--;
Label[] labels = new Label[fields.size()];
for (int i = 0, n = labels.length; i < n; i++)
labels[i] = new Label();
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.get(i);
mv.visitLabel(labels[i]);
mv.visitFrame(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;
default:
}
mv.visitInsn(ARETURN);
}
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
}
insertThrowExceptionForFieldNotFound(mv);
mv.visitMaxs(maxStack, 3);
mv.visitEnd();
}
static private void insertGetString(ClassWriter cw, String classNameInternal, List<Field> fields) {
int maxStack = 6;
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getString", "(Ljava/lang/Object;I)Ljava/lang/String;", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 2);
if (!fields.isEmpty()) {
maxStack--;
Label[] labels = new Label[fields.size()];
Label labelForInvalidTypes = new Label();
boolean hasAnyBadTypeLabel = false;
for (int i = 0, n = labels.length; i < n; i++) {
if (fields.get(i).getType().equals(String.class))
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)) {
mv.visitLabel(labels[i]);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
mv.visitFieldInsn(GETFIELD, classNameInternal, fields.get(i).getName(), "Ljava/lang/String;");
mv.visitInsn(ARETURN);
}
}
// Rest of fields: different type
if (hasAnyBadTypeLabel) {
mv.visitLabel(labelForInvalidTypes);
mv.visitFrame(F_SAME, 0, null, 0, null);
insertThrowExceptionForFieldType(mv, "String");
}
// Default: field not found
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
}
insertThrowExceptionForFieldNotFound(mv);
mv.visitMaxs(maxStack, 3);
mv.visitEnd();
}
static private void insertSetPrimitive(ClassWriter cw, String classNameInternal, List<Field> fields, Type primitiveType) {
int maxStack = 6;
int maxLocals = 4; // See correction below for LLOAD and DLOAD
final String setterMethodName;
final String typeNameInternal = primitiveType.getDescriptor();
final int loadValueInstruction;
switch (primitiveType.getSort()) {
case Type.BOOLEAN:
setterMethodName = "setBoolean";
loadValueInstruction = ILOAD;
break;
case Type.BYTE:
setterMethodName = "setByte";
loadValueInstruction = ILOAD;
break;
case Type.CHAR:
setterMethodName = "setChar";
loadValueInstruction = ILOAD;
break;
case Type.SHORT:
setterMethodName = "setShort";
loadValueInstruction = ILOAD;
break;
case Type.INT:
setterMethodName = "setInt";
loadValueInstruction = ILOAD;
break;
case Type.FLOAT:
setterMethodName = "setFloat";
loadValueInstruction = FLOAD;
break;
case Type.LONG:
setterMethodName = "setLong";
loadValueInstruction = LLOAD;
maxLocals++; // (LLOAD and DLOAD actually load two slots)
break;
case Type.DOUBLE:
setterMethodName = "setDouble";
loadValueInstruction = DLOAD; // (LLOAD and DLOAD actually load two slots)
maxLocals++;
break;
default:
setterMethodName = "set";
loadValueInstruction = ALOAD;
break;
}
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, setterMethodName, "(Ljava/lang/Object;I" + typeNameInternal + ")V", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 2);
if (!fields.isEmpty()) {
maxStack--;
Label[] labels = new Label[fields.size()];
Label labelForInvalidTypes = new Label();
boolean hasAnyBadTypeLabel = false;
for (int i = 0, n = labels.length; i < n; i++) {
if (Type.getType(fields.get(i).getType()).equals(primitiveType))
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)) {
mv.visitLabel(labels[i]);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
mv.visitVarInsn(loadValueInstruction, 3);
mv.visitFieldInsn(PUTFIELD, classNameInternal, fields.get(i).getName(), typeNameInternal);
mv.visitInsn(RETURN);
}
}
// Rest of fields: different type
if (hasAnyBadTypeLabel) {
mv.visitLabel(labelForInvalidTypes);
mv.visitFrame(F_SAME, 0, null, 0, null);
insertThrowExceptionForFieldType(mv, primitiveType.getClassName());
}
// Default: field not found
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
}
mv = insertThrowExceptionForFieldNotFound(mv);
mv.visitMaxs(maxStack, maxLocals);
mv.visitEnd();
}
static private void insertGetPrimitive(ClassWriter cw, String classNameInternal, List<Field> fields, Type primitiveType) {
int maxStack = 6;
final String getterMethodName;
final String typeNameInternal = primitiveType.getDescriptor();
final int returnValueInstruction;
switch (primitiveType.getSort()) {
case Type.BOOLEAN:
getterMethodName = "getBoolean";
returnValueInstruction = IRETURN;
break;
case Type.BYTE:
getterMethodName = "getByte";
returnValueInstruction = IRETURN;
break;
case Type.CHAR:
getterMethodName = "getChar";
returnValueInstruction = IRETURN;
break;
case Type.SHORT:
getterMethodName = "getShort";
returnValueInstruction = IRETURN;
break;
case Type.INT:
getterMethodName = "getInt";
returnValueInstruction = IRETURN;
break;
case Type.FLOAT:
getterMethodName = "getFloat";
returnValueInstruction = FRETURN;
break;
case Type.LONG:
getterMethodName = "getLong";
returnValueInstruction = LRETURN;
break;
case Type.DOUBLE:
getterMethodName = "getDouble";
returnValueInstruction = DRETURN;
break;
default:
getterMethodName = "get";
returnValueInstruction = ARETURN;
break;
}
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, getterMethodName, "(Ljava/lang/Object;I)" + typeNameInternal, null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 2);
if (!fields.isEmpty()) {
maxStack--;
Label[] labels = new Label[fields.size()];
Label labelForInvalidTypes = new Label();
boolean hasAnyBadTypeLabel = false;
for (int i = 0, n = labels.length; i < n; i++) {
if (Type.getType(fields.get(i).getType()).equals(primitiveType))
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++) {
Field field = fields.get(i);
if (!labels[i].equals(labelForInvalidTypes)) {
mv.visitLabel(labels[i]);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
mv.visitFieldInsn(GETFIELD, classNameInternal, field.getName(), typeNameInternal);
mv.visitInsn(returnValueInstruction);
}
}
// Rest of fields: different type
if (hasAnyBadTypeLabel) {
mv.visitLabel(labelForInvalidTypes);
mv.visitFrame(F_SAME, 0, null, 0, null);
insertThrowExceptionForFieldType(mv, primitiveType.getClassName());
}
// Default: field not found
mv.visitLabel(defaultLabel);
mv.visitFrame(F_SAME, 0, null, 0, null);
}
mv = insertThrowExceptionForFieldNotFound(mv);
mv.visitMaxs(maxStack, 3);
mv.visitEnd();
}
static private MethodVisitor insertThrowExceptionForFieldNotFound(MethodVisitor mv) {
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);
return mv;
}
static private MethodVisitor insertThrowExceptionForFieldType(MethodVisitor mv, String fieldType) {
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
mv.visitInsn(DUP);
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitLdcInsn("Field not declared as " + fieldType + ": ");
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);
return mv;
}
}