smallrye / jandex

Java Annotation Indexer

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Provide a way to get method parameter modifiers

kaqqao opened this issue · comments

There seems to be no way to get modifiers for method parameters, equivalent to java.lang.reflect.Parameter#getModifiers.
In general, there seems to be no way to get info on parameters beyond the name and type...
I'm specifically trying to figure out if a parameter is implicit (java.lang.reflect.Parameter#isImplicit) or synthetic (java.lang.reflect.Parameter#isSynthetic).

There's been some work to handle synthetic parameters better (see #45 and #48), but indeed that hasn't been finished yet.

I'm looking into this and I think I know why parameter flags are not exposed... They are almost never present in bytecode in the first place. They are only present in bytecode when javac is invoked with the -parameters option. This is also why the java.lang.reflect.Parameter methods you mention are often unreliable.

I'm aware the -parameters flag is required to keep names in the bytecode, but Javadoc for e.g. isImplicit() mentions nothing about this info potentially not being available. For contrast, the docs for getName() clearly state the name may be synthesized. My own experiments seem to corroborate that the modifiers and attributes are independent of the compilation options, but I really can't be sure.

Either way, I use -parameters in my own projects, so support for that would still be welcome. But if really no useful info is available without -parameters, I'd understand if you decided it isn't worth the effort.

At the moment, I'm leaning towards not exposing the parameter flags, but I need to investigate synthetic parameters more. I'll update this issue when done.

I have a nearly finished PR for handling mandated/synthetic parameters uniformly: #192. The final decision is for the most common parameter-related methods on MethodInfo to pretend that mandated and synthetic parameters don't exist [1]. Interestingly, this is also how parameter names and annotations are indexed in bytecode. There will be a way to access the full list of types as present in the method descriptor, but it won't be possible to correlate those types with parameter names or annotations.

[1] This is actually surprisingly easy, because Jandex has already been doing that for methods that have the Signature attribute in bytecode. So for methods that use type variables in their signature, this is already how Jandex behaves. Note that javac emits the Signature attribute in other situations, too, e.g. for constructors of local/anonymous classes. In case the method doesn't have the Signature attribute, Jandex will employ a simple heuristic that covers the most important cases: constructors of enums and non-static member classes. This won't work perfectly for constructors of local/anonymous classes compiled with ecj, but I believe that's acceptable. See also #45 (comment).

Hence, closing this.

BTW, here's a simple test class to verify whether isImplicit and isSynthetic work:

public class Test {
  public enum MyEnum { INSTANCE }

  public class Inner {
    public Inner(int ignored) {
    }
  }

  public static void main(String[] args) {
    System.out.println(MyEnum.class.getDeclaredConstructors()[0]);
    System.out.println(MyEnum.class.getDeclaredConstructors()[0].getParameters()[0]);
    System.out.println(MyEnum.class.getDeclaredConstructors()[0].getParameters()[0].isImplicit());
    System.out.println(MyEnum.class.getDeclaredConstructors()[0].getParameters()[0].isSynthetic());

    Inner inner = new Test().new Inner(42);
    System.out.println(inner.getClass().getDeclaredConstructors()[0]);
    System.out.println(inner.getClass().getDeclaredConstructors()[0].getParameters()[0]);
    System.out.println(inner.getClass().getDeclaredConstructors()[0].getParameters()[0].isImplicit());
    System.out.println(inner.getClass().getDeclaredConstructors()[0].getParameters()[0].isSynthetic());
  }
}

When compiled without -parameters, this program prints:

private Test$MyEnum(java.lang.String,int)
java.lang.String arg0
false
false
public Test$Inner(Test,int)
Test arg0
false
false

When compiled with -parameters, this program prints:

private Test$MyEnum(java.lang.String,int)
 java.lang.String $enum$name
false
true
public Test$Inner(Test,int)
final Test this$0
true
false

So, really, isImplicit and isSynthetic only work when compiled with -parameters.

For historical reference, JDK 21 fixes the bug that Parameter.isImplicit() and isSynthetic() only work when compiled with -parameters: https://bugs.openjdk.org/browse/JDK-8292275