1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
|
package dotty.tools.dotc
package core
import java.security.MessageDigest
import scala.annotation.switch
import scala.io.Codec
import Names._, StdNames._, Contexts._, Symbols._, Flags._
import Decorators.StringDecorator
import dotty.tools.dotc.util.Chars
import Chars.isOperatorPart
object NameOps {
final object compactify {
lazy val md5 = MessageDigest.getInstance("MD5")
/** COMPACTIFY
*
* The hashed name has the form (prefix + marker + md5 + marker + suffix), where
* - prefix/suffix.length = MaxNameLength / 4
* - md5.length = 32
*
* We obtain the formula:
*
* FileNameLength = 2*(MaxNameLength / 4) + 2.marker.length + 32 + 6
*
* (+6 for ".class"). MaxNameLength can therefore be computed as follows:
*/
def apply(s: String)(implicit ctx: Context): String = {
val marker = "$$$$"
val limit: Int = ctx.settings.maxClassfileName.value
val MaxNameLength = (limit - 6) min 2 * (limit - 6 - 2 * marker.length - 32)
def toMD5(s: String, edge: Int): String = {
val prefix = s take edge
val suffix = s takeRight edge
val cs = s.toArray
val bytes = Codec toUTF8 cs
md5 update bytes
val md5chars = (md5.digest() map (b => (b & 0xFF).toHexString)).mkString
prefix + marker + md5chars + marker + suffix
}
if (s.length <= MaxNameLength) s else toMD5(s, MaxNameLength / 4)
}
}
class PrefixNameExtractor(pre: TermName) {
def apply(name: TermName): TermName = pre ++ name
def unapply(name: TermName): Option[TermName] =
if (name startsWith pre) Some(name.drop(pre.length).asTermName) else None
}
object SuperAccessorName extends PrefixNameExtractor(nme.SUPER_PREFIX)
object InitializerName extends PrefixNameExtractor(nme.INITIALIZER_PREFIX)
implicit class NameDecorator[N <: Name](val name: N) extends AnyVal {
import nme._
def likeTyped(n: Name): N =
(if (name.isTermName) n.toTermName else n.toTypeName).asInstanceOf[N]
def isConstructorName = name == CONSTRUCTOR || name == IMPLCLASS_CONSTRUCTOR
def isExceptionResultName = name startsWith EXCEPTION_RESULT_PREFIX
def isImplClassName = name endsWith IMPL_CLASS_SUFFIX
def isLocalDummyName = name startsWith LOCALDUMMY_PREFIX
def isLoopHeaderLabel = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX)
def isProtectedAccessorName = name startsWith PROTECTED_PREFIX
def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER
def isSetterName = name endsWith SETTER_SUFFIX
def isSingletonName = name endsWith SINGLETON_SUFFIX
def isModuleClassName = name endsWith MODULE_SUFFIX
def isAvoidClashName = name endsWith AVOID_CLASH_SUFFIX
def isImportName = name startsWith IMPORT
def isFieldName = name endsWith LOCAL_SUFFIX
def isShadowedName = name.length > 0 && name.head == '(' && name.startsWith(nme.SHADOWED)
def isDefaultGetterName = name.isTermName && name.asTermName.defaultGetterIndex >= 0
def isScala2LocalSuffix = name.endsWith(" ")
def isModuleVarName(name: Name): Boolean =
name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX
/** Is name a variable name? */
def isVariableName: Boolean = name.length > 0 && {
val first = name.head
(((first.isLower && first.isLetter) || first == '_')
&& (name != false_)
&& (name != true_)
&& (name != null_))
}
def isOpAssignmentName: Boolean = name match {
case raw.NE | raw.LE | raw.GE | EMPTY =>
false
case _ =>
name.length > 0 && name.last == '=' && name.head != '=' && isOperatorPart(name.head)
}
/** Is this the name of a higher-kinded type parameter of a Lambda? */
def isLambdaArgName =
name.length > 0 && name.head == tpnme.LAMBDA_ARG_PREFIXhead && name.startsWith(tpnme.LAMBDA_ARG_PREFIX)
/** The index of the higher-kinded type parameter with this name.
* Pre: isLambdaArgName.
*/
def lambdaArgIndex: Int = name.drop(name.lastIndexOf('$') + 1).toString.toInt
/** If the name ends with $nn where nn are
* all digits, strip the $ and the digits.
* Otherwise return the argument.
*/
def stripAnonNumberSuffix: Name = {
var pos = name.length
while (pos > 0 && name(pos - 1).isDigit)
pos -= 1
if (pos > 0 && pos < name.length && name(pos - 1) == '$')
name take (pos - 1)
else
name
}
/** Convert this module name to corresponding module class name */
def moduleClassName: TypeName = (name ++ tpnme.MODULE_SUFFIX).toTypeName
/** Convert this module class name to corresponding source module name */
def sourceModuleName: TermName = stripModuleClassSuffix.toTermName
/** If name ends in module class suffix, drop it */
def stripModuleClassSuffix: Name =
if (isModuleClassName) name dropRight MODULE_SUFFIX.length else name
/** Append a suffix so that this name does not clash with another name in the same scope */
def avoidClashName: TermName = (name ++ AVOID_CLASH_SUFFIX).toTermName
/** If name ends in "avoid clash" suffix, drop it */
def stripAvoidClashSuffix: Name =
if (isAvoidClashName) name dropRight AVOID_CLASH_SUFFIX.length else name
/** If flags is a ModuleClass but not a Package, add module class suffix */
def adjustIfModuleClass(flags: Flags.FlagSet): N = {
if (flags is (ModuleClass, butNot = Package)) name.asTypeName.moduleClassName
else stripAvoidClashSuffix
}.asInstanceOf[N]
/** The superaccessor for method with given name */
def superName: TermName = (nme.SUPER_PREFIX ++ name).toTermName
/** The expanded name of `name` relative to given class `base`.
*/
def expandedName(base: Symbol)(implicit ctx: Context): N =
expandedName(if (base is Flags.ExpandedName) base.name else base.fullNameSeparated('$'))
/** The expanded name of `name` relative to `basename` with given `separator`
*/
def expandedName(prefix: Name): N =
name.fromName(prefix ++ nme.EXPAND_SEPARATOR ++ name).asInstanceOf[N]
def unexpandedName: N = {
val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR)
if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N]
}
def expandedPrefix: N = {
val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR)
assert(idx >= 0)
name.take(idx).asInstanceOf[N]
}
def shadowedName: N = likeTyped(nme.SHADOWED ++ name)
def revertShadowed: N = likeTyped(name.drop(nme.SHADOWED.length))
/** Translate a name into a list of simple TypeNames and TermNames.
* In all segments before the last, type/term is determined by whether
* the following separator char is '.' or '#'. The last segment
* is of the same type as the original name.
*
* Examples:
*
* package foo {
* object Lorax { object Wog ; class Wog }
* class Lorax { object Zax ; class Zax }
* }
*
* f("foo.Lorax".toTermName) == List("foo": Term, "Lorax": Term) // object Lorax
* f("foo.Lorax".toTypeName) == List("foo": Term, "Lorax": Type) // class Lorax
* f("Lorax.Wog".toTermName) == List("Lorax": Term, "Wog": Term) // object Wog
* f("Lorax.Wog".toTypeName) == List("Lorax": Term, "Wog": Type) // class Wog
* f("Lorax#Zax".toTermName) == List("Lorax": Type, "Zax": Term) // object Zax
* f("Lorax#Zax".toTypeName) == List("Lorax": Type, "Zax": Type) // class Zax
*
* Note that in actual scala syntax you cannot refer to object Zax without an
* instance of Lorax, so Lorax#Zax could only mean the type. One might think
* that Lorax#Zax.type would work, but this is not accepted by the parser.
* For the purposes of referencing that object, the syntax is allowed.
*/
def segments: List[Name] = {
def mkName(name: Name, follow: Char): Name =
if (follow == '.') name.toTermName else name.toTypeName
name.indexWhere(ch => ch == '.' || ch == '#') match {
case -1 =>
if (name.isEmpty) scala.Nil else name :: scala.Nil
case idx =>
mkName(name take idx, name(idx)) :: (name drop (idx + 1)).segments
}
}
/** The name of the generic runtime operation corresponding to an array operation */
def genericArrayOp: TermName = name match {
case nme.apply => nme.array_apply
case nme.length => nme.array_length
case nme.update => nme.array_update
case nme.clone_ => nme.array_clone
}
/** The name of the primitive runtime operation corresponding to an array operation */
def primitiveArrayOp: TermName = name match {
case nme.apply => nme.primitive.arrayApply
case nme.length => nme.primitive.arrayLength
case nme.update => nme.primitive.arrayUpdate
case nme.clone_ => nme.clone_
}
/** If name length exceeds allowable limit, replace part of it by hash */
def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString))
}
// needed???
private val Boxed = Map[TypeName, TypeName](
tpnme.Boolean -> jtpnme.BoxedBoolean,
tpnme.Byte -> jtpnme.BoxedByte,
tpnme.Char -> jtpnme.BoxedCharacter,
tpnme.Short -> jtpnme.BoxedShort,
tpnme.Int -> jtpnme.BoxedInteger,
tpnme.Long -> jtpnme.BoxedLong,
tpnme.Float -> jtpnme.BoxedFloat,
tpnme.Double -> jtpnme.BoxedDouble)
implicit class TermNameDecorator(val name: TermName) extends AnyVal {
import nme._
def setterName: TermName =
if (name.isFieldName) name.fieldToGetter.setterName
else name ++ SETTER_SUFFIX
def getterName: TermName =
if (name.isFieldName) fieldToGetter
else setterToGetter
def fieldName: TermName =
if (name.isSetterName) getterName.fieldName
else name ++ LOCAL_SUFFIX
private def setterToGetter: TermName = {
assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format")
name.take(name.length - SETTER_SUFFIX.length).asTermName
}
def fieldToGetter: TermName = {
assert(name.isFieldName)
name.take(name.length - LOCAL_SUFFIX.length).asTermName
}
/** Nominally, name$default$N, encoded for <init>
* @param Post the parameters position.
* @note Default getter name suffixes start at 1, so `pos` has to be adjusted by +1
*/
def defaultGetterName(pos: Int): TermName = {
val prefix = if (name.isConstructorName) DEFAULT_GETTER_INIT else name
prefix ++ DEFAULT_GETTER ++ (pos + 1).toString
}
/** Nominally, name from name$default$N, CONSTRUCTOR for <init> */
def defaultGetterToMethod: TermName = {
val p = name.indexOfSlice(DEFAULT_GETTER)
if (p >= 0) {
val q = name.take(p).asTermName
// i.e., if (q.decoded == CONSTRUCTOR.toString) CONSTRUCTOR else q
if (q == DEFAULT_GETTER_INIT) CONSTRUCTOR else q
} else name
}
/** If this is a default getter, its index (starting from 0), else -1 */
def defaultGetterIndex: Int = {
var i = name.length
while (i > 0 && name(i - 1).isDigit) i -= 1
if (i > 0 && i < name.length && name.take(i).endsWith(DEFAULT_GETTER))
name.drop(i).toString.toInt - 1
else
-1
}
def stripScala2LocalSuffix: TermName =
if (name.isScala2LocalSuffix) name.init.asTermName else name
/** The name of an accessor for protected symbols. */
def protectedAccessorName: TermName =
PROTECTED_PREFIX ++ name.unexpandedName
/** The name of a setter for protected symbols. Used for inherited Java fields. */
def protectedSetterName: TermName =
PROTECTED_SET_PREFIX ++ name.unexpandedName
def moduleVarName: TermName =
name ++ MODULE_VAR_SUFFIX
/** The name unary_x for a prefix operator x */
def toUnaryName: TermName = name match {
case raw.MINUS => UNARY_-
case raw.PLUS => UNARY_+
case raw.TILDE => UNARY_~
case raw.BANG => UNARY_!
case _ => name
}
/** The name of a method which stands in for a primitive operation
* during structural type dispatch.
*/
def primitiveInfixMethodName: TermName = name match {
case OR => takeOr
case XOR => takeXor
case AND => takeAnd
case EQ => testEqual
case NE => testNotEqual
case ADD => add
case SUB => subtract
case MUL => multiply
case DIV => divide
case MOD => takeModulo
case LSL => shiftSignedLeft
case LSR => shiftLogicalRight
case ASR => shiftSignedRight
case LT => testLessThan
case LE => testLessOrEqualThan
case GE => testGreaterOrEqualThan
case GT => testGreaterThan
case ZOR => takeConditionalOr
case ZAND => takeConditionalAnd
case _ => NO_NAME
}
/** Postfix/prefix, really.
*/
def primitivePostfixMethodName: TermName = name match {
case UNARY_! => takeNot
case UNARY_+ => positive
case UNARY_- => negate
case UNARY_~ => complement
case `toByte` => toByte
case `toShort` => toShort
case `toChar` => toCharacter
case `toInt` => toInteger
case `toLong` => toLong
case `toFloat` => toFloat
case `toDouble` => toDouble
case _ => NO_NAME
}
def primitiveMethodName: TermName =
primitiveInfixMethodName match {
case NO_NAME => primitivePostfixMethodName
case name => name
}
}
}
|