8

一部のリフレクションを置き換えるためにASMJavaライブラリを使用しています。このメソッドの本体を生成します:

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

この生成されたメソッドを使用すると、リフレクションを使用せずに実行時にオブジェクトにフィールドを設定できます。それは素晴らしい働きをします。ただし、プリミティブフィールドでは失敗することがわかりました。これが私の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);
}

このコードは、selectのケースラベルを生成しています。オブジェクトには最適ですが、プリミティブの場合、次のエラーが発生します。

スタック上でフロートを見つけることを期待しています

わかりました、それは理にかなっています、私は自分で開箱をする必要があります。私は以下を実装しました:

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

トレースして、適切なフィールドの「case Type.FLOAT」に間違いなく入りますが、次のエラーが発生します。

スタック上でオブジェクト/配列を見つけることを期待しています

これは私が立ち往生しているところです。私の人生では、開開が機能しない理由を理解できません。「ALOAD、3」は、setメソッドの3番目のパラメーターをスタックに配置します。これはFloatである必要があります。何か案は?

asm-commonsライブラリには、unboxメソッドを持つGeneratorAdapterクラスがあることがわかりました。ただし、これほど単純なもののために、さらに別のJARを含めたくはありません。GeneratorAdapterソースを調べたところ、非常によく似た処理を行っています。GeneratorAdapterを使用するようにコードを変更して、機能するかどうかを確認しようとしましたが、変換が簡単ではありませんでした。

4

2 に答える 2

10

上記の開梱は正しく機能していたことがわかりました。オブジェクトとして返そうとする前に、getを実行し、結果をボックス化しないコードがありました。より簡単なテストがなかったのは私のせいです!

他の誰かがそれを必要とする場合に備えて、ボクシングの適切なコードは次のとおりです。

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;
}
于 2009-10-14T22:48:06.977 に答える
1

GeneratorAdapterを使用して、MethodVisitorよりもクリーンにする必要があり、unbox()を使用して、primitive.valueOf()メソッド呼び出しを呼び出す権利を挿入します。

于 2010-02-10T08:48:32.353 に答える