Domanda

Sto usando la libreria ASM Java per sostituire qualche riflessione. Ho generare il corpo di questo metodo:

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

Con questo metodo generato, è possibile impostare campi su un oggetto in fase di esecuzione senza l'utilizzo di riflessione. E le grandi opere. Tuttavia, ho trovato che non riesce per i campi primitivi. Ecco la parte rilevante del mio metodo 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);
}
caso

Questo codice genera etichette per un prescelto. E le grandi opere per gli oggetti, ma per primitive ottengo questo errore:

  

In attesa di trovare galleggiante sulla pila

Ok, che abbia un senso, ho bisogno di fare l'unboxing me stesso. Ho implementato il seguente:

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

Ho tracciato attraverso e va definitivamente in "caso Type.FLOAT" per il campo appropriato, tuttavia, ottengo questo errore:

  

In attesa di trovare l'oggetto / array sulla pila

Questo è dove mi sono bloccato. Per la vita di me non riesco a capire il motivo per cui l'unboxing non funziona. Il "ALOAD, 3" è mettere il terzo parametro del metodo set sulla pila, che dovrebbe essere un galleggiante. Tutte le idee?

Ho trovato la Biblioteca ASM-commons ha una classe GeneratorAdapter che ha un metodo Unbox. Tuttavia, non ho molta voglia di includere un altro JAR per qualcosa che dovrebbe essere così semplice. Ho guardato la fonte GeneratorAdapter e lo sta facendo qualcosa di molto simile. Ho provato a modificare il mio codice per utilizzare GeneratorAdapter, solo per vedere se ha funzionato, ma non ha trovato affatto facile per convertire.

È stato utile?

Soluzione

Si scopre che l'unboxing sopra funzionava correttamente. Ho avuto il codice che stava facendo un get e non boxe il risultato prima di provare a restituirlo come un oggetto. La mia colpa per non avere un test più semplice!

Nel caso in cui qualcun altro ne ha bisogno, ecco il codice appropriato per 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;
}

Altri suggerimenti

Usa GeneratorAdapter suo dovere più pulita rispetto MethodVisitor ed ha un'Unbox (), che iniserts destra richiamare primitive.valueOf () chiamata di metodo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top