APIdia Launch Partner
Top Description Fields Constructors Methods
sun.invoke.util

public Class BytecodeName

extends Object
Class Inheritance

Utility routines for dealing with bytecode-level names. Includes universal mangling rules for the JVM.

Avoiding Dangerous Characters

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.

Replacement Characters

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:

  1. In each accidental escape, replace the backslash with an escape sequence (\-).
  2. Replace each dangerous character with an escape sequence (\| for /, etc.).
  3. If the first two steps introduced any change, and if the string does not already begin with a backslash, prepend a null prefix (\=).
To demangle a mangled string that begins with an escape, remove any null prefix, and then replace (in parallel) each escape sequence by its original character.

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.

Nice Properties

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:

Suggestions for Human Readable Presentations

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.

Author
John Rose
Version
1.2, 02/06/2008
See Also
Symbolic Freedom in the VM

Field Summary

Modifier and TypeField 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[]

Constructor Summary

AccessConstructor and Description
private

Method Summary

Modifier and TypeMethod and Description
private static String
appendAll(Object[] components)

private static void
private static String
pack-priv static int
pack-priv static boolean
isDangerous(char c)

private static boolean
public static boolean

Returns:

true if the character is safe to use in classfiles
isSafeBytecodeChar
(char
the 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 safe
isSafeBytecodeName
(String
the proposed bytecode name
s
)

Report whether a simple name is safe as a bytecode name.

pack-priv static boolean
isSpecial(char c)

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
replacementOf(char c)

public static String

Returns:

a valid bytecode name which represents the source name
toBytecodeName
(String
the source name
s
)

Given a source name, produce the corresponding bytecode name.

public static String

Returns:

a human-readable presentation
toDisplayName
(String
the 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 characters
toSourceName
(String
the bytecode name
s
)

Given an unqualified bytecode name, produce the corresponding source name.

public static String

Returns:

the concatenation of all components
unparseBytecodeName
(Object[]
a series of name components
components
)

Given a series of components, create a bytecode name for a classfile.

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

DANGEROUS_CHAR_FIRST_INDEXback to summary
pack-priv static final int DANGEROUS_CHAR_FIRST_INDEX
DANGEROUS_CHARSback to summary
pack-priv static final String DANGEROUS_CHARS
DANGEROUS_CHARS_Aback to summary
pack-priv static char[] DANGEROUS_CHARS_A
DANGEROUS_CHARS_CAback to summary
pack-priv static final Character[] DANGEROUS_CHARS_CA
ESCAPE_Cback to summary
pack-priv static char ESCAPE_C
NULL_ESCAPEback to summary
pack-priv static String NULL_ESCAPE
NULL_ESCAPE_Cback to summary
pack-priv static char NULL_ESCAPE_C
REPLACEMENT_CHARSback to summary
pack-priv static final String REPLACEMENT_CHARS
REPLACEMENT_CHARS_Aback to summary
pack-priv static char[] REPLACEMENT_CHARS_A
SPECIAL_BITMAPback to summary
pack-priv static final long[] SPECIAL_BITMAP

Constructor Detail

BytecodeNameback to summary
private BytecodeName()

Method Detail

appendAllback to summary
private static String appendAll(Object[] components)
checkSafeBytecodeNameback to summary
private static void checkSafeBytecodeName(String s) throws IllegalArgumentException
demangleback to summary
private static String demangle(String s)
indexOfDangerousCharback to summary
pack-priv static int indexOfDangerousChar(String s, int from)
isDangerousback to summary
pack-priv static boolean isDangerous(char c)
isJavaIdentback to summary
private static boolean isJavaIdent(String s)
isSafeBytecodeCharback 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: ".;:$[]<>/".

Parameters
c:char

the proposed character

Returns:boolean

true if the character is safe to use in classfiles

isSafeBytecodeNameback 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.

Parameters
s:String

the proposed bytecode name

Returns:boolean

true if the name is non-empty and all of its characters are safe

isSpecialback to summary
pack-priv static boolean isSpecial(char c)
lastIndexOfDangerousCharback to summary
pack-priv static int lastIndexOfDangerousChar(String s, int from)
looksMangledback to summary
private static boolean looksMangled(String s)
mangleback to summary
private static String mangle(String s)
originalOfReplacementback to summary
pack-priv static char originalOfReplacement(char c)
parseBytecodeNameback 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 java/lang/String will be parsed into the array {"java", '/', "lang", '/', "String"}. The name <init> will be parsed into {'<', "init", '>'}. The name foo/bar$:baz will be parsed into {"foo", '/', "bar", '$', ':', "baz"}. The name ::\=:foo:\=bar\!baz will be parsed into {':', ':', "", ':', "foo", ':', "bar:baz"}.

quoteDisplayback to summary
private static String quoteDisplay(String s)
replacementOfback to summary
pack-priv static char replacementOf(char c)
toBytecodeNameback 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.

Parameters
s:String

the source name

Returns:String

a valid bytecode name which represents the source name

toDisplayNameback 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.

Parameters
s:String

the original bytecode name (which may be qualified)

Returns:String

a human-readable presentation

toSourceNameback 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 ':'.

Parameters
s:String

the bytecode name

Returns:String

the source name, which may possibly have unsafe characters

Exceptions
IllegalArgumentException:
if the bytecode name is not safe
See Also
isSafeBytecodeName(java.lang.String)
unparseBytecodeNameback 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 parseBytecodeName(java.lang.String). Each component must either be an interned one-character string of a dangerous character, or else a safe bytecode name.

Parameters
components:Object[]

a series of name components

Returns:String

the concatenation of all components

Exceptions
IllegalArgumentException:
if any component contains an unsafe character, and is not an interned one-character string
NullPointerException:
if any component is null