Skip to content

Support name table sharing across Global instances #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import io.{AbstractFile, SourceReader}
import util.{ClassPath, returning}
import reporters.{Reporter => LegacyReporter}
import scala.reflect.ClassTag
import scala.reflect.internal.{Reporter => InternalReporter}
import scala.reflect.internal.{NameTable, Reporter => InternalReporter}
import scala.reflect.internal.util.{BatchSourceFile, FreshNameCreator, NoSourceFile, ScriptSourceFile, SourceFile}
import scala.reflect.internal.pickling.PickleBuffer
import symtab.{Flags, SymbolTable, SymbolTrackers}
Expand All @@ -41,6 +41,7 @@ import scala.tools.nsc.classpath._
import scala.tools.nsc.profile.Profiler
import scala.util.control.NonFatal
import java.io.Closeable

import scala.annotation.tailrec

class Global(var currentSettings: Settings, reporter0: LegacyReporter)
Expand All @@ -60,6 +61,13 @@ class Global(var currentSettings: Settings, reporter0: LegacyReporter)

override def isCompilerUniverse = true
override val useOffsetPositions = !currentSettings.Yrangepos
override protected def newNameTable: NameTable = {
if (currentSettings.YcacheNameTable) {
val NoFiles = Nil
Global.nameTableCache.getOrCreate(NoFiles, () => new NameTable(synchronizeNames = true), closeableRegistry, checkStamps = true)
}
else super.newNameTable
}

type RuntimeClass = java.lang.Class[_]
implicit val RuntimeClassTag: ClassTag[RuntimeClass] = ClassTag[RuntimeClass](classOf[RuntimeClass])
Expand Down Expand Up @@ -1743,4 +1751,7 @@ object Global {
override def keepsTypeParams = false
def run(): Unit = { throw new Error("InitPhase.run") }
}

// TODO factor reference counting caching out of FileBasedCache for use in spots like this.
private val nameTableCache = new FileBasedCache[NameTable]
}
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ trait ScalaSettings extends AbsScalaSettings
val YcachePluginClassLoader = CachePolicy.setting("plugin", "compiler plugins")
val YcacheMacroClassLoader = CachePolicy.setting("macro", "macros")
val YmacroClasspath = PathSetting ("-Ymacro-classpath", "The classpath used to reflectively load macro implementations, default is the compilation classpath.", "")
val YcacheNameTable = BooleanSetting ("-Ycache-name-table", "Share a single name table for concurrently running instances of the compiler")

val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method")
Expand Down
106 changes: 63 additions & 43 deletions src/reflect/scala/reflect/api/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,75 @@ trait Names {
@deprecated("use explicit `TypeName(s)` instead", "2.11.0")
implicit def stringToTypeName(s: String): TypeName = TypeName(s)

type NameTable <: NameTableApi

val nameTable: NameTable

/** The abstract type of names.
* @group Names
*/
type Name >: Null <: AnyRef with NameApi
type Name >: Null <: AnyRef with nameTable.NameApi

/** The abstract type of names representing terms.
* @group Names
*/
type TypeName >: Null <: nameTable.TypeNameApi with Name

/** The abstract type of names representing types.
* @group Names
*/
type TermName >: Null <: nameTable.TermNameApi with Name

/** The API of Name instances.
* @group API
*/
type NameApi <: nameTable.NameApi

/** Create a new term name.
* @group Names
*/
@deprecated("use TermName instead", "2.11.0")
def newTermName(s: String): TermName

/** Creates a new type name.
* @group Names
*/
@deprecated("use TypeName instead", "2.11.0")
def newTypeName(s: String): TypeName

/** The constructor/extractor for `TermName` instances.
* @group Extractors
*/
val TermName: TermNameExtractor

/** An extractor class to create and pattern match with syntax `TermName(s)`.
* @group Extractors
*/
abstract class TermNameExtractor {
def apply(s: String): TermName
def unapply(name: TermName): Option[String]
}

/** The constructor/extractor for `TypeName` instances.
* @group Extractors
*/
val TypeName: TypeNameExtractor

/** An extractor class to create and pattern match with syntax `TypeName(s)`.
* @group Extractors
*/
abstract class TypeNameExtractor {
def apply(s: String): TypeName
def unapply(name: TypeName): Option[String]
}
}

trait NameTableApi {
/** The abstract type of names.
* @group Names
*/
type Name >: Null <: AnyRef with NameApi

type TypeName >: Null <: TypeNameApi with Name

/** Has no special methods. Is here to provides erased identity for `TypeName`.
Expand All @@ -74,15 +135,11 @@ trait Names {
* @group Names
*/
type TermName >: Null <: TermNameApi with Name

/** Has no special methods. Is here to provides erased identity for `TermName`.
* @group API
*/
trait TermNameApi

/** The API of Name instances.
* @group API
*/
abstract class NameApi {
/** Checks whether the name is a term name */
def isTermName: Boolean
Expand Down Expand Up @@ -117,41 +174,4 @@ trait Names {
def encodedName: Name
}

/** Create a new term name.
* @group Names
*/
@deprecated("use TermName instead", "2.11.0")
def newTermName(s: String): TermName

/** Creates a new type name.
* @group Names
*/
@deprecated("use TypeName instead", "2.11.0")
def newTypeName(s: String): TypeName

/** The constructor/extractor for `TermName` instances.
* @group Extractors
*/
val TermName: TermNameExtractor

/** An extractor class to create and pattern match with syntax `TermName(s)`.
* @group Extractors
*/
abstract class TermNameExtractor {
def apply(s: String): TermName
def unapply(name: TermName): Option[String]
}

/** The constructor/extractor for `TypeName` instances.
* @group Extractors
*/
val TypeName: TypeNameExtractor

/** An extractor class to create and pattern match with syntax `TypeName(s)`.
* @group Extractors
*/
abstract class TypeNameExtractor {
def apply(s: String): TypeName
def unapply(name: TypeName): Option[String]
}
}
}
161 changes: 110 additions & 51 deletions src/reflect/scala/reflect/internal/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,114 @@ package reflect
package internal

import scala.language.implicitConversions

import scala.io.Codec
import scala.reflect.api.NameTableApi

trait Names extends api.Names {
type NameTable = scala.reflect.internal.NameTable

override val nameTable: NameTable = newNameTable
protected def synchronizeNames: Boolean = false
protected def newNameTable: NameTable = {
new NameTable(synchronizeNames)
}

type Name = nameTable.Name
type TermName = nameTable.TermName
type TypeName = nameTable.TypeName

implicit val NameTag = ClassTag[Name](classOf[Name])

implicit val TermNameTag = ClassTag[TermName](classOf[TermName])
object TermName extends TermNameExtractor {
def apply(s: String) = newTermName(s)
def unapply(name: TermName): Option[String] = Some(name.toString)
}
implicit val TypeNameTag = ClassTag[TypeName](classOf[TypeName])
object TypeName extends TypeNameExtractor {
def apply(s: String) = newTypeName(s)
def unapply(name: TypeName): Option[String] = Some(name.toString)
}

final def nameTableSize: Int = nameTable.nameTableSize
final def allNames(): Iterator[TermName] = nameTable.allNames()

final def newTermName(cs: Array[Char], offset: Int, len: Int): TermName =
newTermName(cs, offset, len, cachedString = null)

final def newTermName(cs: Array[Char]): TermName = nameTable.newTermName(cs)

final def newTypeName(cs: Array[Char]): TypeName = nameTable.newTypeName(cs)

/** Create a term name from the characters in cs[offset..offset+len-1].
* TODO - have a mode where name validation is performed at creation time
* (e.g. if a name has the string "$class" in it, then fail if that
* string is not at the very end.)
*
* @param len0 the length of the name. Negative lengths result in empty names.
*/
final def newTermName(cs: Array[Char], offset: Int, len0: Int, cachedString: String): TermName = nameTable.newTermName(cs, offset, len0, cachedString)

final def newTypeName(cs: Array[Char], offset: Int, len: Int, cachedString: String): TypeName = nameTable.newTypeName(cs, offset, len, cachedString)

/** Create a term name from string. */
@deprecatedOverriding("To synchronize, use `override def synchronizeNames = true`", "2.11.0") // overridden in https://github.com/scala-ide/scala-ide/blob/master/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPresentationCompiler.scala
final def newTermName(s: String): TermName = nameTable.newTermName(s)

/** Create a type name from string. */
@deprecatedOverriding("To synchronize, use `override def synchronizeNames = true`", "2.11.0") // overridden in https://github.com/scala-ide/scala-ide/blob/master/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPresentationCompiler.scala
final def newTypeName(s: String): TypeName = nameTable.newTypeName(s)

/** Create a term name from the UTF8 encoded bytes in bs[offset..offset+len-1]. */
final def newTermName(bs: Array[Byte], offset: Int, len: Int): TermName = nameTable.newTermName(bs, offset, len)

final def newTermNameCached(s: String): TermName = nameTable.newTermNameCached(s)

final def newTypeNameCached(s: String): TypeName = nameTable.newTypeNameCached(s)

/** Create a type name from the characters in cs[offset..offset+len-1]. */
final def newTypeName(cs: Array[Char], offset: Int, len: Int): TypeName = nameTable.newTypeName(cs, offset, len)

/** Create a type name from the UTF8 encoded bytes in bs[offset..offset+len-1]. */
final def newTypeName(bs: Array[Byte], offset: Int, len: Int): TypeName = nameTable.newTypeName(bs, offset, len)

/**
* Used by test code only.
*/
final def lookupTypeName(cs: Array[Char]): TypeName = nameTable.lookupTypeName(cs)

implicit def AnyNameOps(name: Name): NameOps[Name] = new NameOps(name)
implicit def TermNameOps(name: TermName): NameOps[TermName] = new NameOps(name)
implicit def TypeNameOps(name: TypeName): NameOps[TypeName] = new NameOps(name)

/** FIXME: This is a good example of something which is pure "value class" but cannot
* reap the benefits because an (unused) $outer pointer so it is not single-field.
*/
final class NameOps[T <: Name](name: T) {
import NameTransformer._
def stripSuffix(suffix: String): T = if (name endsWith suffix) dropRight(suffix.length) else name // OPT avoid creating a Name with `suffix`
def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name
def take(n: Int): T = name.subName(0, n).asInstanceOf[T]
def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T]
def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T]
def dropLocal: TermName = name.toTermName stripSuffix LOCAL_SUFFIX_STRING
def dropSetter: TermName = name.toTermName stripSuffix SETTER_SUFFIX_STRING
def dropModule: T = this stripSuffix MODULE_SUFFIX_STRING
def localName: TermName = getterName append LOCAL_SUFFIX_STRING
def setterName: TermName = getterName append SETTER_SUFFIX_STRING
def getterName: TermName = dropTraitSetterSeparator.dropSetter.dropLocal

private def dropTraitSetterSeparator: TermName =
name indexOf TRAIT_SETTER_SEPARATOR_STRING match {
case -1 => name.toTermName
case idx => name.toTermName drop idx drop TRAIT_SETTER_SEPARATOR_STRING.length
}
}


}

final class NameTable(synchronizeNames: Boolean) extends NameTableApi {
private final val HASH_SIZE = 0x8000
private final val HASH_MASK = 0x7FFF
private final val NAME_SIZE = 0x20000
Expand All @@ -34,7 +138,6 @@ trait Names extends api.Names {
// detect performance regressions.
//
// Discussion: https://groups.google.com/forum/#!search/biased$20scala-internals/scala-internals/0cYB7SkJ-nM/47MLhsgw8jwJ
protected def synchronizeNames: Boolean = false
private[this] val nameLock: Object = new Object

/** Memory to store all names sequentially. */
Expand All @@ -48,7 +151,9 @@ trait Names extends api.Names {
/** Hashtable for finding type names quickly. */
private[this] val typeHashtable = new Array[TypeName](HASH_SIZE)

final def allNames(): Iterator[TermName] = termHashtable.iterator.filter(_ ne null).flatMap(n => Iterator.iterate(n)(_.next).takeWhile(_ ne null))
final def allNames(): Iterator[TermName] = nameLock.synchronized {
termHashtable.iterator.filter(_ ne null).flatMap(n => Iterator.iterate(n)(_.next).takeWhile(_ ne null))
}

/**
* The hashcode of a name depends on the first, the last and the middle character,
Expand Down Expand Up @@ -141,12 +246,10 @@ trait Names extends api.Names {
newTermName(cs, offset, len, cachedString).toTypeName

/** Create a term name from string. */
@deprecatedOverriding("To synchronize, use `override def synchronizeNames = true`", "2.11.0") // overridden in https://github.com/scala-ide/scala-ide/blob/master/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPresentationCompiler.scala
def newTermName(s: String): TermName = newTermName(s.toCharArray(), 0, s.length(), null)
final def newTermName(s: String): TermName = newTermName(s.toCharArray(), 0, s.length(), null)

/** Create a type name from string. */
@deprecatedOverriding("To synchronize, use `override def synchronizeNames = true`", "2.11.0") // overridden in https://github.com/scala-ide/scala-ide/blob/master/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPresentationCompiler.scala
def newTypeName(s: String): TypeName = newTermName(s).toTypeName
final def newTypeName(s: String): TypeName = newTermName(s).toTypeName

/** Create a term name from the UTF8 encoded bytes in bs[offset..offset+len-1]. */
final def newTermName(bs: Array[Byte], offset: Int, len: Int): TermName = {
Expand Down Expand Up @@ -475,36 +578,6 @@ trait Names extends api.Names {
def debugString = { val s = decode ; if (isTypeName) s + "!" else s }
}

implicit def AnyNameOps(name: Name): NameOps[Name] = new NameOps(name)
implicit def TermNameOps(name: TermName): NameOps[TermName] = new NameOps(name)
implicit def TypeNameOps(name: TypeName): NameOps[TypeName] = new NameOps(name)

/** FIXME: This is a good example of something which is pure "value class" but cannot
* reap the benefits because an (unused) $outer pointer so it is not single-field.
*/
final class NameOps[T <: Name](name: T) {
import NameTransformer._
def stripSuffix(suffix: String): T = if (name endsWith suffix) dropRight(suffix.length) else name // OPT avoid creating a Name with `suffix`
def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name
def take(n: Int): T = name.subName(0, n).asInstanceOf[T]
def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T]
def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T]
def dropLocal: TermName = name.toTermName stripSuffix LOCAL_SUFFIX_STRING
def dropSetter: TermName = name.toTermName stripSuffix SETTER_SUFFIX_STRING
def dropModule: T = this stripSuffix MODULE_SUFFIX_STRING
def localName: TermName = getterName append LOCAL_SUFFIX_STRING
def setterName: TermName = getterName append SETTER_SUFFIX_STRING
def getterName: TermName = dropTraitSetterSeparator.dropSetter.dropLocal

private def dropTraitSetterSeparator: TermName =
name indexOf TRAIT_SETTER_SEPARATOR_STRING match {
case -1 => name.toTermName
case idx => name.toTermName drop idx drop TRAIT_SETTER_SEPARATOR_STRING.length
}
}

implicit val NameTag = ClassTag[Name](classOf[Name])

/** A name that contains no operator chars nor dollar signs.
* TODO - see if it's any faster to do something along these lines.
* Cute: now that exhaustivity kind of works, the mere presence of
Expand Down Expand Up @@ -575,13 +648,6 @@ trait Names extends api.Names {
protected def createCompanionName(next: TypeName): TypeName
}

implicit val TermNameTag = ClassTag[TermName](classOf[TermName])

object TermName extends TermNameExtractor {
def apply(s: String) = newTermName(s)
def unapply(name: TermName): Option[String] = Some(name.toString)
}

sealed abstract class TypeName(index0: Int, len0: Int, val next: TypeName) extends Name(index0, len0) with TypeNameApi {
type ThisNameType = TypeName
protected[this] def thisName: TypeName = this
Expand Down Expand Up @@ -610,11 +676,4 @@ trait Names extends api.Names {
def nameKind = "type"
override def decode = if (nameDebug) super.decode + "!" else super.decode
}

implicit val TypeNameTag = ClassTag[TypeName](classOf[TypeName])

object TypeName extends TypeNameExtractor {
def apply(s: String) = newTypeName(s)
def unapply(name: TypeName): Option[String] = Some(name.toString)
}
}
Loading