Question

J'utilise la bibliothèque Java ASM pour remplacer une certaine réflexion. Je générer le corps de cette méthode:

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

Avec cette méthode générée, je peux définir des champs sur un objet lors de l'exécution sans utiliser la réflexion. Il fonctionne très bien. Cependant, je l'ai trouvé échoue pour les champs primitifs. Voici la partie pertinente de ma méthode de jeu:

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

Ce code génère cas des étiquettes pour une sélection. Il fonctionne très bien pour les objets, mais pour les primitives je reçois cette erreur:

  

Attendons de trouver flottant sur la pile

Ok, qui fait sens, je dois faire moi-même unboxing. Je mis en œuvre ce qui suit:

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

Je l'ai tracée à travers et il va certainement en « cas Type.FLOAT » pour le champ approprié, cependant, je reçois cette erreur:

  

pour trouver l'objet Expecting / tableau sur la pile

est où je suis coincé. Pour la vie de moi je ne peux pas comprendre pourquoi l'unboxing ne fonctionne pas. Le « aload, 3 » met le troisième paramètre de la méthode définie dans la pile, qui doit être un flotteur. Toutes les idées?

Je trouve la bibliothèque asm-communes a une classe GeneratorAdapter qui a une méthode Unbox. Cependant, je ne veux pas vraiment inclure encore un autre JAR pour quelque chose qui devrait être si simple. Je regardais la source GeneratorAdapter et il est en train de faire quelque chose de très similaire. J'ai essayé de modifier mon code pour utiliser GeneratorAdapter, juste pour voir si cela a fonctionné, mais n'a pas trouvé du tout facile à convertir.

Était-ce utile?

La solution

Transforme le unboxing ci-dessus fonctionnait correctement. J'avais du code qui fait un get et non la boxe le résultat avant d'essayer de le retourner comme un objet. Ma faute pour ne pas avoir un test plus simple!

Dans le cas où quelqu'un d'autre a besoin, voici le code approprié pour la boxe:

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

Autres conseils

Utilisation GeneratorAdapter son propre que doit MethodVisitor et a une unbox () qui iniserts le droit invoquer appel de méthode primitive.valueOf ().

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top