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("") 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_. 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 + "_" } } 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 }