Lucas
Lucas

Reputation: 13

How can I replace a static field access with a new generate method?

I encountered a problem while using Byte Buddy (a java agent lib) to replace a field, For example, the following example code:

public class A {
    public static String foo;

    public String originalMethod() {
        return foo; // 'return dynamicGenerateMethod()' after replaced
    }

    public String dynamicGenerateMethod() {
        return "bar";
    }
}

When I use MemberSubstitution to replace static field A.foo access with with a new generate method:

DynamicType.Unloaded<?> make = new ByteBuddy()
        .redefine(A.class)
        .defineMethod("dynamicGenerateMethod", String.class, Opcodes.ACC_PUBLIC)
        .intercept(FieldAccessor.ofField("foo"))
        .visit(MemberSubstitution.relaxed()
                .field(named("foo"))
                .onRead()
                .replaceWithMethod(named("dynamicGenerateMethod"))
                .on(not(named("dynamicGenerateMethod"))))
        .make();

An exception is thrown:

java.lang.IllegalStateException: Cannot substitute parameterless instruction with public static java.lang.String com.test.A.foo
    at net.bytebuddy.asm.MemberSubstitution$Substitution$ForMethodInvocation$MethodResolver$Matching.resolve(MemberSubstitution.java:1359)

If we remove the static decoration of foo, everything is normal.

I want to know if static variables cannot be replaced? Or is there a problem with my usage?

Upvotes: 1

Views: 79

Answers (1)

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44032

replaceWithMethod looks for a non-static method that is declared by the instrumented class. If you want to locate a static method, specify it directly.

Otherwise, as foo is static, you would need to specify a target manually as the parameters to the field read do not match. You can use a replaceWithChain for this where you could insert a byte code instruction to load this and then invoke a method on it:

DynamicType.Unloaded<?> make = new ByteBuddy()
  .redefine(A.class)
  .defineMethod("x", String.class, Opcodes.ACC_PUBLIC)
  .intercept(FieldAccessor.ofField("foo"))
  .visit(MemberSubstitution.relaxed()
    .field(named("foo"))
    .onRead()
    .replaceWithChain(
      new MemberSubstitution.Substitution.Chain.Step.Simple(
        MethodVariableAccess.loadThis(),
        A.class
      ),
      new MemberSubstitution.Substitution.Chain.Step.ForInvocation.Factory(
        A.class.getMethod("dynamicGenerateMethod")
      )
    )
    .on(named("originalMethod")))
  .make();

Upvotes: 0

Related Questions