diff --git a/compiler/src/dotty/tools/dotc/classpath/ClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/ClassPath.scala index 1c95589d3fb7..9557a55e25d2 100644 --- a/compiler/src/dotty/tools/dotc/classpath/ClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/ClassPath.scala @@ -23,24 +23,27 @@ trait PackageEntry { } private[dotty] case class ClassFileEntryImpl(file: AbstractFile) extends ClassFileEntry { - override def name: String = FileUtils.stripClassExtension(file.name) // class name + final def fileName: String = file.name + def name: String = FileUtils.stripClassExtension(file.name) // class name - override def binary: Option[AbstractFile] = Some(file) - override def source: Option[AbstractFile] = None + def binary: Option[AbstractFile] = Some(file) + def source: Option[AbstractFile] = None } private[dotty] case class SourceFileEntryImpl(file: AbstractFile) extends SourceFileEntry { - override def name: String = FileUtils.stripSourceExtension(file.name) + final def fileName: String = file.name + def name: String = FileUtils.stripSourceExtension(file.name) - override def binary: Option[AbstractFile] = None - override def source: Option[AbstractFile] = Some(file) + def binary: Option[AbstractFile] = None + def source: Option[AbstractFile] = Some(file) } private[dotty] case class ClassAndSourceFilesEntry(classFile: AbstractFile, srcFile: AbstractFile) extends ClassRepresentation { - override def name: String = FileUtils.stripClassExtension(classFile.name) + final def fileName: String = classFile.name + def name: String = FileUtils.stripClassExtension(classFile.name) - override def binary: Option[AbstractFile] = Some(classFile) - override def source: Option[AbstractFile] = Some(srcFile) + def binary: Option[AbstractFile] = Some(classFile) + def source: Option[AbstractFile] = Some(srcFile) } private[dotty] case class PackageEntryImpl(name: String) extends PackageEntry diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index ed804ff4a7af..63ac69b94eba 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -5,7 +5,7 @@ package config import core._ import Contexts._, Symbols._, Names._, NameOps._, Phases._ import StdNames.nme -import Decorators.{given _} +import Decorators.{_, given _} import util.SrcPos import SourceVersion._ import reporting.Message diff --git a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala index cc9221bad6cf..e7b39ac769ac 100644 --- a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala @@ -5,7 +5,7 @@ package config import core.Contexts._ import core.Names.TermName import core.StdNames.nme -import core.Decorators.{given _} +import core.Decorators.{_, given _} import util.Property enum SourceVersion: diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index fdba7003a9d1..c805be876868 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -894,6 +894,8 @@ object Contexts { private[Contexts] val comparers = new mutable.ArrayBuffer[TypeComparer] private[Contexts] var comparersInUse: Int = 0 + private[dotc] var nameCharBuffer = new Array[Char](256) + def reset(): Unit = { for ((_, set) <- uniqueSets) set.clear() errorTypeMsg.clear() diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index afb356db2d52..f695923d517b 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -20,22 +20,34 @@ object Decorators { * with a normal wildcard. In the future, once #9255 is in trunk, replace with * a simple collective extension. */ - implicit object PreNamedString: - extension (pn: PreName) def toTypeName: TypeName = pn match - case s: String => typeName(s) - case n: Name => n.toTypeName - extension (pn: PreName) def toTermName: TermName = pn match + extension (pn: PreName) + def toTermName: TermName = pn match case s: String => termName(s) case n: Name => n.toTermName + def toTypeName: TypeName = pn match + case s: String => typeName(s) + case n: Name => n.toTypeName extension (s: String): - def splitWhere(f: Char => Boolean, doDropIndex: Boolean): Option[(String, String)] = { + def splitWhere(f: Char => Boolean, doDropIndex: Boolean): Option[(String, String)] = def splitAt(idx: Int, doDropIndex: Boolean): Option[(String, String)] = if (idx == -1) None else Some((s.take(idx), s.drop(if (doDropIndex) idx + 1 else idx))) - splitAt(s.indexWhere(f), doDropIndex) - } + + /** Create a term name from a string slice, using a common buffer. + * This avoids some allocation relative to `termName(s)` + */ + def sliceToTermName(start: Int, end: Int)(using Context): SimpleName = + val base = ctx.base + val len = end - start + while len > base.nameCharBuffer.length do + base.nameCharBuffer = new Array[Char](base.nameCharBuffer.length * 2) + s.getChars(start, end, base.nameCharBuffer, 0) + termName(base.nameCharBuffer, 0, len) + + def sliceToTypeName(start: Int, end: Int)(using Context): TypeName = + sliceToTermName(start, end).toTypeName /** Implements a findSymbol method on iterators of Symbols that * works like find but avoids Option, replacing None with NoSymbol. diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 418d7f3027c9..dde6baa47325 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -682,10 +682,10 @@ class Definitions { @tu lazy val NonLocalReturnControlClass: ClassSymbol = requiredClass("scala.runtime.NonLocalReturnControl") @tu lazy val SelectableClass: ClassSymbol = requiredClass("scala.Selectable") + @tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass @tu lazy val ClassTagClass: ClassSymbol = requiredClass("scala.reflect.ClassTag") @tu lazy val ClassTagModule: Symbol = ClassTagClass.companionModule @tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply) - @tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass @tu lazy val QuotedExprClass: ClassSymbol = requiredClass("scala.quoted.Expr") diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index cd58977f9dda..89adab098abb 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -638,10 +638,13 @@ object Names { def typeName(bs: Array[Byte], offset: Int, len: Int): TypeName = termName(bs, offset, len).toTypeName - /** Create a term name from a string, without encoding operators */ + /** Create a term name from a string. + * See `sliceToTermName` in `Decorators` for a more efficient version + * which however requires a Context for its operation. + */ def termName(s: String): SimpleName = termName(s.toCharArray, 0, s.length) - /** Create a type name from a string, without encoding operators */ + /** Create a type name from a string */ def typeName(s: String): TypeName = typeName(s.toCharArray, 0, s.length) table(0) = new SimpleName(-1, 0, null) diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 0d23f0df1d51..52431854f64b 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -4,24 +4,29 @@ package core import java.io.{IOException, File} import java.nio.channels.ClosedByInterruptException + import scala.compat.Platform.currentTime +import scala.util.control.NonFatal + import dotty.tools.io.{ ClassPath, ClassRepresentation, AbstractFile } -import config.Config +import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions + import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, Names._ import NameOps._ -import StdNames.str -import Decorators._ +import StdNames._ import classfile.ClassfileParser -import util.Stats import Decorators._ -import scala.util.control.NonFatal + +import util.Stats +import reporting.trace +import config.Config + import ast.Trees._ +import ast.desugar + import parsing.JavaParsers.OutlineJavaParser import parsing.Parsers.OutlineParser -import reporting.trace -import ast.desugar -import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions object SymbolLoaders { import ast.untpd._ @@ -178,27 +183,30 @@ object SymbolLoaders { * Note: We do a name-base comparison here because the method is called before we even * have ReflectPackage defined. */ - def binaryOnly(owner: Symbol, name: String)(using Context): Boolean = - name == "package" && - (owner.fullName.toString == "scala" || owner.fullName.toString == "scala.reflect") + def binaryOnly(owner: Symbol, name: TermName)(using Context): Boolean = + name == nme.PACKAGEkw && + (owner.name == nme.scala || owner.name == nme.reflect && owner.owner.name == nme.scala) /** Initialize toplevel class and module symbols in `owner` from class path representation `classRep` */ def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation)(using Context): Unit = ((classRep.binary, classRep.source): @unchecked) match { - case (Some(bin), Some(src)) if needCompile(bin, src) && !binaryOnly(owner, classRep.name) => + case (Some(bin), Some(src)) if needCompile(bin, src) && !binaryOnly(owner, nameOf(classRep)) => if (ctx.settings.verbose.value) report.inform("[symloader] picked up newer source file for " + src.path) - enterToplevelsFromSource(owner, classRep.name, src) + enterToplevelsFromSource(owner, nameOf(classRep), src) case (None, Some(src)) => if (ctx.settings.verbose.value) report.inform("[symloader] no class, picked up source file for " + src.path) - enterToplevelsFromSource(owner, classRep.name, src) + enterToplevelsFromSource(owner, nameOf(classRep), src) case (Some(bin), _) => - enterClassAndModule(owner, classRep.name, ctx.platform.newClassLoader(bin)) + enterClassAndModule(owner, nameOf(classRep), ctx.platform.newClassLoader(bin)) } def needCompile(bin: AbstractFile, src: AbstractFile): Boolean = src.lastModified >= bin.lastModified + private def nameOf(classRep: ClassRepresentation)(using Context): TermName = + classRep.fileName.sliceToTermName(0, classRep.nameLength) + /** Load contents of a package */ class PackageLoader(_sourceModule: TermSymbol, classPath: ClassPath) diff --git a/compiler/src/dotty/tools/io/ClassPath.scala b/compiler/src/dotty/tools/io/ClassPath.scala index f8fa6c2c35be..97d678901a6d 100644 --- a/compiler/src/dotty/tools/io/ClassPath.scala +++ b/compiler/src/dotty/tools/io/ClassPath.scala @@ -170,9 +170,19 @@ object ClassPath { } trait ClassRepresentation { + def fileName: String def name: String def binary: Option[AbstractFile] def source: Option[AbstractFile] + + /** returns the length of `name` by stripping the extension of `fileName` + * + * Used to avoid creating String instance of `name`. + */ + final def nameLength: Int = { + val ix = fileName.lastIndexOf('.') + if (ix < 0) fileName.length else ix + } } @deprecated("shim for sbt's compiler interface", since = "2.12.0") diff --git a/compiler/test/dotty/tools/dotc/core/tasty/CommentPicklingTest.scala b/compiler/test/dotty/tools/dotc/core/tasty/CommentPicklingTest.scala index 62d95843e0f3..28779fe9b697 100644 --- a/compiler/test/dotty/tools/dotc/core/tasty/CommentPicklingTest.scala +++ b/compiler/test/dotty/tools/dotc/core/tasty/CommentPicklingTest.scala @@ -5,7 +5,7 @@ import dotty.tools.dotc.ast.tpd.TreeOps import dotty.tools.dotc.{Driver, Main} import dotty.tools.dotc.core.Comments.CommentsContext import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Decorators.PreNamedString +import dotty.tools.dotc.core.Decorators.{toTermName, toTypeName} import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.interfaces.Diagnostic.ERROR