@@ -13,13 +13,16 @@ import scala.annotation.internal.sharable
13
13
object NameTransformer {
14
14
15
15
private val nops = 128
16
+ private val ncodes = 26 * 26
16
17
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 )
19
19
20
+ @ sharable private val op2code = new Array [String ](nops)
21
+ @ sharable private val code2op = new Array [OpCodes ](ncodes)
20
22
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))
23
26
}
24
27
25
28
/* Note: decoding assumes opcodes are only ever lowercase. */
@@ -42,99 +45,102 @@ object NameTransformer {
42
45
enterOp('?' , " $qmark" )
43
46
enterOp('@' , " $at" )
44
47
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.
88
51
*/
89
52
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))
98
70
}
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))
107
72
}
73
+ else if (buf ne null ) {
74
+ buf.append(c)
75
+ }
76
+ i += 1
108
77
}
109
- loop( name.length, Nil )
78
+ if (buf eq null ) name else termName(buf.toString )
110
79
}
111
80
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.
115
83
*/
116
84
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
+ }
134
131
}
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
136
141
}
137
142
}
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)
139
145
}
140
146
}
0 commit comments