package dotty.tools
package dotc
package core
import Names._
import NameOps._
import StdNames._
import util.DotClass
import tasty.TastyFormat._
import Decorators._
import Contexts.Context
import collection.mutable
object NameKinds {
// These are sharable since all NameKinds are created eagerly at the start of the program
// before any concurrent threads are forked. for this to work, NameKinds should never
// be created lazily or in modules that start running after compilers are forked.
@sharable private val simpleNameKinds = new mutable.HashMap[Int, ClassifiedNameKind]
@sharable private val qualifiedNameKinds = new mutable.HashMap[Int, QualifiedNameKind]
@sharable private val uniqueNameKinds = new mutable.HashMap[String, UniqueNameKind]
abstract class NameInfo extends DotClass {
def kind: NameKind
def mkString(underlying: TermName): String
def map(f: SimpleTermName => SimpleTermName): NameInfo = this
}
abstract class NameKind(val tag: Int) extends DotClass { self =>
type ThisInfo <: Info
class Info extends NameInfo { this: ThisInfo =>
def kind = self
def mkString(underlying: TermName) = self.mkString(underlying, this)
override def toString = infoString
}
def definesNewName = false
def unmangle(name: SimpleTermName): TermName = name
def mkString(underlying: TermName, info: ThisInfo): String
def infoString: String
}
object SimpleTermNameKind extends NameKind(UTF8) { self =>
type ThisInfo = Info
val info = new Info
def mkString(underlying: TermName, info: ThisInfo) = unsupported("mkString")
def infoString = unsupported("infoString")
}
abstract class ClassifiedNameKind(tag: Int, val infoString: String) extends NameKind(tag) {
type ThisInfo = Info
val info = new Info
def apply(qual: TermName) =
qual.derived(info)
def unapply(name: DerivedTermName): Option[TermName] = name match {
case DerivedTermName(underlying, `info`) => Some(underlying)
case _ => None
}
simpleNameKinds(tag) = this
}
class PrefixNameKind(tag: Int, prefix: String, optInfoString: String = "")
extends ClassifiedNameKind(tag, if (optInfoString.isEmpty) s"Prefix $prefix" else optInfoString) {
def mkString(underlying: TermName, info: ThisInfo) =
underlying.mapLast(n => termName(prefix + n.toString)).toString
override def unmangle(name: SimpleTermName): TermName =
if (name.startsWith(prefix)) apply(name.drop(prefix.length).asSimpleName)
else name
}
class SuffixNameKind(tag: Int, suffix: String, optInfoString: String = "")
extends ClassifiedNameKind(tag, if (optInfoString.isEmpty) s"Suffix $suffix" else optInfoString) {
def mkString(underlying: TermName, info: ThisInfo) = underlying.toString ++ suffix
override def unmangle(name: SimpleTermName): TermName =
if (name.endsWith(suffix)) apply(name.take(name.length - suffix.length).asSimpleName)
else name
}
trait QualifiedInfo extends NameInfo {
val name: SimpleTermName
}
class QualifiedNameKind(tag: Int, val separator: String)
extends NameKind(tag) {
type ThisInfo = QualInfo
case class QualInfo(val name: SimpleTermName) extends Info with QualifiedInfo {
override def map(f: SimpleTermName => SimpleTermName): NameInfo = new QualInfo(f(name))
override def toString = s"$infoString $name"
}
def apply(qual: TermName, name: SimpleTermName): TermName =
qual.derived(new QualInfo(name))
/** Overloaded version used only for ExpandedName and TraitSetterName.
* Needed because the suffix of an expanded name may itself be expanded.
* For example, look at javap of scala.App.initCode
*/
def apply(qual: TermName, name: TermName): TermName = name rewrite {
case name: SimpleTermName => apply(qual, name)
case AnyQualifiedName(_, _) => apply(qual, name.toSimpleName)
}
def unapply(name: DerivedTermName): Option[(TermName, SimpleTermName)] = name match {
case DerivedTermName(qual, info: this.QualInfo) => Some((qual, info.name))
case _ => None
}
override def definesNewName = true
def mkString(underlying: TermName, info: ThisInfo) =
s"$underlying$separator${info.name}"
def infoString = s"Qualified $separator"
qualifiedNameKinds(tag) = this
}
object AnyQualifiedName {
def unapply(name: DerivedTermName): Option[(TermName, SimpleTermName)] = name match {
case DerivedTermName(qual, info: QualifiedInfo) =>
Some((name.underlying, info.name))
case _ => None
}
}
trait NumberedInfo extends NameInfo {
def num: Int
}
abstract class NumberedNameKind(tag: Int, val infoString: String) extends NameKind(tag) { self =>
type ThisInfo = NumberedInfo
case class NumberedInfo(val num: Int) extends Info with NameKinds.NumberedInfo {
override def toString = s"$infoString $num"
}
def apply(qual: TermName, num: Int) =
qual.derived(new NumberedInfo(num))
def unapply(name: DerivedTermName): Option[(TermName, Int)] = name match {
case DerivedTermName(underlying, info: this.NumberedInfo) => Some((underlying, info.num))
case _ => None
}
protected def skipSeparatorAndNum(name: SimpleTermName, separator: String): Int = {
var i = name.length
while (i > 0 && name(i - 1).isDigit) i -= 1
if (i > separator.length && i < name.length &&
name.slice(i - separator.length, i).toString == separator) i
else -1
}
}
object AnyNumberedName {
def unapply(name: DerivedTermName): Option[(TermName, Int)] = name match {
case DerivedTermName(qual, info: NumberedInfo) => Some((qual, info.num))
case _ => None
}
}
case class UniqueNameKind(val separator: String)
extends NumberedNameKind(UNIQUE, s"Unique $separator") {
override def definesNewName = true
def mkString(underlying: TermName, info: ThisInfo) = {
val safePrefix = str.sanitize(underlying.toString + separator)
safePrefix + info.num
}
def fresh(prefix: TermName = EmptyTermName)(implicit ctx: Context): TermName =
ctx.freshNames.newName(prefix, this)
uniqueNameKinds(separator) = this
}
object AnyUniqueName {
def unapply(name: DerivedTermName): Option[(TermName, String, Int)] = name match {
case DerivedTermName(qual, info: NumberedInfo) =>
info.kind match {
case unique: UniqueNameKind => Some((qual, unique.separator, info.num))
case _ => None
}
case _ => None
}
}
val QualifiedName = new QualifiedNameKind(QUALIFIED, ".")
val FlatName = new QualifiedNameKind(FLATTENED, "$")
val ExpandPrefixName = new QualifiedNameKind(EXPANDPREFIX, "$")
val ExpandedName = new QualifiedNameKind(EXPANDED, str.EXPAND_SEPARATOR) {
private val FalseSuper = termName("$$super")
private val FalseSuperLength = FalseSuper.length
override def unmangle(name: SimpleTermName): TermName = {
var i = name.lastIndexOfSlice(str.EXPAND_SEPARATOR)
if (i < 0) name
else {
// Hack to make super accessors from traits work. They would otherwise fail because of #765
// The problem is that in `x$$super$$plus` the expansion prefix needs to be `x`
// instead of `x$$super`.
if (i > FalseSuperLength && name.slice(i - FalseSuperLength, i) == FalseSuper)
i -= FalseSuper.length
apply(name.take(i).asTermName, name.drop(i + str.EXPAND_SEPARATOR.length).asSimpleName)
}
}
}
val TraitSetterName = new QualifiedNameKind(TRAITSETTER, str.TRAIT_SETTER_SEPARATOR)
val UniqueName = new UniqueNameKind("$") {
override def mkString(underlying: TermName, info: ThisInfo) =
if (underlying.isEmpty) "$" + info.num + "$" else super.mkString(underlying, info)
}
val InlineAccessorName = new UniqueNameKind("$_inlineAccessor_$")
val TempResultName = new UniqueNameKind("ev$")
val EvidenceParamName = new UniqueNameKind("evidence$")
val DepParamName = new UniqueNameKind("(param)")
val LazyImplicitName = new UniqueNameKind("$_lazy_implicit_$")
val LazyLocalName = new UniqueNameKind("$lzy")
val LazyLocalInitName = new UniqueNameKind("$lzyINIT")
val LazyFieldOffsetName = new UniqueNameKind("$OFFSET")
val LazyBitMapName = new UniqueNameKind(nme.BITMAP_PREFIX.toString)
val NonLocalReturnKeyName = new UniqueNameKind("nonLocalReturnKey")
val WildcardParamName = new UniqueNameKind("_$")
val TailLabelName = new UniqueNameKind("tailLabel")
val ExceptionBinderName = new UniqueNameKind("ex")
val SkolemName = new UniqueNameKind("?")
val LiftedTreeName = new UniqueNameKind("liftedTree")
val UniqueExtMethName = new UniqueNameKind("$extension") {
override def unmangle(name: SimpleTermName): TermName = {
val i = skipSeparatorAndNum(name, separator)
if (i > 0) {
val index = name.drop(i).toString.toInt
var original = name.take(i - separator.length).asTermName
apply(original, index)
}
else name
}
}
val PatMatStdBinderName = new UniqueNameKind("x")
val PatMatPiName = new UniqueNameKind("pi") // FIXME: explain what this is
val PatMatPName = new UniqueNameKind("p") // FIXME: explain what this is
val PatMatOName = new UniqueNameKind("o") // FIXME: explain what this is
val PatMatCaseName = new UniqueNameKind("case")
val PatMatMatchFailName = new UniqueNameKind("matchFail")
val PatMatSelectorName = new UniqueNameKind("selector")
object DefaultGetterName extends NumberedNameKind(DEFAULTGETTER, "DefaultGetter") {
def mkString(underlying: TermName, info: ThisInfo) = {
val prefix = if (underlying.isConstructorName) nme.DEFAULT_GETTER_INIT else underlying
prefix.toString + str.DEFAULT_GETTER + (info.num + 1)
}
// TODO: Reduce code duplication with UniqueExtMethName
override def unmangle(name: SimpleTermName): TermName = {
val i = skipSeparatorAndNum(name, str.DEFAULT_GETTER)
if (i > 0) {
val index = name.drop(i).toString.toInt - 1
var original = name.take(i - str.DEFAULT_GETTER.length).asTermName
if (original == nme.DEFAULT_GETTER_INIT) original = Names.CONSTRUCTOR
apply(original, index)
}
else name
}
}
object VariantName extends NumberedNameKind(VARIANT, "Variant") {
val varianceToPrefix = Map(-1 -> '-', 0 -> '=', 1 -> '+')
def mkString(underlying: TermName, info: ThisInfo) = {
varianceToPrefix(info.num).toString + underlying
}
}
/** Names of the form N_<outer>. Emitted by inliner, replaced by outer path
* in ExplicitOuter.
*/
object OuterSelectName extends NumberedNameKind(OUTERSELECT, "OuterSelect") {
def mkString(underlying: TermName, info: ThisInfo) = {
assert(underlying.isEmpty)
info.num + "_<outer>"
}
}
val SuperAccessorName = new PrefixNameKind(SUPERACCESSOR, "super$")
val InitializerName = new PrefixNameKind(INITIALIZER, "initial$")
val ShadowedName = new PrefixNameKind(SHADOWED, "(shadowed)")
val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$")
val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected$set") // dubious encoding, kept for Scala2 compatibility
val AvoidClashName = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
val DirectName = new SuffixNameKind(DIRECT, "$direct")
val FieldName = new SuffixNameKind(FIELD, "$$local")
val ExtMethName = new SuffixNameKind(EXTMETH, "$extension")
val ModuleVarName = new SuffixNameKind(OBJECTVAR, "$module")
val ModuleClassName = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass")
object SignedName extends NameKind(63) {
/** @param parts resultSig followed by paramsSig */
case class SignedInfo(sig: Signature) extends Info {
override def toString = s"$infoString $sig"
}
type ThisInfo = SignedInfo
def apply(qual: TermName, sig: Signature) =
qual.derived(new SignedInfo(sig))
def unapply(name: DerivedTermName): Option[(TermName, Signature)] = name match {
case DerivedTermName(underlying, info: SignedInfo) => Some((underlying, info.sig))
case _ => None
}
def mkString(underlying: TermName, info: ThisInfo): String = unsupported("mkString")
def infoString: String = "Signed"
}
val Scala2MethodNameKinds: List[NameKind] =
List(DefaultGetterName, ExtMethName, UniqueExtMethName, ProtectedAccessorName, ProtectedSetterName)
def simpleNameKindOfTag : collection.Map[Int, ClassifiedNameKind] = simpleNameKinds
def qualifiedNameKindOfTag : collection.Map[Int, QualifiedNameKind] = qualifiedNameKinds
def uniqueNameKindOfSeparator: collection.Map[String, UniqueNameKind] = uniqueNameKinds
}