2009-10-14 62 views
7

我正在使用ASM Java庫替換一些反射。我生成該方法的主體:使用ASM Java庫進行拆箱

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

利用這種方法產生的,我可以在運行時在對象上設置的字段,而無需使用反射。它效果很好。但是,我發現原始字段失敗。以下是我設置方法的相關部分:

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

此代碼爲選擇生成案例標籤。它適用於物體,但對於原始圖像我得到這個錯誤:

Expecting to find float on stack

好的,這是有道理的,我需要做拆箱自己。我實現了以下內容:

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

我已經通過追查,它肯定會進入「案例Type.FLOAT」爲適當的領域,但是,我得到這個錯誤:

Expecting to find object/array on stack

這是我卡住了。對於我的生活,我無法弄清楚爲什麼拆箱不起作用。 「ALOAD,3」將set方法的第三個參數放在堆棧上,應該是Float。有任何想法嗎?

我發現asm-commons庫有一個具有unbox方法的GeneratorAdapter類。但是,我並不想真的想再包括另一個JAR來實現那麼簡單的事情。我查看了GeneratorAdapter的源代碼,它正在做一些非常相似的事情。我試圖修改我的代碼來使用GeneratorAdapter,只是爲了看看它是否工作,但沒有發現它很容易轉換。

回答

7

原來上面的拆箱工作正常。我有代碼做了一個get而不是在將結果作爲Object返回之前對結果進行裝箱。沒有簡單的測試就是我的錯!

如果別人需要它,這裏是拳擊的正確代碼:

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

使用GeneratorAdapter它必須比MethodVisitor中清潔並具有iniserts正確調用primitive.valueOf()方法的拆箱()呼叫。

+0

謝謝,但我不想依賴額外的asm-commons JAR需要使用GeneratorAdapter。 – NateS 2011-02-09 07:42:40