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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
|
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 util.{Chars, NameTransformer}
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: PreName): N =
(if (name.isTermName) n.toTermName else n.toTypeName).asInstanceOf[N]
def isConstructorName = name == CONSTRUCTOR || name == TRAIT_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 isTraitSetterName = name containsSlice TRAIT_SETTER_SEPARATOR
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
def isSelectorName = name.startsWith(" ") && name.tail.forall(_.isDigit)
def isLazyLocal = name.endsWith(nme.LAZY_LOCAL)
/** 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 isHkArgName =
name.length > 0 &&
name.head == tpnme.hkArgPrefixHead &&
name.startsWith(tpnme.hkArgPrefix) && {
val digits = name.drop(tpnme.hkArgPrefixLength)
digits.length <= 4 && digits.forall(_.isDigit)
}
/** The index of the higher-kinded type parameter with this name.
* Pre: isLambdaArgName.
*/
def hkArgIndex: Int =
name.drop(tpnme.hkArgPrefixLength).toString.toInt
def isLambdaTraitName(implicit ctx: Context): Boolean =
name.startsWith(tpnme.hkLambdaPrefix)
/** 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, separator: Name)(implicit ctx: Context): N =
expandedName(if (base is Flags.ExpandedName) base.name else base.fullNameSeparated("$"), separator)
def expandedName(base: Symbol)(implicit ctx: Context): N = expandedName(base, nme.EXPAND_SEPARATOR)
/** The expanded name of `name` relative to `basename` with given `separator`
*/
def expandedName(prefix: Name, separator: Name = nme.EXPAND_SEPARATOR): N =
name.fromName(prefix ++ separator ++ name).asInstanceOf[N]
def expandedName(prefix: Name): N = expandedName(prefix, nme.EXPAND_SEPARATOR)
/** Revert the expanded name. Note: This currently gives incorrect results
* if the normal name contains `nme.EXPAND_SEPARATOR`, i.e. two consecutive '$'
* signs. This can happen for instance if a super accessor is paired with
* an encoded name, e.g. super$$plus$eq. See #765.
*/
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))
def implClassName: N = likeTyped(name ++ tpnme.IMPL_CLASS_SUFFIX)
def freshened(implicit ctx: Context): N =
likeTyped(
if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName
else likeTyped(ctx.freshName(name ++ NameTransformer.NAME_JOIN_STRING)))
/** 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_
}
def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = {
def typeToTag(tp: Types.Type): Name = {
tp.classSymbol match {
case t if t eq defn.IntClass => nme.specializedTypeNames.Int
case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean
case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte
case t if t eq defn.LongClass => nme.specializedTypeNames.Long
case t if t eq defn.ShortClass => nme.specializedTypeNames.Short
case t if t eq defn.FloatClass => nme.specializedTypeNames.Float
case t if t eq defn.UnitClass => nme.specializedTypeNames.Void
case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double
case t if t eq defn.CharClass => nme.specializedTypeNames.Char
case _ => nme.specializedTypeNames.Object
}
}
val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1))
val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1))
name.fromName(name ++ nme.specializedTypeNames.prefix ++
methodTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.separator ++
classTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix)
}
/** 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) {
if (name.isTraitSetterName) {
// has form <$-separated-trait-name>$_setter_$ `name`_$eq
val start = name.indexOfSlice(TRAIT_SETTER_SEPARATOR) + TRAIT_SETTER_SEPARATOR.length
val end = name.indexOfSlice(SETTER_SUFFIX)
name.slice(start, end) ++ LOCAL_SUFFIX
} else 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
}
def lazyLocalName = name ++ nme.LAZY_LOCAL
def nonLazyName = {
assert(name.isLazyLocal)
name.dropRight(nme.LAZY_LOCAL.length)
}
}
}
|