gpt4 book ai didi

java - 如何使用 ASM 将 boolean 字段替换为常量值

转载 作者:行者123 更新时间:2023-12-01 12:24:08 25 4
gpt4 key购买 nike

我想将类的字段转换为有效的常量。我正在使用 ASM 5.0.3。

这是我的测试类:

public class BytecodeUtilsTest {

@BeforeClass
public static void beforeClass(){
String replaceFieldClassName = "com.mypackage.ClassWithFieldToReplaceWithConstant";
String replaceFieldClassNameAsPath = replaceFieldClassName.replace('.', '/') + ".class";
// standard code to redefine class (inspired by ASM FAQ http://asm.ow2.org/doc/faq.html, sec. 5)
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
InputStream stream = contextClassLoader.getResourceAsStream(replaceFieldClassNameAsPath);
byte[] classBytes;
try {
classBytes = IOUtils.toByteArray(stream);

// here is the interesting part
byte[] patchedClassBytes = BytecodeUtils.patch(classBytes, "_fieldToReplace", true);

Reflection.invoke(contextClassLoader, "defineClass", Class.class,
new Class[]{String.class, byte[].class, int.class, int.class},
new Object[]{replaceFieldClassName, patchedClassBytes, 0, patchedClassBytes.length});
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Test
public void testFieldReplace(){
Assert.assertTrue(new ClassWithFieldToReplaceWithConstant().getFieldToReplace());
Assert.assertTrue(new ClassWithFieldToReplaceWithConstant()._fieldToReplace);
}
}

这是要更新的测试类:

public class ClassWithFieldToReplaceWithConstant {
boolean _fieldToReplace;

public boolean getFieldToReplace() {
return _fieldToReplace;
}
}

这是修补程序:

public class BytecodeUtils {
public static byte[] patch(byte[] bytecode, final String fieldToReplace, final boolean value) {
ClassReader classReader = new ClassReader(bytecode);
final ClassWriter classWriter = new ClassWriter(classReader, 0);
ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM4, classWriter) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM4, super.visitMethod(access, name, desc, signature, exceptions)) {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (opcode == Opcodes.GETFIELD && name.equals(fieldToReplace)) {
mv.visitInsn(Opcodes.POP);
mv.visitInsn(value ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
} else {
super.visitFieldInsn(opcode, owner, name, desc);
}
}
};
}
};
classReader.accept(classVisitor, 0);
return classWriter.toByteArray();
}
}

问题是第二个断言测试失败。因此,如果我使用 getter,它会按预期返回 true,但如果我直接读取字段,它会返回 false。考虑到 getter 产生 INVOKEVIRTUAL 指令和字段读取产生 GETFIELD 并通过调用 visitInsn 方法进行更新,这是非常出乎意料的。

我做错了什么以及如何使直接字段访问返回true

最佳答案

要使第二个断言起作用,您需要修补 BytecodeUtilsTest,而不是 ClassWithFieldToReplaceWithConstant,因为读取第二种情况的字段的字节码指令实际上位于 BytecodeUtilsTest#testFieldReplace 方法中。

Getter 大小写工作正常,因为读取字段的指令位于 getter 主体内部,即 ClassWithFieldToReplaceWithConstant 类内部。

如果在实际场景中字段是私有(private)的,则此代码应该没问题(因为将无法从声明字段的类外部访问该字段)。否则,您将必须修补读取或写入此字段的每个类。

关于java - 如何使用 ASM 将 boolean 字段替换为常量值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26478295/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com