gpt4 book ai didi

java - 为什么这段简单的代码会输出损坏的类文件?

转载 作者:行者123 更新时间:2023-12-02 01:52:02 27 4
gpt4 key购买 nike

ClassReader classReader = new ClassReader(new FileInputStream(new File("input.class")));
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
Files.write(Paths.get("output.class"), classWriter.toByteArray());

反编译output.class时我得到

package corrupted_class_files;

input.class 很好,我可以使用 ClassReader 来读取说明,只是无法保存类

最佳答案

您的代码缺少将类功能从源实际复制到目标的步骤:

try(FileInputStream in = new FileInputStream(new File("input.class")) {
ClassReader classReader = new ClassReader(in);
ClassWriter classWriter = new ClassWriter(classReader, 0);
classReader.accept(classWriter, 0);
Files.write(Paths.get("output.class"), classWriter.toByteArray());
}

ClassReader 传递给 ClassWriter 的构造函数不会复制功能,而是可以进行优化(如果您的转换保留了大部分原始类文件)。或者,如the documentation of ClassWriter(ClassReader classReader, int flags)说:

Constructs a new ClassWriter object and enables optimizations for "mostly add" bytecode transformations. These optimizations are the following:

  • The constant pool and bootstrap methods from the original class are copied as is in the new class, which saves time. New constant pool entries and new bootstrap methods will be added at the end if necessary, but unused constant pool entries or bootstrap methods won't be removed.
  • Methods that are not transformed are copied as is in the new class, directly from the original class bytecode (i.e. without emitting visit events for all the method instructions), which saves a lot of time. Untransformed methods are detected by the fact that the ClassReader receives MethodVisitor objects that come from a ClassWriter (and not from any other ClassVisitor instance).

因此,当您在 accept 方法中将 ClassWriter 直接链接到 ClassReader 时,所有方法访问者都将源自 writer,因此,都是直接复制的。

当您要对类进行重大更改或构造新类时,您可以使用构造函数 ClassWriter(int flags) 来代替。

请注意,COMPUTE_FRAMES 已经暗示了 COMPUTE_MAXS。在上面的示例中,我没有指定任何一个,因为无论如何都会复制方法。当您要实际更改或添加代码并需要 COMPUTE_FRAMES 时,值得向读者指定 SKIP_FRAMES,因为在重新计算原始帧时解码原始帧是没有意义的无论如何,从头开始。

因此,典型的转换设置如下所示:

public class MyClassVisitor extends ClassVisitor {

public MyClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor visitor = super.visitMethod(
access, name, desc, signature, exceptions);
if(method matches criteria) {
visitor = new MyMethodVisitorAdapter(visitor);
}
return visitor;
}
}
try(FileInputStream in = new FileInputStream(new File("input.class"))) {
ClassReader classReader = new ClassReader(in);
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);
classReader.accept(new MyClassVisitor(classWriter), ClassReader.SKIP_FRAMES);
Files.write(Paths.get("output.class"), classWriter.toByteArray());
}

当通过构造函数链接访问者时,您不重写的每个方法都会委托(delegate)给链接的访问者,当最终目标是 ClassWriter 时复制原始构造。由 ClassWriter 提供的 MethodVisitor。如果该方法不满足您的转换条件,因此您返回原始 MethodVisitor,则上述优化仍然适用。方法访问者遵循与类访问者相同的模式,覆盖您想要拦截的那些方法。

顺便说一句,您应该避免混合旧的 I/O 和 NIO。代码的简化变体如下所示

ClassReader classReader = new ClassReader(Files.readAllBytes(Paths.get("input.class")));
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);
classReader.accept(new MyClassVisitor(classWriter), ClassReader.SKIP_FRAMES);
Files.write(Paths.get("output.class"), classWriter.toByteArray());

注意读写之间的对称性

但是,当您使用getResource等时,您可能被迫处理InputStream。但对于可通过系统类加载器访问的类,您也可以将类名传递给 ClassReader(String) 构造函数。

关于java - 为什么这段简单的代码会输出损坏的类文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52824262/

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