java - 使用 ASM Java 库拆箱

标签 java bytecode java-bytecode-asm bytecode-manipulation

我正在使用 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);
}

我已经追查过了,它确实进入了相应字段的“case Type.FLOAT”,但是,我得到了这个错误:

Expecting to find object/array on stack

这就是我卡住的地方。对于我的生活,我无法弄清楚为什么拆箱不起作用。 “ALOAD, 3”是将set方法的第三个参数压入栈中,应该是一个Float。有什么想法吗?

我发现 asm-commons 库有一个 GeneratorAdapter 类,它有一个 unbox 方法。但是,我真的不想为应该如此简单的东西包含另一个 JAR。我查看了 GeneratorAdapter 源代码,它正在做一些非常相似的事情。我尝试修改我的代码以使用 GeneratorAdapter,只是为了看看它是否有效,但发现它根本不容易转换。

最佳答案

原来上面的拆箱工作正常。在尝试将结果作为对象返回之前,我有代码正在执行获取而不是装箱结果。没有更简单的测试是我的错!

如果其他人需要它,这里是正确的装箱代码:

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

关于java - 使用 ASM Java 库拆箱,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1566185/

相关文章:

java - 对多个对象的并发更新

java - 为什么 CF UAA check_token 端点需要 clientId/clientSecret?

java - Spring MVC-存储和检索 @PathVariable Map<String, String>

java - 如何使用java格式化来自excel的值

Java 类文件编辑器

java - 在不调用初始化程序的情况下创建对象实例

hadoop - 分析 Hadoop

java - JAVA ASM 中的类转换器 - 可以生成吗?

java - 调用使用 Java ASM 添加的方法时出现 ClassFormatError 错误

Lua 字节码到 Lua 人类 "readable"