2009-10-14 14 views
7

Estoy usando la biblioteca ASM Java para reemplazar algunas reflexiones. Genero el cuerpo de este método:unboxing usando la biblioteca ASM Java

void set(Object object, int fieldIndex, Object value); 

Con este método generado, puedo establecer campos en un objeto en tiempo de ejecución sin necesidad de utilizar la reflexión. Funciona muy bien. Sin embargo, encontré que falla para los campos primitivos. Aquí está la parte relevante de mi método de configuración:

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); 
} 

Este código está generando etiquetas de casos para una selección. Funciona muy bien para los objetos primitivos pero para me sale este error:

Expecting to find float on stack

Ok, eso tiene sentido, tengo que hacer el unboxing mí mismo. He implementado el siguiente:

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); 
} 

he trazado a través y que sin duda pone en el "caso Type.FLOAT" para el campo apropiado, sin embargo, me sale este error:

Expecting to find object/array on stack

Aquí es donde estoy atrapado. Por mi vida no puedo entender por qué el unboxing no funciona. El "ALOAD, 3" está poniendo el tercer parámetro del método set en la pila, que debería ser un Float. ¿Algunas ideas?

Encontré que la biblioteca asm-commons tiene una clase GeneratorAdapter que tiene un método unbox. Sin embargo, realmente no quiero incluir otro JAR para algo que debería ser tan simple. Miré la fuente GeneratorAdapter y está haciendo algo muy similar. Traté de modificar mi código para usar GeneratorAdapter, solo para ver si funcionaba, pero no lo encontré para nada fácil de convertir.

Respuesta

7

Resulta que el desempaquetado anterior funcionaba correctamente. Tenía un código que estaba haciendo un get y no boxeando el resultado antes de intentar devolverlo como un Object. ¡Mi culpa por no tener una prueba más simple!

En caso de que alguien más lo necesita, aquí está el código apropiado para el boxeo:

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

Uso GeneratorAdapter su más limpio que debe MethodVisitor y tiene una unbox() que iniserts al invocar primitive.valueOf (método) justo llamada.

+0

Gracias, pero no quería una dependencia en los asm-commons adicionales que JAR necesitaba para usar GeneratorAdapter. – NateS