2009-10-14 2 views
7

Я использую библиотеку 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, чтобы убедиться, что он сработал, но не нашел его легко конвертировать.

ответ

7

Выбрано выше, чем описано выше, работает правильно. У меня был код, который делал get и не боксировал результат, прежде чем пытаться вернуть его как Object. Моя вина за то, что у меня не было более простого теста!

В случае, если кто-то нуждается в этом, вот правильный код для бокса:

Type fieldType = Type.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; 
} 
1

Использование GeneratorAdapter его должен чище, чем MethodVisitor и имеет распаковывать(), который iniserts право ссылаться primitive.valueOf (метод) вызов.

+0

Спасибо, но мне не нужна зависимость от дополнительных asm-commons JAR, необходимых для использования GeneratorAdapter. – NateS

Смежные вопросы