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