如何使用ASM 4.0修改Java字节码 - java

我是ASM框架的新手。我一直在围绕这个ASM框架工作一周。我在网上看到了有关解析类和从头开始生成.class文件的教程。
但是无法遵循如何在ASM中修改现有类。

我无法跟踪ClassVisitorClassWriterClassReader之间的执行流程。

请给我一个以下代码的ASM示例,以解决我的问题。

public class ClassName {
  public void showOne() {
    System.out.println("Show One Method");
  }

  public static void main(String[] args) {
    ClassName c = new ClassName();
    c.showOne();
  }
}

上面的类应修改为:

public class ClassName {
  public void showOne() {
    System.out.println("Show One Method");
  }

  public void showTwo() { // <- Newly added method
    System.out.println("Show Two Method");
  }

  public static void main(String[] args) {
    ClassName c = new ClassName();
    c.showOne();
    c.showTwo(); // <- Newly inserted method call
  }
}

修改它应该是什么ASM代码?

我使用了ASMifier工具来生成代码。但是我不知道在哪里使用它。

参考方案

您的要求有些不足。下面是一个示例程序,该程序使用ASM的visitor API将假定具有问题结构的类转换为结果类。我添加了一个方便的方法,该方法采用一个字节数组并返回一个字节数组。在两种情况下都可以使用这种方法,将静态转换应用于磁盘上以及工具代理中的类文件。

当将ClassWriter与传递给ClassVisitorClassReader组合如下时,它将自动复制源类的每个功能,因此您仅需覆盖要应用更改的这些方法。

在这里,当遇到visitMethod方法对其进行修改时,main被覆盖以进行拦截,而visitEnd被覆盖以附加全新的showTwo方法。 MainTransformer将截获RETURN指令(在您的示例中应该只有一个),以便在其之前插入对showTwo的调用。

import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;

public class MyTransformer extends ClassVisitor {

  public static byte[] transform(byte[] b) {
    final ClassReader classReader = new ClassReader(b);
    final ClassWriter cw = new ClassWriter(classReader,
      ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
    classReader.accept(new MyTransformer(cw), ClassReader.EXPAND_FRAMES);
    return cw.toByteArray();
  }

  public MyTransformer(ClassVisitor cv) {
    super(Opcodes.ASM5, cv);
  }
  @Override
  public MethodVisitor visitMethod(int access, String name, String desc,
      String signature, String[] exceptions) {

    MethodVisitor v=super.visitMethod(access, name, desc, signature, exceptions);
    if(name.equals("main") && desc.equals("([Ljava/lang/String;)V"))
      v=new MainTransformer(v, access, name, desc, signature, exceptions);
    return v;
  }
  @Override
  public void visitEnd() {
    appendShowTwo();
    super.visitEnd();
  }
  private void appendShowTwo() {
    final MethodVisitor defVisitor = super.visitMethod(
      Opcodes.ACC_PUBLIC, "showTwo", "()V", null, null);
    defVisitor.visitCode();
    defVisitor.visitFieldInsn(Opcodes.GETSTATIC,
      "java/lang/System", "out", "Ljava/io/PrintStream;");
    defVisitor.visitLdcInsn("Show Two Method");
    defVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
      "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    defVisitor.visitInsn(Opcodes.RETURN);
    defVisitor.visitMaxs(0, 0);
    defVisitor.visitEnd();
  }
  class MainTransformer extends GeneratorAdapter
  {
    MainTransformer(MethodVisitor delegate, int access, String name, String desc,
        String signature, String[] exceptions) {
      super(Opcodes.ASM5, delegate, access, name, desc);
    }
    @Override
    public void visitInsn(int opcode) {
      if(opcode==Opcodes.RETURN) {
        // before return insert c.showTwo();
        super.visitVarInsn(Opcodes.ALOAD, 1); // variable c
        super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
            "ClassName", "showTwo", "()V", false);
      }
      super.visitInsn(opcode);
    }
  }
}

Java-搜索字符串数组中的字符串 - java

在Java中,我们是否有任何方法可以发现特定字符串是字符串数组的一部分。我可以避免出现一个循环。例如String [] array = {"AA","BB","CC" }; string x = "BB" 我想要一个if (some condition to tell wheth…

Java Scanner读取文件的奇怪行为 - java

因此,在使用Scanner类从文件读取内容时,我遇到了一个有趣的问题。基本上,我试图从目录中读取解析应用程序生成的多个输出文件,以计算一些准确性指标。基本上,我的代码只是遍历目录中的每个文件,并使用扫描仪将其打开以处理内容。无论出于何种原因,扫描程序都不会读取其中的一些文件(所有UTF-8编码)。即使文件不是空的,scanner.hasNextLine()在…

Java Globbing模式以匹配目录和文件 - java

我正在使用递归函数遍历根目录下的文件。我只想提取*.txt文件,但不想排除目录。现在,我的代码如下所示:val stream = Files.newDirectoryStream(head, "*.txt") 但是这样做将不会匹配任何目录,并且返回的iterator()是False。我使用的是Mac,所以我不想包含的噪音文件是.DS_ST…

Java RegEx中的单词边界\ b - java

我在使用\b作为Java Regex中的单词定界符时遇到困难。对于text = "/* sql statement */ INSERT INTO someTable"; Pattern.compile("(?i)\binsert\b");找不到匹配项Pattern insPtrn = Pattern.compile(&…

Java Double与BigDecimal - java

我正在查看一些使用双精度变量来存储(360-359.9998779296875)结果为0.0001220703125的代码。 double变量将其存储为-1.220703125E-4。当我使用BigDecimal时,其存储为0.0001220703125。为什么将它双重存储为-1.220703125E-4? 参考方案 我不会在这里提及精度问题,而只会提及数字…