Bug 189158 - [1.5][compiler] Malformed generic signature for nested classes (. vs $)
Summary: [1.5][compiler] Malformed generic signature for nested classes (. vs $)
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.3   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 3.4 M2   Edit
Assignee: Philipe Mulet CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-05-25 11:27 EDT by Erling Ellingsen CLA
Modified: 2007-09-18 06:10 EDT (History)
1 user (show)

See Also:


Attachments
Proposed fix + regression test (4.16 KB, patch)
2007-06-05 13:07 EDT, Olivier Thomann CLA
no flags Details | Diff
Proposed patch (11.96 KB, patch)
2007-08-29 09:52 EDT, Philipe Mulet CLA
no flags Details | Diff
Better patch (19.83 KB, patch)
2007-08-30 11:50 EDT, Philipe Mulet CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Erling Ellingsen CLA 2007-05-25 11:27:16 EDT
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());
	}
}
Comment 1 Olivier Thomann CLA 2007-06-05 12:03:57 EDT
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.
Comment 2 Olivier Thomann CLA 2007-06-05 13:07:41 EDT
Created attachment 70175 [details]
Proposed fix + regression test
Comment 3 Philipe Mulet CLA 2007-08-29 07:49:11 EDT
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.


Comment 4 Philipe Mulet CLA 2007-08-29 09:48:42 EDT
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.
Comment 5 Philipe Mulet CLA 2007-08-29 09:52:06 EDT
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.
Comment 6 Philipe Mulet CLA 2007-08-29 10:15:11 EDT
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.
Comment 7 Philipe Mulet CLA 2007-08-30 11:50:05 EDT
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.
Comment 8 Philipe Mulet CLA 2007-08-30 11:50:56 EDT
(note that the previous patch was made against 3.3 maintenance stream)
Comment 9 Philipe Mulet CLA 2007-08-31 07:23:55 EDT
Released for 3.4M2.

Added GenericTypeTest#test1050-1055
Fixed
Comment 10 David Audel CLA 2007-09-18 06:10:24 EDT
Verified for 3.4M2 using build I20070917-0010