Pregunta

Estoy usando la biblioteca ASM Java para reemplazar alguna reflexión. Genero el cuerpo de este método:

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 a la perfección. Sin embargo, encontré que falla por campos primitivos. Aquí está la parte pertinente de mi método set:

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 caso etiquetas para una selección. Funciona muy bien para los objetos primitivos pero para me sale este error:

  

Esperando encontrar flotador en la pila

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 rastreado a través y que sin duda pone en el "caso Type.FLOAT" para el campo apropiado, sin embargo, me sale este error:

  

Esperando encontrar objetos / matriz en la pila

Aquí es donde estoy atascado. Porque la vida de mí no puedo entender por qué el unboxing no funciona. El "aload, 3" está poniendo el tercer parámetro del método de conjunto en la pila, que debe ser un flotador. ¿Alguna idea?

Me encontrado la biblioteca ASM-Commons tiene una clase GeneratorAdapter que tiene un método unbox. Sin embargo, yo realmente no quiero incluir otro JAR para algo que debería ser tan simple. Miré a la fuente GeneratorAdapter y lo está haciendo algo muy similar. He intentado modificar el código para utilizar GeneratorAdapter, sólo para ver si funcionaba, pero no nos pareció nada fácil para convertir.

¿Fue útil?

Solución

Ahora resulta que el unboxing anteriormente funcionaba correctamente. Tenía código que estaba haciendo un get y no el resultado de boxeo antes de tratar de devolverlo como un objeto. 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;
}

Otros consejos

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top