summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/internal/NameManglers.scala
blob: d4aa6339fff2df06223b172370eb492e680a3eca (plain) (blame)
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
/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.reflect
package internal

import java.security.MessageDigest
import scala.io.Codec
import Chars.isOperatorPart

/** A trait to encapsulate name mangling.  It's intended for the
 *  values and methods involved in assembling names out of other names,
 *  and not for simple synthetically named locals.
 */
trait NameManglers {
  self: SymbolTable =>

  trait NameManglingCommon {
    self: CommonNames =>

    val MODULE_SUFFIX_STRING = NameTransformer.MODULE_SUFFIX_STRING
    val NAME_JOIN_STRING     = NameTransformer.NAME_JOIN_STRING
    
    val MODULE_SUFFIX_NAME: TermName = MODULE_SUFFIX_STRING
    val NAME_JOIN_NAME: TermName     = NAME_JOIN_STRING
    
    def flattenedName(segments: Name*): NameType = compactedString(segments mkString NAME_JOIN_STRING)

    /**
     * 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:
     */
    private final val marker = "$$$$"
    private final val MaxNameLength = math.min(
      settings.maxClassfileName.value - 6,
      2 * (settings.maxClassfileName.value - 6 - 2*marker.length - 32)
    )
    private lazy val md5 = MessageDigest.getInstance("MD5")
    private def toMD5(s: String, edge: Int) = {
      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
    }
    private def compactedString(s: String) =
      if (s.length <= MaxNameLength) s
      else toMD5(s, MaxNameLength / 4)
  }

  trait TypeNameMangling extends NameManglingCommon {
    self: tpnme.type =>

  }

  trait TermNameMangling extends NameManglingCommon {
    self: nme.type =>

    val IMPL_CLASS_SUFFIX             = "$class"
    val LOCALDUMMY_PREFIX             = "<local "   // owner of local blocks
    val PROTECTED_PREFIX              = "protected$"
    val PROTECTED_SET_PREFIX          = PROTECTED_PREFIX + "set"
    val SETTER_SUFFIX                 = encode("_=")
    val SINGLETON_SUFFIX              = ".type"
    val SPECIALIZED_SUFFIX_STRING     = "$sp"
    val SUPER_PREFIX_STRING           = "super$"
    val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$"
    
    val SPECIALIZED_SUFFIX_NAME: TermName = SPECIALIZED_SUFFIX_STRING

    def isConstructorName(name: Name)       = name == CONSTRUCTOR || name == MIXIN_CONSTRUCTOR
    def isExceptionResultName(name: Name)   = name startsWith EXCEPTION_RESULT_PREFIX
    def isImplClassName(name: Name)         = stripAnonNumberSuffix(name) endsWith IMPL_CLASS_SUFFIX
    def isLocalDummyName(name: Name)        = name startsWith LOCALDUMMY_PREFIX
    def isLocalName(name: Name)             = name endsWith LOCAL_SUFFIX_STRING
    def isLoopHeaderLabel(name: Name)       = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX)
    def isProtectedAccessorName(name: Name) = name startsWith PROTECTED_PREFIX
    def isReplWrapperName(name: Name)       = name containsName INTERPRETER_IMPORT_WRAPPER
    def isSetterName(name: Name)            = name endsWith SETTER_SUFFIX
    def isTraitSetterName(name: Name)       = isSetterName(name) && (name containsName TRAIT_SETTER_SEPARATOR_STRING)
    def isSingletonName(name: Name)         = name endsWith SINGLETON_SUFFIX
    def isModuleName(name: Name)            = name endsWith MODULE_SUFFIX_NAME

    def isOpAssignmentName(name: Name) = name match {
      case raw.NE | raw.LE | raw.GE | EMPTY => false
      case _                                =>
        name.endChar == '=' && name.startChar != '=' && isOperatorPart(name.startChar)
    }

    /** The expanded setter name of `name` relative to this class `base`
     */
    def expandedSetterName(name: TermName, base: Symbol): TermName =
      expandedName(name, base, separator = TRAIT_SETTER_SEPARATOR_STRING)

    /** If `name` is an expandedName name, the original name.
     *  Otherwise `name` itself.
     */
    def originalName(name: Name): Name = {
      var i = name.length
      while (i >= 2 && !(name(i - 1) == '$' && name(i - 2) == '$')) i -= 1
      if (i >= 2) {
        while (i >= 3 && name(i - 3) == '$') i -= 1
        name.subName(i, name.length)
      } else name
    }
    
    def unspecializedName(name: Name): Name = (
      if (name endsWith SPECIALIZED_SUFFIX_NAME)
        name.subName(0, name.lastIndexOf('m') - 1)
      else name
    )

    /** Return the original name and the types on which this name
     *  is specialized. For example,
     *  {{{
     *     splitSpecializedName("foo$mIcD$sp") == ('foo', "I", "D")
     *  }}}
     *  `foo$mIcD$sp` is the name of a method specialized on two type
     *  parameters, the first one belonging to the method itself, on Int,
     *  and another one belonging to the enclosing class, on Double.
     */
    def splitSpecializedName(name: Name): (Name, String, String) =
      if (name endsWith SPECIALIZED_SUFFIX_NAME) {
        val name1 = name stripEnd SPECIALIZED_SUFFIX_NAME
        val idxC  = name1 lastIndexOf 'c'
        val idxM  = name1 lastIndexOf 'm'

        (name1.subName(0, idxM - 1),
         name1.subName(idxC + 1, name1.length).toString,
         name1.subName(idxM + 1, idxC).toString)
      } else
        (name, "", "")

    def getterName(name: TermName): TermName     = if (isLocalName(name)) localToGetter(name) else name
    def getterToLocal(name: TermName): TermName  = name.toTermName append LOCAL_SUFFIX_STRING
    def getterToSetter(name: TermName): TermName = name.toTermName append SETTER_SUFFIX
    def localToGetter(name: TermName): TermName  = name stripEnd LOCAL_SUFFIX_STRING toTermName
    
    def dropLocalSuffix(name: Name): Name  = if (name endsWith ' ') name dropRight 1 else name

    def setterToGetter(name: TermName): TermName = {
      val p = name.pos(TRAIT_SETTER_SEPARATOR_STRING)
      if (p < name.length)
        setterToGetter(name.subName(p + TRAIT_SETTER_SEPARATOR_STRING.length, name.length))
      else
        name stripEnd SETTER_SUFFIX toTermName
    }

    def defaultGetterName(name: Name, pos: Int): TermName = {
      val prefix = if (isConstructorName(name)) "init" else name
      newTermName(prefix + DEFAULT_GETTER_STRING + pos)
    }
    def defaultGetterToMethod(name: Name): TermName = {
      val p = name.pos(DEFAULT_GETTER_STRING)
      if (p < name.length) name.subName(0, p)
      else name
    }

    /** !!! I'm putting this logic in place because I can witness
     *  trait impls get lifted and acquiring names like 'Foo$class$1'
     *  while clearly still being what they were. It's only being used on
     *  isImplClassName. However, it's anyone's guess how much more
     *  widely this logic actually ought to be applied. Anything which
     *  tests for how a name ends is a candidate for breaking down once
     *  something is lifted from a method.
     *
     *  TODO: resolve this significant problem.
     */
    def stripAnonNumberSuffix(name: Name): Name = {
      val str = "" + name
      if (str == "" || !str.endChar.isDigit) name
      else {
        val idx = name.lastPos('$')
        if (idx < 0 || str.substring(idx + 1).exists(c => !c.isDigit)) name
        else name.subName(0, idx)
      }
    }

    def stripModuleSuffix(name: Name): Name = (
      if (isModuleName(name)) name stripEnd MODULE_SUFFIX_STRING else name
    )

    /** Note that for performance reasons, stripEnd does not verify that the
     *  suffix is actually the suffix specified.
     */
    def dropSingletonName(name: Name): TypeName = name stripEnd SINGLETON_SUFFIX toTypeName
    def singletonName(name: Name): TypeName     = name append SINGLETON_SUFFIX toTypeName
    def implClassName(name: Name): TypeName     = name append IMPL_CLASS_SUFFIX toTypeName
    def interfaceName(implname: Name): TypeName = implname stripEnd IMPL_CLASS_SUFFIX toTypeName
    def localDummyName(clazz: Symbol): TermName = newTermName(LOCALDUMMY_PREFIX + clazz.name + ">")
    def productAccessorName(i: Int): TermName   = newTermName("_" + i)
    def superName(name: Name): TermName         = newTermName(SUPER_PREFIX_STRING + name)

    /** The name of an accessor for protected symbols. */
    def protName(name: Name): TermName = newTermName(PROTECTED_PREFIX + name)

    /** The name of a setter for protected symbols. Used for inherited Java fields. */
    def protSetterName(name: Name): TermName = newTermName(PROTECTED_SET_PREFIX + name)
  }
}