Search code examples
javabyte-buddy

How to get a Java method's parameter's actual name from a MethodDescription with Byte Buddy?


Inspired by this article http://mydailyjava.blogspot.com/2022/02/using-byte-buddy-for-proxy-creation.html, I managed to intercept a method invocation, check all the method parameter's values, and return a mock response.

However, I cannot get the parameters' actual names (as in source code). I can only get names such as "arg0".

The best effort I made is, in a method matcher, I have access to the method's MethodDescription, and I can get the parameter's ParameterDescription.

@Override
public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
    logger.info("OiiPlugin processing "+typeDescription);
    return builder
            .method(new ElementMatcher<MethodDescription>() {
                @Override
                public boolean matches(MethodDescription target) {
                    boolean isTarget = target.getDeclaredAnnotations().isAnnotationPresent(MockMe.class);
                    if (isTarget) {
                        ParameterDescription p = target.getParameters().get(0);
                        logger.info(
                                "Parameter 0 NAME:"+p.getActualName()+":"+p.getName()+":"+p.getInternalName());
                    };
                    return isTarget;
                }
            })
            .intercept(MethodDelegation.to(
                    OiiInterceptor.class
            ));
}

However, its getActualName() returns empty string.

[INFO] OiiPlugin processing class com.example.research.oii.client.ExampleClient
[INFO] Parameter 0 NAME::arg0:arg0

I did add javac -g:vars parameter through maven plugin config, to instruct javac put parameter names into the .class files.

    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.8.0</version>
      <configuration>
        <compilerArgs>
          <arg>-g:vars</arg>
        </compilerArgs>
      </configuration>
    </plugin>

What else can I do?

Thanks!


Post Note:

Thanks @boneill for the answer. After using -parameters with compiler:

  1. The TypeDescription API is able to get the actual name
  2. The Java reflection API can also get the actual name, with a catch - the parameter names can only be obtained from the Method annotated with @Origin, not from the Method annotated with @SuperMethod
    @RuntimeType
    public static Object intercept(
            @This Object self,
            @Origin Method method, 
            @AllArguments Object[] args,
            @SuperMethod Method superMethod 
    ) throws Throwable {
        // need to get parameter name from @Origin method, not @SuperMethod
        Parameter[] parameters = method.getParameters(); 
        Object[] mockInterceptionParameters = new Object[parameters.length*2];
        for (int i=0; i<parameters.length; i++) {
            Parameter p = parameters[i];
            mockInterceptionParameters[i*2] = p.getName();
            mockInterceptionParameters[i*2+1] = args[i];
        }
        .... ....
    }

Solution

  • You need to pass the -parameters argument to the java compiler. The -g:vars argument provides information regarding all local variables, but the attribute which defines this is never accessible using the reflection API. When using -parameters, a special attribute is used which is explicitly designed for being accessed by the reflection API.