RemappingSignatureAdapter.visitInnerClassType is broken when handling identifiers with "$" signs
One of our developers found an issue with jarjar (code.google.com/p/jarjar/)
when used on certain scala code. I believe I've tracked this problem down to
the visitInnerClassType method in RemappingSignatureAdapter.
As I understand it, inner classes end up with two distinct names in the
bytecode. In the containing class is an InnerClasses attribute which maps a
name (that behaves as if it were a field on the containing class) to the full
name of the inner class. Second you have the inner class itself, which is
named by javac using the convention:
enclosing class name + "$" + inner class name
For example:
class Outer {
class Inner {
}
}
The class "Inner" here is named "Outer$Inner" by javac. In addition the class
file "Outer.class" contains an inner class record for the name "Inner" that
maps that name to the actual class (Outer$Inner).
The visitInnerClassType method on RemappingSignatureAdapter assumes that it can
reconstruct the name of the class by using the same convention as javac:
enclosing class name + "$" + inner class name
However, it further assumes that after calling remapper.mapType it can
deconstruct this name by splitting on the last "$" in the name. This makes two
assumptions:
1) The mapType method maintains the convention of naming the inner class
after the containing class + "$" plus the name of inner class. I believe
that the original JDK 1.1 documentation had an "Inner Classes
Specification" that required this naming, but that newer version of the
Java Virtual Machine specification don't mention it.
2) That there are no other "$" signs in the identifiers.
Scala is violating assumption 2 by using other $ signs in identifiers, but this
is allowed by the Java Language Specification as they are "mechanically
generated":
http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.8
"An identifier is an unlimited-length sequence of Java letters and Java digits,
the first of which must be a Java letter. ... The "Java letters" include
uppercase and lowercase ASCII Latin letters A-Z (\u0041-\u005a), and a-z
(\u0061-\u007a), and, for historical reasons, the ASCII underscore (_, or
\u005f) and dollar sign ($, or \u0024). The $ character should be used only in
mechanically generated source code or, rarely, to access pre-existing names on
legacy systems."
I attach an example Java file which triggers the problem. The signature of the
getValue method is mangled by an empty remapper that is supposed to have no
impact, it becomes:
Signature: #29 // ()Lpack/Test<TT;>.One.Two;
Rather than remaining as:
Signature: #29 // ()Lpack/Test<TT;>.Inner$One.Inner$Two;