package dotty.tools.dotc
package core
import scala.io.Codec
import util.NameTransformer
import Periods._
import Decorators._
object Names {
/** A name is essentially a string, with three differences
* 1. Names belong in one of two universes: they are type names or term names.
* The same string can correspond both to a type name and to a term name.
* 2. In each universe, names are hash-consed per basis. Two names
* representing the same string in the same basis are always reference identical.
* 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 {
/** The basis in which this name is stored */
val basis: NameTable
/** The start index in the character array */
val start: Int
/** The length of the names */
val length: Int
/** Is this name a type name? */
def isTypeName: Boolean
/** Is this name a term name? */
def isTermName: Boolean
/** This name converted to a type name */
def toTypeName: TypeName
/** This name converted to a term name */
def toTermName: TermName
/** This name downcasted to a type name */
def asTypeName: TypeName
/** This name downcasted to a term name */
def asTermName: TermName
/** This name in the given basis */
def in(basis: NameTable) =
if (this.basis eq basis) this
else newName(basis, this.basis.chrs, start, length)
/** Create a new name of same kind as this one, in the given
* basis, with `len` characters taken from `cs` starting at `offset`.
*/
protected def newName(basis: NameTable, cs: Array[Char], offset: Int, len: Int): Name
/** A dummy equals method to catch all comparisons of names
* to other entities (e.g. strings).
* One always should use the ==(Name) method instead.
*/
final override def equals(that: Any): Boolean = unsupported("equals")
/** The only authorized == method on names */
def == (that: Name): Boolean = (
(this eq that)
||
(this.basis ne that.basis) &&
(this == (that in this.basis))
)
override def toString = new String(basis.chrs, start, length)
/** 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(basis.chrs, start, length)
scala.compat.Platform.arraycopy(bytes, 0, bs, offset, bytes.length)
offset + bytes.length
}
/** Convert to string replacing operator symbols by corresponding \$op_name. */
def decode: String = NameTransformer.decode(toString)
}
class TermName(val basis: NameTable, val start: Int, val length: Int, val next: TermName) extends Name {
def isTypeName = false
def isTermName = true
lazy val toTypeName: TypeName = new TypeName(basis, start, length, this)
def toTermName = this
def asTypeName = throw new ClassCastException(this+" is not a type name")
def asTermName = this
protected def newName(basis: NameTable, cs: Array[Char], offset: Int, len: Int): Name =
basis.newTermName(cs, offset, len)
}
class TypeName(val basis: NameTable, val start: Int, val length: Int, val toTermName: TermName) extends Name {
def isTypeName = true
def isTermName = false
def toTypeName = this
def asTypeName = this
def asTermName = throw new ClassCastException(this+" is not a term name")
protected def newName(basis: NameTable, cs: Array[Char], offset: Int, len: Int): Name =
basis.newTypeName(cs, offset, len)
}
class NameTable {
private final val HASH_SIZE = 0x8000
private final val HASH_MASK = 0x7FFF
private final val NAME_SIZE = 0x20000
final val nameDebug = false
/** Memory to store all names sequentially. */
private[Names] var chrs: Array[Char] = new Array[Char](NAME_SIZE)
private var nc = 0
/** Hashtable for finding term names quickly. */
private val table = new Array[Names.TermName](HASH_SIZE)
/** The hashcode of a name. */
private def hashValue(cs: Array[Char], offset: Int, len: Int): Int =
if (len > 0)
(len * (41 * 41 * 41) +
cs(offset) * (41 * 41) +
cs(offset + len - 1) * 41 +
cs(offset + (len >> 1)))
else 0
/**
* Is (the ASCII representation of) name at given index equal to
* cs[offset..offset+len-1]?
*/
private def equals(index: Int, cs: Array[Char], offset: Int, len: Int): Boolean = {
var i = 0
while ((i < len) && (chrs(index + i) == cs(offset + i)))
i += 1;
i == len
}
/** Enter characters into chrs array. */
private def enterChars(cs: Array[Char], offset: Int, len: Int) {
var i = 0
while (i < len) {
if (nc + i == chrs.length) {
val newchrs = new Array[Char](chrs.length * 2)
scala.compat.Platform.arraycopy(chrs, 0, newchrs, 0, chrs.length)
chrs = newchrs
}
chrs(nc + i) = cs(offset + i)
i += 1
}
if (len == 0) nc += 1
else nc = nc + len
}
/**
* Create a term name from the characters in cs[offset..offset+len-1].
* Assume they are already encoded.
*/
def newTermName(cs: Array[Char], offset: Int, len: Int): TermName = /* sync if parallel */ {
val h = hashValue(cs, offset, len) & HASH_MASK
val next = table(h)
var name = next
while ((name ne null) && (name.length != len || !equals(name.start, cs, offset, len)))
name = name.next
if (name eq null) /* needs sync if parallel */ {
name = new TermName(this, nc, len, next)
enterChars(cs, offset, len)
table(h) = name
name
}
name
}
/**
* Create a type name from the characters in cs[offset..offset+len-1].
* Assume they are already encoded.
*/
def newTypeName(cs: Array[Char], offset: Int, len: Int): TypeName =
newTermName(cs, offset, len).toTypeName
/**
* Create a term name from the UTF8 encoded bytes in bs[offset..offset+len-1].
* Assume they are already encoded.
*/
def newTermName(bs: Array[Byte], offset: Int, len: Int): TermName = {
val chars = Codec.fromUTF8(bs, offset, len)
newTermName(chars, 0, chars.length)
}
/**
* Create a type name from the UTF8 encoded bytes in bs[offset..offset+len-1].
* Assume they are already encoded.
*/
def newTypeName(bs: Array[Byte], offset: Int, len: Int): TypeName =
newTermName(bs, offset, len).toTypeName
/** Create a term name from a string, encode if necessary*/
def newTermName(s: String): TermName = {
val es = NameTransformer.encode(s)
newTermName(es.toCharArray, 0, es.length)
}
/** Create a type name from a string, encode if necessary */
def newTypeName(s: String): TypeName = {
val es = NameTransformer.encode(s)
newTypeName(es.toCharArray, 0, es.length)
}
}
object BootNameTable extends NameTable
val EmptyTypeName = BootNameTable.newTypeName("")
val EmptyTermName = BootNameTable.newTermName("")
}