Skip to content

Commit 123d3a2

Browse files
committed
Reduce allocations when loading packages from classpath
This is a port of the following PR in scalac: scala/scala#8927
1 parent 64a239f commit 123d3a2

File tree

5 files changed

+60
-24
lines changed

5 files changed

+60
-24
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/core/Contexts.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ object Contexts {
8989
inline def withoutMode[T](mode: Mode)(inline op: Context ?=> T)(using ctx: Context): T =
9090
inMode(ctx.mode &~ mode)(op)
9191

92+
private[dotc] inline def withNameBuffer(inline op: Array[Char] => Int)(using ctx: Context): Names.TermName = {
93+
val base = ctx.base
94+
var len = op(base.nameCharBuffer)
95+
while(len == -1) {
96+
base.nameCharBuffer = new Array[Char](base.nameCharBuffer.length * 2)
97+
len = op(base.nameCharBuffer)
98+
}
99+
Names.termName(base.nameCharBuffer, 0, len)
100+
}
101+
92102
/** A context is passed basically everywhere in dotc.
93103
* This is convenient but carries the risk of captured contexts in
94104
* objects that turn into space leaks. To combat this risk, here are some
@@ -894,6 +904,8 @@ object Contexts {
894904
private[Contexts] val comparers = new mutable.ArrayBuffer[TypeComparer]
895905
private[Contexts] var comparersInUse: Int = 0
896906

907+
private[dotc] var nameCharBuffer = new Array[Char](256)
908+
897909
def reset(): Unit = {
898910
for ((_, set) <- uniqueSets) set.clear()
899911
errorTypeMsg.clear()

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

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

685+
@tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass
685686
@tu lazy val ClassTagClass: ClassSymbol = requiredClass("scala.reflect.ClassTag")
686687
@tu lazy val ClassTagModule: Symbol = ClassTagClass.companionModule
687688
@tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply)
688-
@tu lazy val ReflectPackageClass: Symbol = requiredPackage("scala.reflect.package").moduleClass
689689

690690

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

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

Lines changed: 22 additions & 14 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" &&
186+
def binaryOnly(owner: Symbol, name: TermName)(using Context): Boolean =
187+
name == nme.PACKAGEkw &&
183188
(owner.fullName.toString == "scala" || owner.fullName.toString == "scala.reflect")
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+
withNameBuffer(classRep.nameChars)
209+
202210
/** Load contents of a package
203211
*/
204212
class PackageLoader(_sourceModule: TermSymbol, classPath: ClassPath)

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,22 @@ 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+
/** Low level way to extract the entry name without allocation. */
179+
final def nameChars(buffer: Array[Char]): Int = {
180+
val ix = fileName.lastIndexOf('.')
181+
val nameLength = if (ix < 0) fileName.length else ix
182+
if (nameLength > buffer.length)
183+
-1
184+
else {
185+
fileName.getChars(0, nameLength, buffer, 0)
186+
nameLength
187+
}
188+
}
176189
}
177190

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

0 commit comments

Comments
 (0)