The obfuscation results for an (anonymous) inner class that extends a generically typed class/interface are incompatible with Java reflection utilities.
This seems to be caused by an (anonymous) inner class of OUTERTYPE not being represented as OUTERTYPE$1 after obfuscation (but as an obfuscated, independently named type) such that the Java reflection utilities fail to resolve a generic type that is defined by the outer type.
How to reproduce
- Create a class with an inner class that uses a generic type defined in the outer class. Issues especially arise when the generic type parameter is used to parameterize a supertype of that inner class. The following creates an inner class with super interface
Supplier<T>:
public class Type {
public <T> void test(T input) {
new Supplier<T>() {
@Override
public T get() {
return input;
}
}.get();
}
}
- Run ProGuard with obfuscation and ensure that attribute
Signature is kept (via -keepattributes Signature, this is important as otherwise reflection will not have the original signature with generic types available anymore)
- Call
getGenericInterfaces() on the inner type, e.g., via the following main method:
public static void main(String[] args) {
new Type().test("");
var superInterface = Type.class.getNestMembers()[1].getGenericInterfaces()[0];
}
With Java 21 and below, this code just assignes an invalid ParameterizedType instance to superInterface (having an unresolved generic type "null"). This can be seen when trying to print the type name of that parameterized type:
System.out.println(((ParameterizedType) superInterface).getTypeName());
It produces:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.reflect.Type.getTypeName()" because "t" is null
at java.base/sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.toString(ParameterizedTypeImpl.java:236)
at java.base/java.lang.reflect.Type.getTypeName(Type.java:54)
at test.Main.main(Unknown Source)
When using the type without accessing the generic type, no error occurred at all (which is the reason why we did not experience the issue before switing to Java 25, as that's the use case we had due to reflection performed by HK2).
With Java 25, the issue becomes more severe (and also affects our use case), because getGenericInterfaces() now already fails due to an added check in the CoreReflectionFactory of the JDK:
openjdk/jdk@1d070a3#diff-8e4042af6c50a9ad11d179d224feb250cebcf8298cbd07029a9d5feee47fe567
When just executing the above posted main method, the following exception occurs:
Exception in thread "main" java.lang.TypeNotPresentException: Type T not present
at java.base/sun.reflect.generics.factory.CoreReflectionFactory.findTypeVariable(CoreReflectionFactory.java:111)
at java.base/sun.reflect.generics.visitor.Reifier.visitTypeVariableSignature(Reifier.java:165)
at java.base/sun.reflect.generics.tree.TypeVariableSignature.accept(TypeVariableSignature.java:43)
at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at java.base/sun.reflect.generics.repository.ClassRepository.computeSuperInterfaces(ClassRepository.java:117)
at java.base/sun.reflect.generics.repository.ClassRepository.getSuperInterfaces(ClassRepository.java:95)
at java.base/java.lang.Class.getGenericInterfaces(Class.java:1278)
at test.Main.main(Unknown Source)
Version Info
Tested with ProGuard 7.8.2 (and also with 7.4.2).
Issue is present with probably every Java version but became more severe with change in Java 25.
JDK used for testing was: Eclipse Temuring 25.0.2.10
The obfuscation results for an (anonymous) inner class that extends a generically typed class/interface are incompatible with Java reflection utilities.
This seems to be caused by an (anonymous) inner class of
OUTERTYPEnot being represented asOUTERTYPE$1after obfuscation (but as an obfuscated, independently named type) such that the Java reflection utilities fail to resolve a generic type that is defined by the outer type.How to reproduce
Supplier<T>:Signatureis kept (via-keepattributes Signature, this is important as otherwise reflection will not have the original signature with generic types available anymore)getGenericInterfaces()on the inner type, e.g., via the followingmainmethod:With Java 21 and below, this code just assignes an invalid
ParameterizedTypeinstance tosuperInterface(having an unresolved generic type "null"). This can be seen when trying to print the type name of that parameterized type:It produces:
When using the type without accessing the generic type, no error occurred at all (which is the reason why we did not experience the issue before switing to Java 25, as that's the use case we had due to reflection performed by HK2).
With Java 25, the issue becomes more severe (and also affects our use case), because
getGenericInterfaces()now already fails due to an added check in theCoreReflectionFactoryof the JDK:openjdk/jdk@1d070a3#diff-8e4042af6c50a9ad11d179d224feb250cebcf8298cbd07029a9d5feee47fe567
When just executing the above posted
mainmethod, the following exception occurs:Version Info
Tested with ProGuard 7.8.2 (and also with 7.4.2).
Issue is present with probably every Java version but became more severe with change in Java 25.
JDK used for testing was: Eclipse Temuring 25.0.2.10