2009-10-14 16 views
7

J'utilise la bibliothèque ASM Java pour remplacer certaines réflexions. Je génère le corps de cette méthode:désemballage à l'aide de la bibliothèque Java ASM

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 de réflexion. Cela fonctionne très bien. Cependant, j'ai trouvé qu'il échoue pour les champs primitifs. Voici la partie pertinente de ma méthode 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); 
} 

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

Expecting to find float on stack

Ok, cela fait sens, je dois faire moi-même unboxing. Je mis en œuvre les éléments suivants:

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:

Expecting to find object/array on stack

C'est là que je suis coincé. Pour la vie de moi, je ne peux pas comprendre pourquoi le déballage ne fonctionne pas. Le "ALOAD, 3" met le troisième paramètre de la méthode set sur la pile, qui devrait être un Float. Des idées?

J'ai trouvé la bibliothèque asm-commons a une classe GeneratorAdapter qui a une méthode unbox. Cependant, je ne veux pas vraiment inclure un autre JAR pour quelque chose qui devrait être si simple. J'ai regardé la source GeneratorAdapter et il fait quelque chose de très similaire. J'ai essayé de modifier mon code pour utiliser GeneratorAdapter, juste pour voir si cela fonctionnait, mais je ne l'ai pas trouvé du tout facile à convertir.

Répondre

7

Il s'avère que le déballage ci-dessus fonctionnait correctement. J'ai eu un code qui faisait un get et non boxe le résultat avant d'essayer de le renvoyer 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; 
} 
1

Utilisez GeneratorAdapter son doit plus propre que MethodVisitor et a un Unbox() qui iniserts le droit invoquer la méthode primitive.valueOf() appel.

+0

Merci, mais je ne voulais pas avoir de dépendance sur le JAR asm-commons supplémentaire nécessaire pour utiliser GeneratorAdapter. – NateS