The JVM defines a very small set of characters which are illegal in name spellings. We will slightly extend and regularize this set into a group of dangerous characters. These characters will then be replaced, in mangled names, by escape sequences. In addition, accidental escape sequences must be further escaped. Finally, a special prefix will be applied if and only if the mangling would otherwise fail to begin with the escape character. This happens to cover the corner case of the null string, and also clearly marks symbols which need demangling.
Dangerous characters are the union of all characters forbidden
or otherwise restricted by the JVM specification,
plus their mates, if they are brackets
([ and ],
< and >),
plus, arbitrarily, the colon character :.
There is no distinction between type, method, and field names.
This makes it easier to convert between mangled names of different
types, since they do not need to be decoded (demangled).
The escape character is backslash \
(also known as reverse solidus).
This character is, until now, unheard of in bytecode names,
but traditional in the proposed role.
Every escape sequence is two characters (in fact, two UTF8 bytes) beginning with the escape character and followed by a replacement character. (Since the replacement character is never a backslash, iterated manglings do not double in size.)
Each dangerous character has some rough visual similarity to its corresponding replacement character. This makes mangled symbols easier to recognize by sight.
The dangerous characters are
/ (forward slash, used to delimit package components),
. (dot, also a package delimiter),
; (semicolon, used in signatures),
$ (dollar, used in inner classes and synthetic members),
< (left angle),
> (right angle),
[ (left square bracket, used in array types),
] (right square bracket, reserved in this scheme for language use),
and : (colon, reserved in this scheme for language use).
Their replacements are, respectively,
| (vertical bar),
, (comma),
? (question mark),
% (percent),
^ (caret),
_ (underscore), and
{ (left curly bracket),
} (right curly bracket),
! (exclamation mark).
In addition, the replacement character for the escape character itself is
- (hyphen),
and the replacement character for the null prefix is
= (equal sign).
An escape character \
followed by any of these replacement characters
is an escape sequence, and there are no other escape sequences.
An equal sign is only part of an escape sequence
if it is the second character in the whole string, following a backslash.
Two consecutive backslashes do not form an escape sequence.
Each escape sequence replaces a so-called original character which is either one of the dangerous characters or the escape character. A null prefix replaces an initial null string, not a character.
All this implies that escape sequences cannot overlap and may be
determined all at once for a whole string. Note that a spelling
string can contain accidental escapes, apparent escape
sequences which must not be interpreted as manglings.
These are disabled by replacing their leading backslash with an
escape sequence (\-). To mangle a string, three logical steps
are required, though they may be carried out in one pass:
\-).\| for /, etc.).\=).Spelling strings which contain accidental escapes must have them replaced, even if those strings do not contain dangerous characters. This restriction means that mangling a string always requires a scan of the string for escapes. But then, a scan would be required anyway, to check for dangerous characters.
If a bytecode name does not contain any escape sequence, demangling is a no-op: The string demangles to itself. Such a string is called self-mangling. Almost all strings are self-mangling. In practice, to demangle almost any name “found in nature”, simply verify that it does not begin with a backslash.
Mangling is a one-to-one function, while demangling
is a many-to-one function.
A mangled string is defined as validly mangled if
it is in fact the unique mangling of its spelling string.
Three examples of invalidly mangled strings are \=foo,
\-bar, and baz\!, which demangle to foo, \bar, and
baz\!, but then remangle to foo, \bar, and \=baz\-!.
If a language back-end or runtime is using mangled names,
it should never present an invalidly mangled bytecode
name to the JVM. If the runtime encounters one,
it should also report an error, since such an occurrence
probably indicates a bug in name encoding which
will lead to errors in linkage.
However, this note does not propose that the JVM verifier
detect invalidly mangled names.
As a result of these rules, it is a simple matter to compute validly mangled substrings and concatenations of validly mangled strings, and (with a little care) these correspond to corresponding operations on their spelling strings.
If languages that include non-Java symbol spellings use this mangling convention, they will enjoy the following advantages:
For human readable displays of symbols, it will be better to present a string-like quoted representation of the spelling, because JVM users are generally familiar with such tokens. We suggest using single or double quotes before and after mangled symbols which are not valid Java identifiers, with quotes, backslashes, and non-printing characters escaped as if for literals in the Java language.
For example, an HTML-like spelling
<pre> mangles to
\^pre\_ and could
display more cleanly as
'<pre>',
with the quotes included.
Such string-like conventions are not suitable
for mangled bytecode names, in part because
dangerous characters must be eliminated, rather
than just quoted. Otherwise internally structured
strings like package prefixes and method signatures
could not be reliably parsed.
In such human-readable displays, invalidly mangled
names should not be demangled and quoted,
for this would be misleading. Likewise, JVM symbols
which contain dangerous characters (like dots in field
names or brackets in method names) should not be
simply quoted. The bytecode names
\=phase\,1 and
phase.1 are distinct,
and in demangled displays they should be presented as
'phase.1' and something like
'phase'.1, respectively.
| Modifier and Type | Field and Description |
|---|---|
| pack-priv static final int | |
| pack-priv static final String | |
| pack-priv static char[] | |
| pack-priv static final Character[] | |
| pack-priv static char | |
| pack-priv static String | |
| pack-priv static char | |
| pack-priv static final String | |
| pack-priv static char[] | |
| pack-priv static final long[] |
| Access | Constructor and Description |
|---|---|
| private |
| Modifier and Type | Method and Description |
|---|---|
| private static String | |
| private static void | |
| private static String | |
| pack-priv static int | |
| pack-priv static boolean | |
| private static boolean | |
| public static boolean | Returns: true if the character is safe to use in classfilesthe proposed character c)Report whether a character is safe in a bytecode name. |
| public static boolean | Returns: true if the name is non-empty and all of its characters are safethe proposed bytecode name s)Report whether a simple name is safe as a bytecode name. |
| pack-priv static boolean | |
| pack-priv static int | |
| private static boolean | |
| private static String | |
| pack-priv static char | |
| public static Object[] | parseBytecodeName(String s)
Given a bytecode name from a classfile, separate it into components delimited by dangerous characters. |
| private static String | |
| pack-priv static char | |
| public static String | Returns: a valid bytecode name which represents the source namethe source name s)Given a source name, produce the corresponding bytecode name. |
| public static String | Returns: a human-readable presentationthe original bytecode name (which may be qualified) s)Given a bytecode name, produce the corresponding display name. |
| public static String | Returns: the source name, which may possibly have unsafe charactersthe bytecode name s)Given an unqualified bytecode name, produce the corresponding source name. |
| public static String | Returns: the concatenation of all componentsa series of name components components)Given a series of components, create a bytecode name for a classfile. |
| DANGEROUS_CHAR_FIRST_INDEX | back to summary |
|---|---|
| pack-priv static final int DANGEROUS_CHAR_FIRST_INDEX | |
| DANGEROUS_CHARS | back to summary |
|---|---|
| pack-priv static final String DANGEROUS_CHARS | |
| DANGEROUS_CHARS_A | back to summary |
|---|---|
| pack-priv static char[] DANGEROUS_CHARS_A | |
| DANGEROUS_CHARS_CA | back to summary |
|---|---|
| pack-priv static final Character[] DANGEROUS_CHARS_CA | |
| ESCAPE_C | back to summary |
|---|---|
| pack-priv static char ESCAPE_C | |
| NULL_ESCAPE | back to summary |
|---|---|
| pack-priv static String NULL_ESCAPE | |
| NULL_ESCAPE_C | back to summary |
|---|---|
| pack-priv static char NULL_ESCAPE_C | |
| REPLACEMENT_CHARS | back to summary |
|---|---|
| pack-priv static final String REPLACEMENT_CHARS | |
| REPLACEMENT_CHARS_A | back to summary |
|---|---|
| pack-priv static char[] REPLACEMENT_CHARS_A | |
| SPECIAL_BITMAP | back to summary |
|---|---|
| pack-priv static final long[] SPECIAL_BITMAP | |
| BytecodeName | back to summary |
|---|---|
| private BytecodeName() | |
| appendAll | back to summary |
|---|---|
| private static String appendAll(Object[] components) | |
| checkSafeBytecodeName | back to summary |
|---|---|
| private static void checkSafeBytecodeName(String s) throws IllegalArgumentException | |
| demangle | back to summary |
|---|---|
| private static String demangle(String s) | |
| indexOfDangerousChar | back to summary |
|---|---|
| pack-priv static int indexOfDangerousChar(String s, int from) | |
| isDangerous | back to summary |
|---|---|
| pack-priv static boolean isDangerous(char c) | |
| isJavaIdent | back to summary |
|---|---|
| private static boolean isJavaIdent(String s) | |
| isSafeBytecodeChar | back to summary |
|---|---|
| public static boolean isSafeBytecodeChar(char c) Report whether a character is safe in a bytecode name.
This is true of any unicode character except the following
dangerous characters:
| |
| isSafeBytecodeName | back to summary |
|---|---|
| public static boolean isSafeBytecodeName(String s) Report whether a simple name is safe as a bytecode name. Such names are acceptable in class files as class, method, and field names. Additionally, they are free of "dangerous" characters, even if those characters are legal in some (or all) names in class files.
| |
| isSpecial | back to summary |
|---|---|
| pack-priv static boolean isSpecial(char c) | |
| lastIndexOfDangerousChar | back to summary |
|---|---|
| pack-priv static int lastIndexOfDangerousChar(String s, int from) | |
| looksMangled | back to summary |
|---|---|
| private static boolean looksMangled(String s) | |
| mangle | back to summary |
|---|---|
| private static String mangle(String s) | |
| originalOfReplacement | back to summary |
|---|---|
| pack-priv static char originalOfReplacement(char c) | |
| parseBytecodeName | back to summary |
|---|---|
| public static Object[] parseBytecodeName(String s) Given a bytecode name from a classfile, separate it into
components delimited by dangerous characters.
Each resulting array element will be either a dangerous character,
or else a safe bytecode name.
(The safe name might possibly be mangled to hide further dangerous characters.)
For example, the qualified class name | |
| quoteDisplay | back to summary |
|---|---|
| private static String quoteDisplay(String s) | |
| replacementOf | back to summary |
|---|---|
| pack-priv static char replacementOf(char c) | |
| toBytecodeName | back to summary |
|---|---|
| public static String toBytecodeName(String s) Given a source name, produce the corresponding bytecode name. The source name should not be qualified, because any syntactic markers (dots, slashes, dollar signs, colons, etc.) will be mangled. | |
| toDisplayName | back to summary |
|---|---|
| public static String toDisplayName(String s) Given a bytecode name, produce the corresponding display name. This is the source name, plus quotes if needed. If the bytecode name contains dangerous characters, assume that they are being used as punctuation, and pass them through unchanged. Non-empty runs of non-dangerous characters are demangled if necessary, and the resulting names are quoted if they are not already valid Java identifiers, or if they contain a dangerous character (i.e., dollar sign "$"). Single quotes are used when quoting. Within quoted names, embedded single quotes and backslashes are further escaped by prepended backslashes. | |
| toSourceName | back to summary |
|---|---|
| public static String toSourceName(String s) Given an unqualified bytecode name, produce the corresponding source name.
The bytecode name must not contain dangerous characters.
In particular, it must not be qualified or segmented by colon
| |
| unparseBytecodeName | back to summary |
|---|---|
| public static String unparseBytecodeName(Object[] components) Given a series of components, create a bytecode name for a classfile.
This is the inverse of
| |