I am using the ASM Java library to replace some reflection. I generate the body of this method:
void set(Object object, int fieldIndex, Object value);
Using this generated method, I can set fields for an object at runtime without using reflection. It works great. However, I found that for primitive fields this fails. Here is the relevant part of my method:
for (int i = 0, n = cachedFields.length; i < n; i++) { mv.visitLabel(labels[i]); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, targetClassName); mv.visitVarInsn(ALOAD, 3); Field field = cachedFields[i].field; Type fieldType = Type.getType(field.getType()); mv.visitFieldInsn(PUTFIELD, targetClassName, field.getName(), fieldType.getDescriptor()); mv.visitInsn(RETURN); }
This code generates label tags for selection. It works fine for objects, but for primitives I get this error:
Waiting for a float search on the stack
Ok, that makes sense, I need to do unboxing myself. I have done the following:
for (int i = 0, n = cachedFields.length; i < n; i++) { mv.visitLabel(labels[i]); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, targetClassName); mv.visitVarInsn(ALOAD, 3); Field field = cachedFields[i].field; 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, targetClassName, field.getName(), fieldType.getDescriptor()); mv.visitInsn(RETURN); }
I traced and, of course, gets into "case Type.FLOAT" for the corresponding field, however I get this error:
Waiting for an object / array search on the stack
This is where I am stuck. For life, I cannot understand why unpacking does not work. "ALOAD 3" pushes the third parameter of the installed method, which should be Float, onto the stack. Any ideas?
I found that the asm-commons library has a GeneratorAdapter class that has an unbox method. However, I really don't want to include another JAR for something that should be so simple. I looked at the source of the GeneratorAdapter and it does something very similar. I tried changing my code to use the GeneratorAdapter, just to make sure it worked, but did not find it easy to convert.