-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
17
-
generic
-
generic
A DESCRIPTION OF THE PROBLEM :
Some JVM languages (e.g. Kotlin) have syntaxes that allow using the full range of valid characters in an Identifier (JVMS §4.2.2), which includes whitespace. Despite these signatures being valid (JVMS §4.7.9.1), attempting to use them with certain reflective operations (e.g. getting the generic superclass) results in a `java.lang.reflect.GenericSignatureFormatError` being thrown.
The location this first cropped up was with a local class in a Kotlin unit test method that included spaces in its name. The local class's internal name includes the spaces from the method name (e.g. `SomeTests$method with spaces$X`), which led to this issue.
When debugging the issue in Java 8 I believe I managed to track it down to `sun.reflect.generics.parser.SignatureParser.parseIdentifier()`. When testing in Java 17 that method has been rewritten, and the bug seems to have been moved to `sun.reflect.generics.parser.SignatureParser.skipIdentifier()`.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a class that includes spaces in its name (e.g. `Bad Name`)
2. Create another class (e.g. `BadSuper`)
3. Give `BadSuper` a superclass which has `Bad Name` as a type argument (e.g. `ArrayList<Bad Name>`)
4. Attempt to get the generic superclass of `BadSuper` using `Class.getGenericSuperclass()`
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
`Class.getGenericSuperclass()` will return a parameterized type with `Bad Name` as the type argument
ACTUAL -
`Class.getGenericSuperclass()` throws `java.lang.reflect.GenericSignatureFormatError`
```
Exception in thread "main" java.lang.reflect.GenericSignatureFormatError: Signature Parse error: expected '<' or ';' but got
Remaining input: Name;>;
at sun.reflect.generics.parser.SignatureParser.error(SignatureParser.java:124)
at sun.reflect.generics.parser.SignatureParser.parsePackageNameAndSimpleClassTypeSignature(SignatureParser.java:348)
at sun.reflect.generics.parser.SignatureParser.parseClassTypeSignature(SignatureParser.java:310)
at sun.reflect.generics.parser.SignatureParser.parseFieldTypeSignature(SignatureParser.java:289)
at sun.reflect.generics.parser.SignatureParser.parseFieldTypeSignature(SignatureParser.java:283)
at sun.reflect.generics.parser.SignatureParser.parseTypeArgument(SignatureParser.java:436)
at sun.reflect.generics.parser.SignatureParser.parseTypeArguments(SignatureParser.java:396)
at sun.reflect.generics.parser.SignatureParser.parsePackageNameAndSimpleClassTypeSignature(SignatureParser.java:346)
at sun.reflect.generics.parser.SignatureParser.parseClassTypeSignature(SignatureParser.java:310)
at sun.reflect.generics.parser.SignatureParser.parseClassSignature(SignatureParser.java:213)
at sun.reflect.generics.parser.SignatureParser.parseClassSig(SignatureParser.java:156)
at sun.reflect.generics.repository.ClassRepository.parse(ClassRepository.java:57)
at sun.reflect.generics.repository.ClassRepository.parse(ClassRepository.java:41)
at sun.reflect.generics.repository.AbstractRepository.<init>(AbstractRepository.java:74)
at sun.reflect.generics.repository.GenericDeclRepository.<init>(GenericDeclRepository.java:49)
at sun.reflect.generics.repository.ClassRepository.<init>(ClassRepository.java:53)
at sun.reflect.generics.repository.ClassRepository.make(ClassRepository.java:70)
at java.lang.Class.getGenericInfo(Class.java:2548)
at java.lang.Class.getGenericSuperclass(Class.java:765)
at BadMain.main(BadMain.java:3)
```
---------- BEGIN SOURCE ----------
// I've created a pre-built jar containing this test case so you don't have to fuss with the Kotlin compiler (necessary to put spaces in the class name):
// https://gist.github.com/thecodewarrior/0b23b6ee1c5fddb18f156c20eddd8ec1
// Bad.kt:
class `Bad Name`
class BadSuper: ArrayList<`Bad Name`>()
/* approximate Java equivalent:
public class `Bad Name` {}
public class BadSuper extends ArrayList<`Bad Name`> {}
*/
// BadMain.java:
public class BadMain {
public static void main(String args[]) {
BadSuper.class.getGenericSuperclass();
}
}
---------- END SOURCE ----------
FREQUENCY : always
Some JVM languages (e.g. Kotlin) have syntaxes that allow using the full range of valid characters in an Identifier (JVMS §4.2.2), which includes whitespace. Despite these signatures being valid (JVMS §4.7.9.1), attempting to use them with certain reflective operations (e.g. getting the generic superclass) results in a `java.lang.reflect.GenericSignatureFormatError` being thrown.
The location this first cropped up was with a local class in a Kotlin unit test method that included spaces in its name. The local class's internal name includes the spaces from the method name (e.g. `SomeTests$method with spaces$X`), which led to this issue.
When debugging the issue in Java 8 I believe I managed to track it down to `sun.reflect.generics.parser.SignatureParser.parseIdentifier()`. When testing in Java 17 that method has been rewritten, and the bug seems to have been moved to `sun.reflect.generics.parser.SignatureParser.skipIdentifier()`.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a class that includes spaces in its name (e.g. `Bad Name`)
2. Create another class (e.g. `BadSuper`)
3. Give `BadSuper` a superclass which has `Bad Name` as a type argument (e.g. `ArrayList<Bad Name>`)
4. Attempt to get the generic superclass of `BadSuper` using `Class.getGenericSuperclass()`
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
`Class.getGenericSuperclass()` will return a parameterized type with `Bad Name` as the type argument
ACTUAL -
`Class.getGenericSuperclass()` throws `java.lang.reflect.GenericSignatureFormatError`
```
Exception in thread "main" java.lang.reflect.GenericSignatureFormatError: Signature Parse error: expected '<' or ';' but got
Remaining input: Name;>;
at sun.reflect.generics.parser.SignatureParser.error(SignatureParser.java:124)
at sun.reflect.generics.parser.SignatureParser.parsePackageNameAndSimpleClassTypeSignature(SignatureParser.java:348)
at sun.reflect.generics.parser.SignatureParser.parseClassTypeSignature(SignatureParser.java:310)
at sun.reflect.generics.parser.SignatureParser.parseFieldTypeSignature(SignatureParser.java:289)
at sun.reflect.generics.parser.SignatureParser.parseFieldTypeSignature(SignatureParser.java:283)
at sun.reflect.generics.parser.SignatureParser.parseTypeArgument(SignatureParser.java:436)
at sun.reflect.generics.parser.SignatureParser.parseTypeArguments(SignatureParser.java:396)
at sun.reflect.generics.parser.SignatureParser.parsePackageNameAndSimpleClassTypeSignature(SignatureParser.java:346)
at sun.reflect.generics.parser.SignatureParser.parseClassTypeSignature(SignatureParser.java:310)
at sun.reflect.generics.parser.SignatureParser.parseClassSignature(SignatureParser.java:213)
at sun.reflect.generics.parser.SignatureParser.parseClassSig(SignatureParser.java:156)
at sun.reflect.generics.repository.ClassRepository.parse(ClassRepository.java:57)
at sun.reflect.generics.repository.ClassRepository.parse(ClassRepository.java:41)
at sun.reflect.generics.repository.AbstractRepository.<init>(AbstractRepository.java:74)
at sun.reflect.generics.repository.GenericDeclRepository.<init>(GenericDeclRepository.java:49)
at sun.reflect.generics.repository.ClassRepository.<init>(ClassRepository.java:53)
at sun.reflect.generics.repository.ClassRepository.make(ClassRepository.java:70)
at java.lang.Class.getGenericInfo(Class.java:2548)
at java.lang.Class.getGenericSuperclass(Class.java:765)
at BadMain.main(BadMain.java:3)
```
---------- BEGIN SOURCE ----------
// I've created a pre-built jar containing this test case so you don't have to fuss with the Kotlin compiler (necessary to put spaces in the class name):
// https://gist.github.com/thecodewarrior/0b23b6ee1c5fddb18f156c20eddd8ec1
// Bad.kt:
class `Bad Name`
class BadSuper: ArrayList<`Bad Name`>()
/* approximate Java equivalent:
public class `Bad Name` {}
public class BadSuper extends ArrayList<`Bad Name`> {}
*/
// BadMain.java:
public class BadMain {
public static void main(String args[]) {
BadSuper.class.getGenericSuperclass();
}
}
---------- END SOURCE ----------
FREQUENCY : always