Я использую библиотеку ASM Java для замены некоторого отражения. Я генерирую тело этого метода:unboxing с использованием библиотеки ASM Java
void set(Object object, int fieldIndex, Object value);
С помощью этого сгенерированного метода я могу задать поля для объекта во время выполнения без использования отражения. Он отлично работает. Однако я обнаружил, что для примитивных полей это не удается. Вот соответствующая часть моего метода:
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);
}
Этот код генерирует метки фреймов для выбора. Он отлично работает для объектов, но для примитивов я получаю эту ошибку:
Expecting to find float on stack
Ok, это имеет смысл, мне нужно сделать распаковку самого. Я осуществил следующее:
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);
}
Я проследил через и это, безусловно, входит в «случае Type.FLOAT» для соответствующего поля, однако, я получаю эту ошибку:
Expecting to find object/array on stack
Это где я «Застрял. Для жизни я не могу понять, почему распаковка не работает. «ALOAD, 3» помещает в стек третий параметр установленного метода, который должен быть Float. Есть идеи?
Я нашел, что библиотека asm-commons имеет класс GeneratorAdapter, который имеет метод unbox. Тем не менее, я действительно не хочу включать еще один JAR для чего-то, что должно быть так просто. Я посмотрел на источник GeneratorAdapter, и он делает что-то очень похожее. Я попытался изменить свой код, чтобы использовать GeneratorAdapter, чтобы убедиться, что он сработал, но не нашел его легко конвертировать.
Спасибо, но мне не нужна зависимость от дополнительных asm-commons JAR, необходимых для использования GeneratorAdapter. – NateS