下面列出了org.objectweb.asm.Type#LONG 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private Expression pushDefaultValue(Type type) {
switch (type.getSort()) {
case BOOLEAN:
return value(false);
case CHAR:
return value((char) 0);
case BYTE:
return value((byte) 0);
case SHORT:
return value((short) 0);
case INT:
return value(0);
case Type.LONG:
return value(0L);
case Type.FLOAT:
return value(0f);
case Type.DOUBLE:
return value(0d);
case ARRAY:
case OBJECT:
return nullRef(type);
default:
throw new IllegalArgumentException("Unsupported type " + type);
}
}
private static Type getBoxedType(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return BYTE_TYPE;
case Type.BOOLEAN:
return BOOLEAN_TYPE;
case Type.SHORT:
return SHORT_TYPE;
case Type.CHAR:
return CHARACTER_TYPE;
case Type.INT:
return INTEGER_TYPE;
case Type.FLOAT:
return FLOAT_TYPE;
case Type.LONG:
return LONG_TYPE;
case Type.DOUBLE:
return DOUBLE_TYPE;
default:
return type;
}
}
private static Type getBoxedType(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return BYTE_TYPE;
case Type.BOOLEAN:
return BOOLEAN_TYPE;
case Type.SHORT:
return SHORT_TYPE;
case Type.CHAR:
return CHARACTER_TYPE;
case Type.INT:
return INTEGER_TYPE;
case Type.FLOAT:
return FLOAT_TYPE;
case Type.LONG:
return LONG_TYPE;
case Type.DOUBLE:
return DOUBLE_TYPE;
default:
return type;
}
}
private void boxingIfShould(final String desc) {
switch (Type.getType(desc).getSort()) {
case Type.BOOLEAN:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
break;
case Type.BYTE:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
break;
case Type.CHAR:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
break;
case Type.SHORT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
break;
case Type.INT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
break;
case Type.FLOAT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
break;
case Type.LONG:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
break;
case Type.DOUBLE:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
break;
}
}
/**
* Generates the instructions to unbox the top stack value. This value is replaced by its unboxed
* equivalent on top of the stack.
*
* @param type the type of the top stack value.
*/
public void unbox(final Type type) {
Type boxedType = NUMBER_TYPE;
Method unboxMethod;
switch (type.getSort()) {
case Type.VOID:
return;
case Type.CHAR:
boxedType = CHARACTER_TYPE;
unboxMethod = CHAR_VALUE;
break;
case Type.BOOLEAN:
boxedType = BOOLEAN_TYPE;
unboxMethod = BOOLEAN_VALUE;
break;
case Type.DOUBLE:
unboxMethod = DOUBLE_VALUE;
break;
case Type.FLOAT:
unboxMethod = FLOAT_VALUE;
break;
case Type.LONG:
unboxMethod = LONG_VALUE;
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
unboxMethod = INT_VALUE;
break;
default:
unboxMethod = null;
}
if (unboxMethod == null) {
checkCast(type);
} else {
checkCast(boxedType);
invokeVirtual(boxedType, unboxMethod);
}
}
/**
* Constructs a new local variable of the given type.
*
* @param type the type of the local variable to be created.
* @return the identifier of the newly created local variable.
*/
public int newLocal(final Type type) {
Object localType;
switch (type.getSort()) {
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.SHORT:
case Type.INT:
localType = Opcodes.INTEGER;
break;
case Type.FLOAT:
localType = Opcodes.FLOAT;
break;
case Type.LONG:
localType = Opcodes.LONG;
break;
case Type.DOUBLE:
localType = Opcodes.DOUBLE;
break;
case Type.ARRAY:
localType = type.getDescriptor();
break;
case Type.OBJECT:
localType = type.getInternalName();
break;
default:
throw new AssertionError();
}
int local = newLocalMapping(type);
setLocalType(local, type);
setFrameLocal(local, localType);
return local;
}
/**
* Generates the instruction to create a new array.
*
* @param type the type of the array elements.
*/
public void newArray(final Type type) {
int arrayType;
switch (type.getSort()) {
case Type.BOOLEAN:
arrayType = Opcodes.T_BOOLEAN;
break;
case Type.CHAR:
arrayType = Opcodes.T_CHAR;
break;
case Type.BYTE:
arrayType = Opcodes.T_BYTE;
break;
case Type.SHORT:
arrayType = Opcodes.T_SHORT;
break;
case Type.INT:
arrayType = Opcodes.T_INT;
break;
case Type.FLOAT:
arrayType = Opcodes.T_FLOAT;
break;
case Type.LONG:
arrayType = Opcodes.T_LONG;
break;
case Type.DOUBLE:
arrayType = Opcodes.T_DOUBLE;
break;
default:
typeInsn(Opcodes.ANEWARRAY, type);
return;
}
mv.visitIntInsn(Opcodes.NEWARRAY, arrayType);
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
Type argType = arg.load(ctx);
int argSort = argType.getSort();
if (argSort == Type.DOUBLE || argSort == Type.FLOAT || argSort == Type.LONG || argSort == Type.INT) {
g.math(GeneratorAdapter.NEG, argType);
return argType;
}
if (argSort == Type.BYTE || argSort == Type.SHORT || argSort == Type.CHAR) {
// g.cast(argType, INT_TYPE);
g.math(GeneratorAdapter.NEG, INT_TYPE);
return argType;
}
if (argSort == Type.BOOLEAN) {
Label labelTrue = new Label();
Label labelExit = new Label();
g.push(true);
g.ifCmp(BOOLEAN_TYPE, GeneratorAdapter.EQ, labelTrue);
g.push(true);
g.goTo(labelExit);
g.mark(labelTrue);
g.push(false);
g.mark(labelExit);
return INT_TYPE;
}
throw new RuntimeException(format("%s is not primitive. %s",
ctx.toJavaType(argType),
exceptionInGeneratedClass(ctx))
);
}
/**
* Generates the instruction to push the given value on the stack.
*
* @param value the value to be pushed on the stack.
*/
public void push(final Type value) {
if (value == null) {
mv.visitInsn(Opcodes.ACONST_NULL);
} else {
switch (value.getSort()) {
case Type.BOOLEAN:
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", CLASS_DESCRIPTOR);
break;
case Type.CHAR:
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", "TYPE", CLASS_DESCRIPTOR);
break;
case Type.BYTE:
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", CLASS_DESCRIPTOR);
break;
case Type.SHORT:
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", CLASS_DESCRIPTOR);
break;
case Type.INT:
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", "TYPE", CLASS_DESCRIPTOR);
break;
case Type.FLOAT:
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", CLASS_DESCRIPTOR);
break;
case Type.LONG:
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", CLASS_DESCRIPTOR);
break;
case Type.DOUBLE:
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", "TYPE", CLASS_DESCRIPTOR);
break;
default:
mv.visitLdcInsn(value);
break;
}
}
}
/**
* Convert a Type sort to a string representation.
*
* @param sort
* Type sort value.
*
* @return Sort string value.
*/
public static String sortToString(int sort) {
switch(sort) {
case Type.VOID:
return "VOID";
case Type.BOOLEAN:
return "BOOLEAN";
case Type.CHAR:
return "CHAR";
case Type.BYTE:
return "BYTE";
case Type.SHORT:
return "SHORT";
case Type.INT:
return "INT";
case Type.FLOAT:
return "FLOAT";
case Type.LONG:
return "LONG";
case Type.DOUBLE:
return "DOUBLE";
case Type.ARRAY:
return "ARRAY";
case Type.OBJECT:
return "OBJECT";
case Type.METHOD:
return "METHOD";
case INTERNAL:
return "INTERNAL";
default:
return "UNKNOWN";
}
}
private static int doMethodEmulation(String desc) {
int result = 0;
Type returnType = Type.getReturnType(desc);
if (returnType.getSort() == Type.LONG || returnType.getSort() == Type.DOUBLE)
result++;
if (returnType.getSort() != Type.VOID)
result++;
return result;
}
/**
* Constructs a new {@link AnalyzerAdapter}.
*
* @param api the ASM API version implemented by this visitor. Must be one of {@link
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
* @param owner the owner's class name.
* @param access the method's access flags (see {@link Opcodes}).
* @param name the method's name.
* @param descriptor the method's descriptor (see {@link Type}).
* @param methodVisitor the method visitor to which this adapter delegates calls. May be {@literal
* null}.
*/
protected AnalyzerAdapter(
final int api,
final String owner,
final int access,
final String name,
final String descriptor,
final MethodVisitor methodVisitor) {
super(api, methodVisitor);
this.owner = owner;
locals = new ArrayList<Object>();
stack = new ArrayList<Object>();
uninitializedTypes = new HashMap<Object, Object>();
if ((access & Opcodes.ACC_STATIC) == 0) {
if ("<init>".equals(name)) {
locals.add(Opcodes.UNINITIALIZED_THIS);
} else {
locals.add(owner);
}
}
for (Type argumentType : Type.getArgumentTypes(descriptor)) {
switch (argumentType.getSort()) {
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.SHORT:
case Type.INT:
locals.add(Opcodes.INTEGER);
break;
case Type.FLOAT:
locals.add(Opcodes.FLOAT);
break;
case Type.LONG:
locals.add(Opcodes.LONG);
locals.add(Opcodes.TOP);
break;
case Type.DOUBLE:
locals.add(Opcodes.DOUBLE);
locals.add(Opcodes.TOP);
break;
case Type.ARRAY:
locals.add(argumentType.getDescriptor());
break;
case Type.OBJECT:
locals.add(argumentType.getInternalName());
break;
default:
throw new AssertionError();
}
}
maxLocals = locals.size();
}
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();
}
/**
* Append the call of proper extract primitive type of an boxed object. this
* methode use Number interface to unbox object
*/
protected static void autoUnBoxing2(MethodVisitor mv, Type fieldType) {
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/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, fieldType.getInternalName());
break;
default:
mv.visitTypeInsn(CHECKCAST, fieldType.getInternalName());
}
}
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();
}
/**
* Generates instructions that returns a dummy value. Return values are as follows:
* <ul>
* <li>void -> no value</li>
* <li>boolean -> false</li>
* <li>byte/short/char/int -> 0</li>
* <li>long -> 0L</li>
* <li>float -> 0.0f</li>
* <li>double -> 0.0</li>
* <li>Object -> null</li>
* </ul>
*
* @param returnType return type of the method this generated bytecode is for
* @return instructions to return a dummy value
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if {@code returnType}'s sort is of {@link Type#METHOD}
*/
private static InsnList returnDummy(Type returnType) {
Validate.notNull(returnType);
Validate.isTrue(returnType.getSort() != Type.METHOD);
InsnList ret = new InsnList();
switch (returnType.getSort()) {
case Type.VOID:
ret.add(new InsnNode(Opcodes.RETURN));
break;
case Type.BOOLEAN:
case Type.BYTE:
case Type.SHORT:
case Type.CHAR:
case Type.INT:
ret.add(new InsnNode(Opcodes.ICONST_0));
ret.add(new InsnNode(Opcodes.IRETURN));
break;
case Type.LONG:
ret.add(new InsnNode(Opcodes.LCONST_0));
ret.add(new InsnNode(Opcodes.LRETURN));
break;
case Type.FLOAT:
ret.add(new InsnNode(Opcodes.FCONST_0));
ret.add(new InsnNode(Opcodes.FRETURN));
break;
case Type.DOUBLE:
ret.add(new InsnNode(Opcodes.DCONST_0));
ret.add(new InsnNode(Opcodes.DRETURN));
break;
case Type.OBJECT:
case Type.ARRAY:
ret.add(new InsnNode(Opcodes.ACONST_NULL));
ret.add(new InsnNode(Opcodes.ARETURN));
break;
default:
throw new IllegalStateException();
}
return ret;
}
@Override
public Type load(Context ctx) {
GeneratorAdapter g = ctx.getGeneratorAdapter();
int resultVar = g.newLocal(INT_TYPE);
boolean firstIteration = true;
for (Expression argument : arguments) {
if (firstIteration) {
g.push(0);
firstIteration = false;
} else {
g.push(31);
g.loadLocal(resultVar);
g.math(IMUL, INT_TYPE);
}
Type fieldType = argument.load(ctx);
if (isPrimitiveType(fieldType)) {
if (fieldType.getSort() == Type.LONG) {
g.dup2();
g.push(32);
g.visitInsn(LUSHR);
g.visitInsn(LXOR);
g.visitInsn(L2I);
}
if (fieldType.getSort() == Type.FLOAT) {
g.invokeStatic(getType(Float.class), getMethod("int floatToRawIntBits (float)"));
}
if (fieldType.getSort() == Type.DOUBLE) {
g.invokeStatic(getType(Double.class), getMethod("long doubleToRawLongBits (double)"));
g.dup2();
g.push(32);
g.visitInsn(LUSHR);
g.visitInsn(LXOR);
g.visitInsn(L2I);
}
g.visitInsn(IADD);
} else {
int tmpVar = g.newLocal(fieldType);
g.storeLocal(tmpVar);
g.loadLocal(tmpVar);
Label ifNullLabel = g.newLabel();
g.ifNull(ifNullLabel);
g.loadLocal(tmpVar);
g.invokeVirtual(fieldType, getMethod("int hashCode()"));
g.visitInsn(IADD);
g.mark(ifNullLabel);
}
g.storeLocal(resultVar);
}
if (firstIteration) {
g.push(0);
} else {
g.loadLocal(resultVar);
}
return INT_TYPE;
}
/**
* Generates the instructions to jump to a label based on the comparison of the top two stack
* values.
*
* @param type the type of the top two stack values.
* @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE.
* @param label where to jump if the comparison result is {@literal true}.
*/
public void ifCmp(final Type type, final int mode, final Label label) {
switch (type.getSort()) {
case Type.LONG:
mv.visitInsn(Opcodes.LCMP);
break;
case Type.DOUBLE:
mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL : Opcodes.DCMPG);
break;
case Type.FLOAT:
mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL : Opcodes.FCMPG);
break;
case Type.ARRAY:
case Type.OBJECT:
if (mode == EQ) {
mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
return;
} else if (mode == NE) {
mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
return;
} else {
throw new IllegalArgumentException("Bad comparison for type " + type);
}
default:
int intOp = -1;
switch (mode) {
case EQ:
intOp = Opcodes.IF_ICMPEQ;
break;
case NE:
intOp = Opcodes.IF_ICMPNE;
break;
case GE:
intOp = Opcodes.IF_ICMPGE;
break;
case LT:
intOp = Opcodes.IF_ICMPLT;
break;
case LE:
intOp = Opcodes.IF_ICMPLE;
break;
case GT:
intOp = Opcodes.IF_ICMPGT;
break;
default:
throw new IllegalArgumentException("Bad comparison mode " + mode);
}
mv.visitJumpInsn(intOp, label);
return;
}
mv.visitJumpInsn(mode, label);
}
static private void insertSetPrimitive (ClassWriter cw, String classNameInternal, ArrayList<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;
maxLocals++; // (LLOAD and DLOAD actually load two slots)
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)) {
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.visitVarInsn(loadValueInstruction, 3);
mv.visitFieldInsn(PUTFIELD, field.getDeclaringClass().getName().replace('.', '/'), field.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();
}
/**
* Compute sizes required for the storage arrays that will contain the operand stack at this frame.
* @param frame frame to compute for
* @param offset the position within the operand stack to start calculating
* @param length the number of stack items to include in calculation
* @return size required by each storage array
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if any numeric argument is negative, or if {@code offset + length} is larger than the size of the
* operand stack
*/
public static StorageSizes computeSizes(Frame<BasicValue> frame, int offset, int length) {
Validate.notNull(frame);
Validate.isTrue(offset >= 0);
Validate.isTrue(length >= 0);
Validate.isTrue(offset < frame.getStackSize());
Validate.isTrue(offset + length <= frame.getStackSize());
// Count size required for each storage array
int intsSize = 0;
int longsSize = 0;
int floatsSize = 0;
int doublesSize = 0;
int objectsSize = 0;
for (int i = offset + length - 1; i >= offset; i--) {
BasicValue basicValue = frame.getStack(i);
Type type = basicValue.getType();
// If type is 'Lnull;', this means that the slot has been assigned null and that "there has been no merge yet that would 'raise'
// the type toward some class or interface type" (from ASM mailing list). We know this slot will always contain null at this
// point in the code so we can avoid saving it. When we load it back up, we can simply push a null in to that slot, thereby
// keeping the same 'Lnull;' type.
if ("Lnull;".equals(type.getDescriptor())) {
continue;
}
switch (type.getSort()) {
case Type.BOOLEAN:
case Type.BYTE:
case Type.SHORT:
case Type.CHAR:
case Type.INT:
intsSize++;
break;
case Type.FLOAT:
floatsSize++;
break;
case Type.LONG:
longsSize++;
break;
case Type.DOUBLE:
doublesSize++;
break;
case Type.ARRAY:
case Type.OBJECT:
objectsSize++;
break;
case Type.METHOD:
case Type.VOID:
default:
throw new IllegalStateException();
}
}
return new StorageSizes(intsSize, longsSize, floatsSize, doublesSize, objectsSize);
}