0

I am building a Java agent that should manipulate bytecode using the ASM library. I need to add instructions at the beginning of the specific method. I managed to do that by creating agent following:

public class JavaAgent {

    public static void begin(String methodName) throws IOException {
        System.err.println("Begin");
    }

    public static void premain(String agentArgs, Instrumentation inst) throws FileNotFoundException, UnsupportedEncodingException {
        MyTransformer transformer = new MyTransformer();
        inst.addTransformer(transformer, true);
    }
public class MyTransformer implements ClassFileTransformer {

    public MyTransformer() {
        this.className = className;
        this.methodName = methodName;
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        if (className.equals("MyClass") {
            ClassReader classReader = new ClassReader(classfileBuffer);
            final ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
            classReader.accept(new MyVisitor(classWriter), ClassReader.EXPAND_FRAMES);
            return classWriter.toByteArray();
       }

       return classfileBuffer;
    }
}
public class MyVisitor extends ClassVisitor {
    public MyVisitor(ClassVisitor classVisitor) {
        super(Opcodes.ASM4, classVisitor);
        this.className = className;
        this.methodName = methodName;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc,
                                     String signature, String[] exceptions) {
        if (name.equals("myMethod")) {
            return new MyMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions), className, name, methodName);
        }
        return super.visitMethod(access, name, desc, signature, exceptions);
    }
}
public class MyMethodVisitor extends MethodVisitor {

    public MyMethodVisitor(MethodVisitor methodVisitor, String className, String methodName, String methodToVisit) {
        super(Opcodes.ASM4, methodVisitor);
    }

    @Override
    public void visitCode() {
        visitMethodInsn(Opcodes.INVOKESTATIC, "agent/JavaAgent", "begin", "(Ljava/lang/String;)V", false);
        super.visitCode();
    }
}

This works perfectly with Java in any case, but I have some strange problems in Scala. If I have the following code in Scala everything works and I get a printed message from move method

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   
   def move() {
      println ("Point x location : " + x);
      println ("Point y location : " + y);
   }
}

object Demo {
   def main(args: Array[String]) {
      val pt = new Point(10, 20);

      // Move to a new location
      pt.move();
   }
}

But if I add arguments to move method I get nothing.

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   
   def move(xd : Int) {
      println ("Point x location : " + x);
      println ("Point y location : " + y);
   }
}

object Demo {
   def main(args: Array[String]) {
      val pt = new Point(10, 20);

      // Move to a new location
      pt.move(2);
   }
}

Of course, in the Agent I have class Point and method move specified. Does anyone have an idea what the problem could be?

NO ONE
  • 33
  • 6
  • 1
    As a side note, there is no sense in combining `COMPUTE_FRAMES` with `EXPAND_FRAMES`. You are requesting ASM to do extra work to expand frames, just to drop the resulting frame information as you requested (re-)calculating frames from scratch via `COMPUTE_FRAMES`. If you don’t use the old frame information, `SKIP_FRAMES` would be more useful. But when you just insert simple logging calls, the branches are entirely unaffected. So using `0` for `accept` on the reader and just `COMPUTE_MAXS` for the writer, to keep the original frames in their original form would be sufficient and more efficient. – Holger Jul 07 '22 at 15:05

1 Answers1

1

I have found the issue. I was using ASM4. In jdk.internal.org.objectweb.asm.MethodVisitor#visitParameter, there is a condition:

if (api < Opcodes.ASM5) {
   throw new UnsupportedOperationException(REQUIRES_ASM5);
}

and that was the cause of the issue.

NO ONE
  • 33
  • 6