Community
Participate
Working Groups
Build ID: I20070517-1700 (3.3.0 RC1) The code below throws GenericSignatureFormatError if compiled with Eclipse. The generated signatures are Ljava/lang/ref/Reference<LNested$Rather$Deeply;>; (javac) Ljava/lang/ref/Reference<LNested$Rather.Deeply;>; (eclipse) public class Nested<T> { static class Rather { static class Deeply { } } Reference<Nested.Rather.Deeply> x; public static void main(String[] args) throws Exception { System.out.println(Nested.class.getDeclaredField("x").getGenericType()); } }
Interestingly enough, if you change slightly the given test case, the generated .class file by javac would also be rejected. ---------------------------------------------------- import java.lang.ref.Reference; public class X<T> { class Rather { class Deeply { } } Reference<X<String>.Rather.Deeply> x; public static void main(String[] args) throws Exception { System.out.println(X.class.getDeclaredField("x").getGenericType()); } } ---------------------------------------------------- leads to the same GenericSignatureFormatError. The actual logic of the generic type signature seems to be that if one of the enclosing types is a parameterized type with arguments, then all separators are '.', but if none of the enclosing types have arguments then '$' should be used. Following this rules leads to the same signature as javac, but this doesn't prevent the error mentionned above. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6470192 seems to be the same kind of issues. This is reported as a bug against the library and not against the compiler.
Created attachment 70175 [details] Proposed fix + regression test
The patch doesn't work for corner case 'u' below. import java.lang.ref.Reference; public class X<T> { static class Rather { static class Deeply { static class Inside { } } } class Other<U> { class Deeply { class Inside<V> { } } } Reference<X.Rather.Deeply> x; Reference<X.Rather> y; Reference<X.Rather.Deeply.Inside> z; Reference<X<String>.Other<Thread>.Deeply> t; Reference<X<String>.Other<Thread>.Deeply.Inside<Number>> u; public static void main(String[] args) throws Exception { System.out.print(X.class.getDeclaredField("x").getGenericType()); System.out.print("##"); System.out.print(X.class.getDeclaredField("y").getGenericType()); System.out.print("##"); System.out.print(X.class.getDeclaredField("z").getGenericType()); System.out.print("##"); System.out.print(X.class.getDeclaredField("t").getGenericType()); System.out.print("##"); System.out.print(X.class.getDeclaredField("u").getGenericType()); System.out.println(); } } It produces: Ljava/lang/ref/Reference<LX<Ljava/lang/String;>.Other<Ljava/lang/Thread;>.Deeply.Inside<Ljava/lang/Number;>;>; where I think it expects: Ljava/lang/ref/Reference<LX<Ljava/lang/String;>.Other<Ljava/lang/Thread;>.Deeply$Inside<Ljava/lang/Number;>;>; so the rule would rather be that if immediate enclosing type had arguments, then separator needs to be '.', and '$' otherwise.
Now, if fixing the compiler output for generic type signature, we also need to tune the signature decoding (when reading binaries back in) symmetrically. Then it looks strange, since the '.' was a convenient way to find out about presence of trailing type arguments; which is no longer the case (since a '$' may now be used, and still with trailing arguments). This is why I suspect this is a library bug; i.e. the rule should be that a '.' is used as soon as there are type arguments (for member types). Thus our original code would be fine. Until this is clarified, we should remove the 3.3.1 commitment.
Created attachment 77253 [details] Proposed patch This patch does produce different generic signatures in output, but fails to read them back in properly, as demonstrated by GenericTypeTest#test1053. Until the spec is clarified, we shouldn't release this patch as is.
According to VM spec on classfile format, my previous comment looks valid wrt to signature format: i.e. the only way you can get member type arguments is after a '.'. From JVM spec [4.4.4] ClassTypeSignature: L PackageSpecifier* SimpleClassTypeSignature ClassTypeSignatureSuffix* ; PackageSpecifier: Identifier / PackageSpecifier* SimpleClassTypeSignature: Identifier TypeArgumentsopt ClassTypeSignatureSuffix: DESCRIPTORS AND SIGNATURES 107 . SimpleClassTypeSignature TypeVariableSignature: T Identifer ; TypeArguments: <TypeArgument+> TypeArgument: WildcardIndicatoropt FieldTypeSignature * WildcardIndicator: + - ArrayTypeSignature: [TypeSignature TypeSignature: [FieldTypeSignature [BaseType A class type signature gives complete type information for a class or interface type. The class type signature must be formulated such that it can be reliably mapped to the binary name of the class it denotes by erasing any type arguments and converting ‘.’ characters in the signature to ‘$’ characters.
Created attachment 77388 [details] Better patch More tests are supported by this patch. It pretty much agrees with Javac in all the tested cases, but still conflict with the signature parser. Checked that the binary decoding is also able to read back in the new signatures.
(note that the previous patch was made against 3.3 maintenance stream)
Released for 3.4M2. Added GenericTypeTest#test1050-1055 Fixed
Verified for 3.4M2 using build I20070917-0010