Jacob van Lingen
Jacob van Lingen

Reputation: 9537

Use java agent with manipulated java bytecode (ASM)

I am trying to figure out how ASM works, with the help of A Guide to Java Bytecode Manipulation with ASM. I followed the tutorial and created an 'extra' static field for the Integer class. Here is the simplified logic of the Instrumentation class:

public static void premain(String agentArgs, Instrumentation inst) {
  inst.addTransformer(new ClassFileTransformer() {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, final byte[] buffer) {
      if (className.equals("ClassX")) {
        var reader = new ClassReader(bytes);
        var writer = new ClassWriter(reader, 0);
        var visitor = new ClassVisitor() { // simplified
          @Override
          public void visitEnd() {
            if (!isFieldPresent) {
              var fv = cv.visitField(ACC_PUBLIC + ACC_STATIC, "aNewBooleanField", BOOLEAN_TYPE.toString(), null, true);
              if (fv != null) fv.visitEnd();
            }
            cv.visitEnd();
          }
        };
        reader.accept(visitor, 0);
        return writer.toByteArray();
      }
      return buffer;
    }
  });
}

I packaged the ASM code with maven; now I am trying the run it, but I got no clue how to use it. I expected I should be able to to something like this:

public static void main(String[] args) {
    System.out.println(ClassX.aNewBooleanField);
}

This won't compile of course, as the compiler is not aware of the extra field. I tried adding the java agent¹ as the tutorial suggest, but I don't understand that either. How can the compiler be aware of a process (the agent) that operates at runtime?

So bottom line question would be, how do I use the ASM manipulated code?


¹ -javaagent:"/<path>/.m2/repository/org/example/try-asm/1.0-SNAPSHOT/try-asm-1.0-SNAPSHOT.jar"

Upvotes: 0

Views: 308

Answers (1)

Jacob van Lingen
Jacob van Lingen

Reputation: 9537

As stated in the question, the compiler is indeed not aware of the modifications made to the bytecode of the class. Therefore, you can't call ClassX.aNewBooleanField directly. Thus you need to fall back to reflection:

var modifiedClass = Class.forName("ClassX");
var field = modifiedClass.getField("aNewBooleanField");
System.out.println(field.getBoolean(null));

As you can compile your code now, you can run it with the java agent and above code will print out the true value!

Upvotes: 0

Related Questions