@@ -13,13 +13,16 @@ import scala.annotation.internal.sharable
1313object NameTransformer {
1414
1515 private val nops = 128
16+ private val ncodes = 26 * 26
1617
17- @ sharable private val op2code = new Array [String ](nops)
18- @ sharable private val str2op = new mutable.HashMap [String , Char ]
18+ private class OpCodes (val op : Char , val code : String , val next : OpCodes )
1919
20+ @ sharable private val op2code = new Array [String ](nops)
21+ @ sharable private val code2op = new Array [OpCodes ](ncodes)
2022 private def enterOp (op : Char , code : String ) = {
21- op2code(op) = code
22- str2op(code) = op
23+ op2code(op.toInt) = code
24+ val c = (code.charAt(1 ) - 'a' ) * 26 + code.charAt(2 ) - 'a'
25+ code2op(c.toInt) = new OpCodes (op, code, code2op(c))
2326 }
2427
2528 /* Note: decoding assumes opcodes are only ever lowercase. */
@@ -42,99 +45,102 @@ object NameTransformer {
4245 enterOp('?' , " $qmark" )
4346 enterOp('@' , " $at" )
4447
45- /** Expand characters that are illegal as JVM method names by `$u`, followed
46- * by the character's unicode expansion.
47- */
48- def avoidIllegalChars (name : SimpleName ): SimpleName = {
49- var i = name.length - 1
50- while (i >= 0 && Chars .isValidJVMMethodChar(name(i))) i -= 1
51- if (i >= 0 )
52- termName(
53- name.toString.flatMap(ch =>
54- if (Chars .isValidJVMMethodChar(ch)) ch.toString else " $u%04X" .format(ch.toInt)))
55- else name
56- }
57-
58- /** Decode expanded characters starting with `$u`, followed by the character's unicode expansion. */
59- def decodeIllegalChars (name : String ): String =
60- if (name.contains(" $u" )) {
61- val sb = new mutable.StringBuilder ()
62- var i = 0
63- while (i < name.length)
64- if (i < name.length - 5 && name(i) == '$' && name(i + 1 ) == 'u' ) {
65- val numbers = name.substring(i + 2 , i + 6 )
66- try sb.append(Integer .valueOf(name.substring(i + 2 , i + 6 ), 16 ).toChar)
67- catch {
68- case _ : java.lang.NumberFormatException =>
69- sb.append(" $u" ).append(numbers)
70- }
71- i += 6
72- }
73- else {
74- sb.append(name(i))
75- i += 1
76- }
77- sb.result()
78- }
79- else name
80-
81- /** Replace operator symbols by corresponding expansion strings.
82- *
83- * @param name the string to encode
84- * @return the string with all recognized opchars replaced with their encoding
85- *
86- * Operator symbols are only recognized if they make up the whole name, or
87- * if they make up the last part of the name which follows a `_`.
48+ /** Replace operator symbols by corresponding expansion strings, and replace
49+ * characters that are not valid Java identifiers by "$u" followed by the
50+ * character's unicode expansion.
8851 */
8952 def encode (name : SimpleName ): SimpleName = {
90- def loop (len : Int , ops : List [String ]): SimpleName = {
91- def convert =
92- if (ops.isEmpty) name
93- else {
94- val buf = new java.lang.StringBuilder
95- buf.append(chrs, name.start, len)
96- for (op <- ops) buf.append(op)
97- termName(buf.toString)
53+ var buf : StringBuilder = null
54+ val len = name.length
55+ var i = 0
56+ while (i < len) {
57+ val c = name(i)
58+ if (c < nops && (op2code(c.toInt) ne null )) {
59+ if (buf eq null ) {
60+ buf = new StringBuilder ()
61+ buf.append(name.sliceToString(0 , i))
62+ }
63+ buf.append(op2code(c.toInt))
64+ /* Handle glyphs that are not valid Java/JVM identifiers */
65+ }
66+ else if (! Character .isJavaIdentifierPart(c)) {
67+ if (buf eq null ) {
68+ buf = new StringBuilder ()
69+ buf.append(name.sliceToString(0 , i))
9870 }
99- if (len == 0 || name(len - 1 ) == '_' ) convert
100- else {
101- val ch = name(len - 1 )
102- if (ch <= nops && op2code(ch) != null )
103- loop(len - 1 , op2code(ch) :: ops)
104- else if (Chars .isSpecial(ch))
105- loop(len - 1 , ch.toString :: ops)
106- else name
71+ buf.append(" $u%04X" .format(c.toInt))
10772 }
73+ else if (buf ne null ) {
74+ buf.append(c)
75+ }
76+ i += 1
10877 }
109- loop( name.length, Nil )
78+ if (buf eq null ) name else termName(buf.toString )
11079 }
11180
112- /** Replace operator expansions by the operators themselves.
113- * Operator expansions are only recognized if they make up the whole name, or
114- * if they make up the last part of the name which follows a `_`.
81+ /** Replace operator expansions by the operators themselves,
82+ * and decode `$u....` expansions into unicode characters.
11583 */
11684 def decode (name : SimpleName ): SimpleName = {
117- def loop (len : Int , ops : List [Char ]): SimpleName = {
118- def convert =
119- if (ops.isEmpty) name
120- else {
121- val buf = new java.lang.StringBuilder
122- buf.append(chrs, name.start, len)
123- for (op <- ops) buf.append(op)
124- termName(buf.toString)
125- }
126- if (len == 0 || name(len - 1 ) == '_' ) convert
127- else if (Chars .isSpecial(name(len - 1 ))) loop(len - 1 , name(len - 1 ) :: ops)
128- else {
129- val idx = name.lastIndexOf('$' , len - 1 )
130- if (idx >= 0 && idx + 2 < len)
131- str2op.get(name.sliceToString(idx, len)) match {
132- case Some (ch) => loop(idx, ch :: ops)
133- case None => name
85+ // System.out.println("decode: " + name);//DEBUG
86+ var buf : StringBuilder = null
87+ val len = name.length
88+ var i = 0
89+ while (i < len) {
90+ var ops : OpCodes = null
91+ var unicode = false
92+ val c = name(i)
93+ if (c == '$' && i + 2 < len) {
94+ val ch1 = name(i + 1 )
95+ if ('a' <= ch1 && ch1 <= 'z' ) {
96+ val ch2 = name(i + 2 )
97+ if ('a' <= ch2 && ch2 <= 'z' ) {
98+ ops = code2op((ch1 - 'a' ) * 26 + ch2 - 'a' )
99+ while ((ops ne null ) && ! name.startsWith(ops.code, i)) ops = ops.next
100+ if (ops ne null ) {
101+ if (buf eq null ) {
102+ buf = new StringBuilder ()
103+ buf.append(name.sliceToString(0 , i))
104+ }
105+ buf.append(ops.op)
106+ i += ops.code.length()
107+ }
108+ /* Handle the decoding of Unicode glyphs that are
109+ * not valid Java/JVM identifiers */
110+ } else if ((len - i) >= 6 && // Check that there are enough characters left
111+ ch1 == 'u' &&
112+ ((Character .isDigit(ch2)) ||
113+ ('A' <= ch2 && ch2 <= 'F' ))) {
114+ /* Skip past "$u", next four should be hexadecimal */
115+ val hex = name.sliceToString(i+ 2 , i+ 6 )
116+ try {
117+ val str = Integer .parseInt(hex, 16 ).toChar
118+ if (buf eq null ) {
119+ buf = new StringBuilder ()
120+ buf.append(name.sliceToString(0 , i))
121+ }
122+ buf.append(str)
123+ /* 2 for "$u", 4 for hexadecimal number */
124+ i += 6
125+ unicode = true
126+ } catch {
127+ case _:NumberFormatException =>
128+ /* `hex` did not decode to a hexadecimal number, so
129+ * do nothing. */
130+ }
134131 }
135- else name
132+ }
133+ }
134+ /* If we didn't see an opcode or encoded Unicode glyph, and the
135+ buffer is non-empty, write the current character and advance
136+ one */
137+ if ((ops eq null ) && ! unicode) {
138+ if (buf ne null )
139+ buf.append(c)
140+ i += 1
136141 }
137142 }
138- loop(name.length, Nil )
143+ // System.out.println("= " + (if (buf == null) name else buf.toString()));//DEBUG
144+ if (buf eq null ) name else termName(buf.toString)
139145 }
140146}
0 commit comments