aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/core/Names.scala
blob: 30d80a93806be9941b4eabca3a8a4aca98175c33 (plain) (tree)
1
2
3
4
5
6
7




                           
                
                   










                                                                                       
                                        









































                                                                                         
                                                                         
































































































































































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