diff options
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core/Names.scala')
-rw-r--r-- | compiler/src/dotty/tools/dotc/core/Names.scala | 455 |
1 files changed, 330 insertions, 125 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 11f0b55a8..a72a02844 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -10,13 +10,16 @@ import Decorators._ import Contexts.Context import collection.IndexedSeqOptimized import collection.generic.CanBuildFrom -import collection.mutable.{ Builder, StringBuilder } +import collection.mutable.{ Builder, StringBuilder, AnyRefMap } import collection.immutable.WrappedString import collection.generic.CanBuildFrom -import util.DotClass +import util.{DotClass, SimpleMap} +import java.util.HashMap + //import annotation.volatile object Names { + import NameKinds._ /** A common class for things that can be turned into names. * Instances are both names and strings, the latter via a decorator. @@ -37,20 +40,11 @@ object Names { * 3. Names are intended to be encoded strings. @see dotc.util.NameTransformer. * The encoding will be applied when converting a string to a name. */ - abstract class Name extends DotClass - with PreName - with collection.immutable.Seq[Char] - with IndexedSeqOptimized[Char, Name] { + abstract class Name extends DotClass with PreName { /** A type for names of the same kind as this name */ type ThisName <: Name - /** The start index in the character array */ - val start: Int - - /** The length of the names */ - override val length: Int - /** Is this name a type name? */ def isTypeName: Boolean @@ -69,120 +63,244 @@ object Names { /** This name downcasted to a term name */ def asTermName: TermName - /** Create a new name of same kind as this one, in the given - * basis, with `len` characters taken from `cs` starting at `offset`. - */ - def fromChars(cs: Array[Char], offset: Int, len: Int): ThisName + def isSimple: Boolean + def asSimpleName: SimpleTermName + def toSimpleName: SimpleTermName + def mangled: Name - /** Create new name of same kind as this name and with same - * characters as given `name`. - */ - def fromName(name: Name): ThisName = fromChars(chrs, name.start, name.length) + def rewrite(f: PartialFunction[Name, Name]): ThisName + def collect[T](f: PartialFunction[Name, T]): Option[T] + def mapLast(f: SimpleTermName => SimpleTermName): ThisName + def mapParts(f: SimpleTermName => SimpleTermName): ThisName - /** Create new name of same kind as this name with characters from - * the given string + /** A name in the same (term or type) namespace as this name and + * with same characters as given `name`. */ - def fromString(str: String): ThisName = { - val cs = str.toCharArray - fromChars(cs, 0, cs.length) - } + def likeSpaced(name: Name): ThisName - override def toString = - if (length == 0) "" else new String(chrs, start, length) + def derived(info: NameInfo): ThisName + def derived(kind: ClassifiedNameKind): ThisName = derived(kind.info) + def exclude(kind: NameKind): ThisName + def is(kind: NameKind): Boolean + def debugString: String def toText(printer: Printer): Text = printer.toText(this) - /** Write to UTF8 representation of this name to given character array. - * Start copying to index `to`. Return index of next free byte in array. - * Array must have enough remaining space for all bytes - * (i.e. maximally 3*length bytes). - */ - final def copyUTF8(bs: Array[Byte], offset: Int): Int = { - val bytes = Codec.toUTF8(chrs, start, length) - scala.compat.Platform.arraycopy(bytes, 0, bs, offset, bytes.length) - offset + bytes.length - } - /** Replace \$op_name's by corresponding operator symbols. */ - def decode: Name = - if (contains('$')) fromString(NameTransformer.decode(toString)) - else this + def decode: Name /** Replace operator symbols by corresponding \$op_name's. */ - def encode: Name = - if (dontEncode(toTermName)) this else NameTransformer.encode(this) + def encode: Name + + def firstPart: SimpleTermName + def lastPart: SimpleTermName /** A more efficient version of concatenation */ def ++ (other: Name): ThisName = ++ (other.toString) + def ++ (other: String): ThisName = mapLast(n => termName(n.toString + other)) + def replace(from: Char, to: Char): ThisName = mapParts(_.replace(from, to)) + + def isEmpty: Boolean + + def startsWith(str: String): Boolean = firstPart.startsWith(str) + def endsWith(str: String): Boolean = lastPart.endsWith(str) + + override def hashCode = System.identityHashCode(this) + override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + } - def ++ (other: String): ThisName = { - val s = toString + other - fromChars(s.toCharArray, 0, s.length) + abstract class TermName extends Name { + type ThisName = TermName + def isTypeName = false + def isTermName = true + def toTermName = this + def asTypeName = throw new ClassCastException(this + " is not a type name") + def asTermName = this + + @sharable // because it is only modified in the synchronized block of toTypeName. + @volatile private[this] var _typeName: TypeName = null + + def toTypeName: TypeName = { + if (_typeName == null) + synchronized { + if (_typeName == null) + _typeName = new TypeName(this) + } + _typeName } - def replace(from: Char, to: Char): ThisName = { - val cs = new Array[Char](length) - Array.copy(chrs, start, cs, 0, length) - for (i <- 0 until length) { - if (cs(i) == from) cs(i) = to + def likeSpaced(name: Name): TermName = name.toTermName + + def info: NameInfo = SimpleTermNameKind.info + def underlying: TermName = unsupported("underlying") + + @sharable // because of synchronized block in `and` + private var derivedNames: AnyRef /* SimpleMap | j.u.HashMap */ = + SimpleMap.Empty[NameInfo] + + private def getDerived(info: NameInfo): DerivedTermName /* | Null */= derivedNames match { + case derivedNames: SimpleMap[NameInfo, DerivedTermName] @unchecked => + derivedNames(info) + case derivedNames: HashMap[NameInfo, DerivedTermName] @unchecked => + derivedNames.get(info) + } + + private def putDerived(info: NameInfo, name: DerivedTermName): name.type = { + derivedNames match { + case derivedNames: SimpleMap[NameInfo, DerivedTermName] @unchecked => + if (derivedNames.size < 4) + this.derivedNames = derivedNames.updated(info, name) + else { + val newMap = new HashMap[NameInfo, DerivedTermName] + derivedNames.foreachBinding(newMap.put(_, _)) + newMap.put(info, name) + this.derivedNames = newMap + } + case derivedNames: HashMap[NameInfo, DerivedTermName] @unchecked => + derivedNames.put(info, name) + } + name + } + + private def add(info: NameInfo): TermName = synchronized { + getDerived(info) match { + case null => putDerived(info, new DerivedTermName(this, info)) + case derivedName => derivedName + } + } + + private def rewrap(underlying: TermName) = + if (underlying eq this.underlying) this else underlying.add(info) + + /** Return derived name with given `info` and the current + * name as underlying name. + */ + def derived(info: NameInfo): TermName = { + val thisKind = this.info.kind + val thatKind = info.kind + if (thisKind.tag < thatKind.tag || thatKind.definesNewName) add(info) + else if (thisKind.tag > thatKind.tag) rewrap(underlying.derived(info)) + else { + assert(info == this.info) + this } - fromChars(cs, 0, length) } + def exclude(kind: NameKind): TermName = { + val thisKind = this.info.kind + if (thisKind.tag < kind.tag || thisKind.definesNewName) this + else if (thisKind.tag > kind.tag) rewrap(underlying.exclude(kind)) + else underlying + } + + def is(kind: NameKind): Boolean = { + val thisKind = this.info.kind + thisKind == kind || + !thisKind.definesNewName && thisKind.tag > kind.tag && underlying.is(kind) + } + } + + class SimpleTermName(val start: Int, val length: Int, @sharable private[Names] var next: SimpleTermName) extends TermName { + // `next` is @sharable because it is only modified in the synchronized block of termName. + + def apply(n: Int) = chrs(start + n) + + def exists(p: Char => Boolean): Boolean = { + var i = 0 + while (i < length && !p(chrs(start + i))) i += 1 + i < length + } + + def forall(p: Char => Boolean) = !exists(!p(_)) + def contains(ch: Char): Boolean = { var i = 0 while (i < length && chrs(start + i) != ch) i += 1 i < length } - def firstChar = chrs(start) + def isEmpty = length == 0 + + override def startsWith(str: String): Boolean = { + var i = 0 + while (i < str.length && i < length && apply(i) == str(i)) i += 1 + i == str.length + } + + override def endsWith(str: String): Boolean = { + var i = 1 + while (i <= str.length && i <= length && apply(length - i) == str(str.length - i)) i += 1 + i > str.length + } - // ----- Collections integration ------------------------------------- + def lastIndexOf(ch: Char, start: Int = length - 1): Int = { + var i = start + while (i >= 0 && apply(i) != ch) i -= 1 + i + } - override protected[this] def thisCollection: WrappedString = new WrappedString(repr.toString) - override protected[this] def toCollection(repr: Name): WrappedString = new WrappedString(repr.toString) + def lastIndexOfSlice(str: String): Int = toString.lastIndexOfSlice(str) - override protected[this] def newBuilder: Builder[Char, Name] = unsupported("newBuilder") + override def replace(from: Char, to: Char): SimpleTermName = { + val cs = new Array[Char](length) + Array.copy(chrs, start, cs, 0, length) + for (i <- 0 until length) { + if (cs(i) == from) cs(i) = to + } + termName(cs, 0, length) + } - override def apply(index: Int): Char = chrs(start + index) + def slice(from: Int, until: Int): SimpleTermName = { + assert(0 <= from && from <= until && until <= length) + termName(chrs, start + from, until - from) + } - override def slice(from: Int, until: Int): ThisName = - fromChars(chrs, start + from, until - from) + def drop(n: Int) = slice(n, length) + def take(n: Int) = slice(0, n) + def dropRight(n: Int) = slice(0, length - n) + def takeRight(n: Int) = slice(length - n, length) - override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + def head = apply(0) + def last = apply(length - 1) - override def seq = toCollection(this) - } + def isSimple = true + def asSimpleName = this + def toSimpleName = this + def mangled = this - class TermName(val start: Int, val length: Int, @sharable private[Names] var next: TermName) extends Name { - // `next` is @sharable because it is only modified in the synchronized block of termName. - type ThisName = TermName - def isTypeName = false - def isTermName = true + def rewrite(f: PartialFunction[Name, Name]): ThisName = + if (f.isDefinedAt(this)) likeSpaced(f(this)) else this + def collect[T](f: PartialFunction[Name, T]): Option[T] = f.lift(this) + def mapLast(f: SimpleTermName => SimpleTermName) = f(this) + def mapParts(f: SimpleTermName => SimpleTermName) = f(this) - @sharable // because it is only modified in the synchronized block of toTypeName. - @volatile private[this] var _typeName: TypeName = null + def encode: SimpleTermName = + if (dontEncode(toTermName)) this else NameTransformer.encode(this) - def toTypeName: TypeName = { - if (_typeName == null) - synchronized { - if (_typeName == null) - _typeName = new TypeName(start, length, this) - } - _typeName - } - def toTermName = this - def asTypeName = throw new ClassCastException(this + " is not a type name") - def asTermName = this + /** Replace \$op_name's by corresponding operator symbols. */ + def decode: SimpleTermName = + if (contains('$')) termName(NameTransformer.decode(toString)) else this + + def firstPart = this + def lastPart = this override def hashCode: Int = start - override protected[this] def newBuilder: Builder[Char, Name] = termNameBuilder + override def toString = + if (length == 0) "" else new String(chrs, start, length) - def fromChars(cs: Array[Char], offset: Int, len: Int): TermName = termName(cs, offset, len) + def debugString: String = toString } - class TypeName(val start: Int, val length: Int, val toTermName: TermName) extends Name { + class TypeName(val toTermName: TermName) extends Name { + + def isEmpty = toTermName.isEmpty + + def encode = toTermName.encode.toTypeName + def decode = toTermName.decode.toTypeName + def firstPart = toTermName.firstPart + def lastPart = toTermName.lastPart + type ThisName = TypeName def isTypeName = true def isTermName = false @@ -190,12 +308,77 @@ object Names { def asTypeName = this def asTermName = throw new ClassCastException(this + " is not a term name") - override def hashCode: Int = -start + def isSimple = toTermName.isSimple + def asSimpleName = toTermName.asSimpleName + def toSimpleName = toTermName.toSimpleName + def mangled = toTermName.toSimpleName.toTypeName - override protected[this] def newBuilder: Builder[Char, Name] = - termNameBuilder.mapResult(_.toTypeName) + def rewrite(f: PartialFunction[Name, Name]): ThisName = toTermName.rewrite(f).toTypeName + def collect[T](f: PartialFunction[Name, T]): Option[T] = toTermName.collect(f) + def mapLast(f: SimpleTermName => SimpleTermName) = toTermName.mapLast(f).toTypeName + def mapParts(f: SimpleTermName => SimpleTermName) = toTermName.mapParts(f).toTypeName - def fromChars(cs: Array[Char], offset: Int, len: Int): TypeName = typeName(cs, offset, len) + def likeSpaced(name: Name): TypeName = name.toTypeName + + def derived(info: NameInfo): TypeName = toTermName.derived(info).toTypeName + def exclude(kind: NameKind): TypeName = toTermName.exclude(kind).toTypeName + def is(kind: NameKind) = toTermName.is(kind) + + override def toString = toTermName.toString + override def debugString = toTermName.debugString + "/T" + } + + /** A term name that's derived from an `underlying` name and that + * adds `info` to it. + */ + case class DerivedTermName(override val underlying: TermName, override val info: NameInfo) + extends TermName { + def isEmpty = false + def encode: Name = underlying.encode.derived(info.map(_.encode)) + def decode: Name = underlying.decode.derived(info.map(_.decode)) + def firstPart = underlying.firstPart + def lastPart = info match { + case qual: QualifiedInfo => qual.name + case _ => underlying.lastPart + } + override def toString = info.mkString(underlying) + override def debugString = s"${underlying.debugString}[$info]" + + def isSimple = false + def asSimpleName = throw new UnsupportedOperationException(s"$debugString is not a simple name") + + private[this] var simpleName: SimpleTermName = null + def toSimpleName = { + if (simpleName == null) simpleName = termName(toString) + simpleName + } + def mangled = toSimpleName + + def rewrite(f: PartialFunction[Name, Name]): ThisName = + if (f.isDefinedAt(this)) likeSpaced(f(this)) + else info match { + case qual: QualifiedInfo => this + case _ => underlying.rewrite(f).derived(info) + } + + def collect[T](f: PartialFunction[Name, T]): Option[T] = + if (f.isDefinedAt(this)) Some(f(this)) + else info match { + case qual: QualifiedInfo => None + case _ => underlying.collect(f) + } + + def mapLast(f: SimpleTermName => SimpleTermName): ThisName = + info match { + case qual: QualifiedInfo => underlying.derived(qual.map(f)) + case _ => underlying.mapLast(f).derived(info) + } + + def mapParts(f: SimpleTermName => SimpleTermName): ThisName = + info match { + case qual: QualifiedInfo => underlying.mapParts(f).derived(qual.map(f)) + case _ => underlying.mapParts(f).derived(info) + } } // Nametable @@ -214,7 +397,7 @@ object Names { /** Hashtable for finding term names quickly. */ @sharable // because it's only mutated in synchronized block of termName - private var table = new Array[TermName](InitialHashSize) + private var table = new Array[SimpleTermName](InitialHashSize) /** The number of defined names. */ @sharable // because it's only mutated in synchronized block of termName @@ -242,7 +425,7 @@ object Names { /** Create a term name from the characters in cs[offset..offset+len-1]. * Assume they are already encoded. */ - def termName(cs: Array[Char], offset: Int, len: Int): TermName = synchronized { + def termName(cs: Array[Char], offset: Int, len: Int): SimpleTermName = synchronized { util.Stats.record("termName") val h = hashValue(cs, offset, len) & (table.size - 1) @@ -266,7 +449,7 @@ object Names { } /** Rehash chain of names */ - def rehash(name: TermName): Unit = + def rehash(name: SimpleTermName): Unit = if (name != null) { val oldNext = name.next val h = hashValue(chrs, name.start, name.length) & (table.size - 1) @@ -280,7 +463,7 @@ object Names { size += 1 if (size.toDouble / table.size > fillFactor) { val oldTable = table - table = new Array[TermName](table.size * 2) + table = new Array[SimpleTermName](table.size * 2) for (i <- 0 until oldTable.size) rehash(oldTable(i)) } } @@ -292,7 +475,7 @@ object Names { return name name = name.next } - name = new TermName(nc, len, next) + name = new SimpleTermName(nc, len, next) enterChars() table(h) = name incTableSize() @@ -308,7 +491,7 @@ object Names { /** Create a term name from the UTF8 encoded bytes in bs[offset..offset+len-1]. * Assume they are already encoded. */ - def termName(bs: Array[Byte], offset: Int, len: Int): TermName = { + def termName(bs: Array[Byte], offset: Int, len: Int): SimpleTermName = { val chars = Codec.fromUTF8(bs, offset, len) termName(chars, 0, chars.length) } @@ -320,53 +503,75 @@ object Names { termName(bs, offset, len).toTypeName /** Create a term name from a string, without encoding operators */ - def termName(s: String): TermName = termName(s.toCharArray, 0, s.length) + def termName(s: String): SimpleTermName = termName(s.toCharArray, 0, s.length) /** Create a type name from a string, without encoding operators */ def typeName(s: String): TypeName = typeName(s.toCharArray, 0, s.length) - /** The term name represented by the empty string */ - val EmptyTermName = new TermName(-1, 0, null) + table(0) = new SimpleTermName(-1, 0, null) - table(0) = EmptyTermName + /** The term name represented by the empty string */ + val EmptyTermName: TermName = table(0) /** The type name represented by the empty string */ val EmptyTypeName = EmptyTermName.toTypeName // can't move CONSTRUCTOR/EMPTY_PACKAGE to `nme` because of bootstrap failures in `encode`. - val CONSTRUCTOR = termName("<init>") - val STATIC_CONSTRUCTOR = termName("<clinit>") - val EMPTY_PACKAGE = termName("<empty>") - - val dontEncode = Set(CONSTRUCTOR, EMPTY_PACKAGE) + val CONSTRUCTOR: TermName = termName("<init>") + val STATIC_CONSTRUCTOR: TermName = termName("<clinit>") + val EMPTY_PACKAGE: TermName = termName("<empty>") + val REFINEMENT: TermName = termName("<refinement>") - def termNameBuilder: Builder[Char, TermName] = - StringBuilder.newBuilder.mapResult(termName) - - implicit val nameCanBuildFrom: CanBuildFrom[Name, Char, Name] = new CanBuildFrom[Name, Char, Name] { - def apply(from: Name): Builder[Char, Name] = - StringBuilder.newBuilder.mapResult(s => from.fromChars(s.toCharArray, 0, s.length)) - def apply(): Builder[Char, Name] = termNameBuilder - } + val dontEncode = Set(CONSTRUCTOR, EMPTY_PACKAGE, REFINEMENT) implicit val NameOrdering: Ordering[Name] = new Ordering[Name] { + private def compareInfos(x: NameInfo, y: NameInfo): Int = + if (x.kind.tag != y.kind.tag) x.kind.tag - y.kind.tag + else x match { + case x: QualifiedInfo => + y match { + case y: QualifiedInfo => + compareSimpleNames(x.name, y.name) + } + case x: NumberedInfo => + y match { + case y: NumberedInfo => + x.num - y.num + } + case _ => + assert(x == y) + 0 + } + private def compareSimpleNames(x: SimpleTermName, y: SimpleTermName): Int = { + val until = x.length min y.length + var i = 0 + while (i < until && x(i) == y(i)) i = i + 1 + if (i < until) { + if (x(i) < y(i)) -1 + else /*(x(i) > y(i))*/ 1 + } else { + x.length - y.length + } + } + private def compareTermNames(x: TermName, y: TermName): Int = x match { + case x: SimpleTermName => + y match { + case y: SimpleTermName => compareSimpleNames(x, y) + case _ => -1 + } + case DerivedTermName(xPre, xInfo) => + y match { + case DerivedTermName(yPre, yInfo) => + val s = compareInfos(xInfo, yInfo) + if (s == 0) compareTermNames(xPre, yPre) else s + case _ => 1 + } + } def compare(x: Name, y: Name): Int = { if (x.isTermName && y.isTypeName) 1 else if (x.isTypeName && y.isTermName) -1 else if (x eq y) 0 - else { - val until = x.length min y.length - var i = 0 - - while (i < until && x(i) == y(i)) i = i + 1 - - if (i < until) { - if (x(i) < y(i)) -1 - else /*(x(i) > y(i))*/ 1 - } else { - x.length - y.length - } - } + else compareTermNames(x.toTermName, y.toTermName) } } } |