Skip to content

Commit 6149e92

Browse files
authored
Merge pull request #9587 from dotty-staging/class-rep-name
Reduce allocations when loading packages from classpath
2 parents 13c3210 + aebb209 commit 6149e92

File tree

10 files changed

+76
-38
lines changed

10 files changed

+76
-38
lines changed

compiler/src/dotty/tools/dotc/classpath/ClassPath.scala

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,27 @@ trait PackageEntry {
2323
}
2424

2525
private[dotty] case class ClassFileEntryImpl(file: AbstractFile) extends ClassFileEntry {
26-
override def name: String = FileUtils.stripClassExtension(file.name) // class name
26+
final def fileName: String = file.name
27+
def name: String = FileUtils.stripClassExtension(file.name) // class name
2728

28-
override def binary: Option[AbstractFile] = Some(file)
29-
override def source: Option[AbstractFile] = None
29+
def binary: Option[AbstractFile] = Some(file)
30+
def source: Option[AbstractFile] = None
3031
}
3132

3233
private[dotty] case class SourceFileEntryImpl(file: AbstractFile) extends SourceFileEntry {
33-
override def name: String = FileUtils.stripSourceExtension(file.name)
34+
final def fileName: String = file.name
35+
def name: String = FileUtils.stripSourceExtension(file.name)
3436

35-
override def binary: Option[AbstractFile] = None
36-
override def source: Option[AbstractFile] = Some(file)
37+
def binary: Option[AbstractFile] = None
38+
def source: Option[AbstractFile] = Some(file)
3739
}
3840

3941
private[dotty] case class ClassAndSourceFilesEntry(classFile: AbstractFile, srcFile: AbstractFile) extends ClassRepresentation {
40-
override def name: String = FileUtils.stripClassExtension(classFile.name)
42+
final def fileName: String = classFile.name
43+
def name: String = FileUtils.stripClassExtension(classFile.name)
4144

42-
override def binary: Option[AbstractFile] = Some(classFile)
43-
override def source: Option[AbstractFile] = Some(srcFile)
45+
def binary: Option[AbstractFile] = Some(classFile)
46+
def source: Option[AbstractFile] = Some(srcFile)
4447
}
4548

4649
private[dotty] case class PackageEntryImpl(name: String) extends PackageEntry

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package config
55
import core._
66
import Contexts._, Symbols._, Names._, NameOps._, Phases._
77
import StdNames.nme
8-
import Decorators.{given _}
8+
import Decorators.{_, given _}
99
import util.SrcPos
1010
import SourceVersion._
1111
import reporting.Message

compiler/src/dotty/tools/dotc/config/SourceVersion.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package config
55
import core.Contexts._
66
import core.Names.TermName
77
import core.StdNames.nme
8-
import core.Decorators.{given _}
8+
import core.Decorators.{_, given _}
99
import util.Property
1010

1111
enum SourceVersion:

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,8 @@ object Contexts {
889889
private[Contexts] val comparers = new mutable.ArrayBuffer[TypeComparer]
890890
private[Contexts] var comparersInUse: Int = 0
891891

892+
private[dotc] var nameCharBuffer = new Array[Char](256)
893+
892894
def reset(): Unit = {
893895
for ((_, set) <- uniqueSets) set.clear()
894896
errorTypeMsg.clear()

compiler/src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,34 @@ object Decorators {
2020
* with a normal wildcard. In the future, once #9255 is in trunk, replace with
2121
* a simple collective extension.
2222
*/
23-
implicit object PreNamedString:
24-
extension (pn: PreName) def toTypeName: TypeName = pn match
25-
case s: String => typeName(s)
26-
case n: Name => n.toTypeName
27-
extension (pn: PreName) def toTermName: TermName = pn match
23+
extension (pn: PreName)
24+
def toTermName: TermName = pn match
2825
case s: String => termName(s)
2926
case n: Name => n.toTermName
27+
def toTypeName: TypeName = pn match
28+
case s: String => typeName(s)
29+
case n: Name => n.toTypeName
3030

3131
extension (s: String):
32-
def splitWhere(f: Char => Boolean, doDropIndex: Boolean): Option[(String, String)] = {
32+
def splitWhere(f: Char => Boolean, doDropIndex: Boolean): Option[(String, String)] =
3333
def splitAt(idx: Int, doDropIndex: Boolean): Option[(String, String)] =
3434
if (idx == -1) None
3535
else Some((s.take(idx), s.drop(if (doDropIndex) idx + 1 else idx)))
36-
3736
splitAt(s.indexWhere(f), doDropIndex)
38-
}
37+
38+
/** Create a term name from a string slice, using a common buffer.
39+
* This avoids some allocation relative to `termName(s)`
40+
*/
41+
def sliceToTermName(start: Int, end: Int)(using Context): SimpleName =
42+
val base = ctx.base
43+
val len = end - start
44+
while len > base.nameCharBuffer.length do
45+
base.nameCharBuffer = new Array[Char](base.nameCharBuffer.length * 2)
46+
s.getChars(start, end, base.nameCharBuffer, 0)
47+
termName(base.nameCharBuffer, 0, len)
48+
49+
def sliceToTypeName(start: Int, end: Int)(using Context): TypeName =
50+
sliceToTermName(start, end).toTypeName
3951

4052
/** Implements a findSymbol method on iterators of Symbols that
4153
* works like find but avoids Option, replacing None with NoSymbol.

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,10 +681,10 @@ class Definitions {
681681
@tu lazy val NonLocalReturnControlClass: ClassSymbol = requiredClass("scala.runtime.NonLocalReturnControl")
682682
@tu lazy val SelectableClass: ClassSymbol = requiredClass("scala.Selectable")
683683

684+
@tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass
684685
@tu lazy val ClassTagClass: ClassSymbol = requiredClass("scala.reflect.ClassTag")
685686
@tu lazy val ClassTagModule: Symbol = ClassTagClass.companionModule
686687
@tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply)
687-
@tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass
688688

689689

690690
@tu lazy val QuotedExprClass: ClassSymbol = requiredClass("scala.quoted.Expr")

compiler/src/dotty/tools/dotc/core/Names.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,10 +638,13 @@ object Names {
638638
def typeName(bs: Array[Byte], offset: Int, len: Int): TypeName =
639639
termName(bs, offset, len).toTypeName
640640

641-
/** Create a term name from a string, without encoding operators */
641+
/** Create a term name from a string.
642+
* See `sliceToTermName` in `Decorators` for a more efficient version
643+
* which however requires a Context for its operation.
644+
*/
642645
def termName(s: String): SimpleName = termName(s.toCharArray, 0, s.length)
643646

644-
/** Create a type name from a string, without encoding operators */
647+
/** Create a type name from a string */
645648
def typeName(s: String): TypeName = typeName(s.toCharArray, 0, s.length)
646649

647650
table(0) = new SimpleName(-1, 0, null)

compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,29 @@ package core
44

55
import java.io.{IOException, File}
66
import java.nio.channels.ClosedByInterruptException
7+
78
import scala.compat.Platform.currentTime
9+
import scala.util.control.NonFatal
10+
811
import dotty.tools.io.{ ClassPath, ClassRepresentation, AbstractFile }
9-
import config.Config
12+
import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions
13+
1014
import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, Names._
1115
import NameOps._
12-
import StdNames.str
13-
import Decorators._
16+
import StdNames._
1417
import classfile.ClassfileParser
15-
import util.Stats
1618
import Decorators._
17-
import scala.util.control.NonFatal
19+
20+
import util.Stats
21+
import reporting.trace
22+
import config.Config
23+
1824
import ast.Trees._
25+
import ast.desugar
26+
1927
import parsing.JavaParsers.OutlineJavaParser
2028
import parsing.Parsers.OutlineParser
21-
import reporting.trace
22-
import ast.desugar
2329

24-
import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions
2530

2631
object SymbolLoaders {
2732
import ast.untpd._
@@ -178,27 +183,30 @@ object SymbolLoaders {
178183
* Note: We do a name-base comparison here because the method is called before we even
179184
* have ReflectPackage defined.
180185
*/
181-
def binaryOnly(owner: Symbol, name: String)(using Context): Boolean =
182-
name == "package" &&
183-
(owner.fullName.toString == "scala" || owner.fullName.toString == "scala.reflect")
186+
def binaryOnly(owner: Symbol, name: TermName)(using Context): Boolean =
187+
name == nme.PACKAGEkw &&
188+
(owner.name == nme.scala || owner.name == nme.reflect && owner.owner.name == nme.scala)
184189

185190
/** Initialize toplevel class and module symbols in `owner` from class path representation `classRep`
186191
*/
187192
def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation)(using Context): Unit =
188193
((classRep.binary, classRep.source): @unchecked) match {
189-
case (Some(bin), Some(src)) if needCompile(bin, src) && !binaryOnly(owner, classRep.name) =>
194+
case (Some(bin), Some(src)) if needCompile(bin, src) && !binaryOnly(owner, nameOf(classRep)) =>
190195
if (ctx.settings.verbose.value) report.inform("[symloader] picked up newer source file for " + src.path)
191-
enterToplevelsFromSource(owner, classRep.name, src)
196+
enterToplevelsFromSource(owner, nameOf(classRep), src)
192197
case (None, Some(src)) =>
193198
if (ctx.settings.verbose.value) report.inform("[symloader] no class, picked up source file for " + src.path)
194-
enterToplevelsFromSource(owner, classRep.name, src)
199+
enterToplevelsFromSource(owner, nameOf(classRep), src)
195200
case (Some(bin), _) =>
196-
enterClassAndModule(owner, classRep.name, ctx.platform.newClassLoader(bin))
201+
enterClassAndModule(owner, nameOf(classRep), ctx.platform.newClassLoader(bin))
197202
}
198203

199204
def needCompile(bin: AbstractFile, src: AbstractFile): Boolean =
200205
src.lastModified >= bin.lastModified
201206

207+
private def nameOf(classRep: ClassRepresentation)(using Context): TermName =
208+
classRep.fileName.sliceToTermName(0, classRep.nameLength)
209+
202210
/** Load contents of a package
203211
*/
204212
class PackageLoader(_sourceModule: TermSymbol, classPath: ClassPath)

compiler/src/dotty/tools/io/ClassPath.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,19 @@ object ClassPath {
170170
}
171171

172172
trait ClassRepresentation {
173+
def fileName: String
173174
def name: String
174175
def binary: Option[AbstractFile]
175176
def source: Option[AbstractFile]
177+
178+
/** returns the length of `name` by stripping the extension of `fileName`
179+
*
180+
* Used to avoid creating String instance of `name`.
181+
*/
182+
final def nameLength: Int = {
183+
val ix = fileName.lastIndexOf('.')
184+
if (ix < 0) fileName.length else ix
185+
}
176186
}
177187

178188
@deprecated("shim for sbt's compiler interface", since = "2.12.0")

compiler/test/dotty/tools/dotc/core/tasty/CommentPicklingTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import dotty.tools.dotc.ast.tpd.TreeOps
55
import dotty.tools.dotc.{Driver, Main}
66
import dotty.tools.dotc.core.Comments.CommentsContext
77
import dotty.tools.dotc.core.Contexts.Context
8-
import dotty.tools.dotc.core.Decorators.PreNamedString
8+
import dotty.tools.dotc.core.Decorators.{toTermName, toTypeName}
99
import dotty.tools.dotc.core.Mode
1010
import dotty.tools.dotc.core.Names.Name
1111
import dotty.tools.dotc.interfaces.Diagnostic.ERROR

0 commit comments

Comments
 (0)