Skip to content

Commit 2f6fa4a

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 2f6fa4a

File tree

6 files changed

+62
-23
lines changed

6 files changed

+62
-23
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/ContextOps.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,15 @@ object ContextOps:
7272
if (pkg.is(Package)) fresh.setOwner(pkg.moduleClass).setTree(tree)
7373
else ctx
7474
}
75+
76+
private[dotc] def withNameBuffer(op: Array[Char] => Int)(using Context): Names.TermName = {
77+
val base = ctx.base
78+
while(true) {
79+
val len = op(base.nameCharBuffer)
80+
if (len == -1) base.nameCharBuffer = new Array[Char](base.nameCharBuffer.length * 2)
81+
else return Names.termName(base.nameCharBuffer, 0, len)
82+
}
83+
throw new IllegalStateException()
84+
}
85+
7586
end ContextOps

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

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

897+
private[dotc] var nameCharBuffer = new Array[Char](256)
898+
897899
def reset(): Unit = {
898900
for ((_, set) <- uniqueSets) set.clear()
899901
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: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,31 @@ 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 }
12+
13+
import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions
14+
915
import config.Config
1016
import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, Names._
11-
import NameOps._
12-
import StdNames.str
17+
import NameOps._, ContextOps._
18+
import StdNames._
1319
import Decorators._
1420
import classfile.ClassfileParser
15-
import util.Stats
1621
import Decorators._
17-
import scala.util.control.NonFatal
22+
23+
import util.Stats
24+
import reporting.trace
25+
1826
import ast.Trees._
27+
import ast.desugar
28+
1929
import parsing.JavaParsers.OutlineJavaParser
2030
import parsing.Parsers.OutlineParser
21-
import reporting.trace
22-
import ast.desugar
2331

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

2633
object SymbolLoaders {
2734
import ast.untpd._
@@ -178,27 +185,30 @@ object SymbolLoaders {
178185
* Note: We do a name-base comparison here because the method is called before we even
179186
* have ReflectPackage defined.
180187
*/
181-
def binaryOnly(owner: Symbol, name: String)(using Context): Boolean =
182-
name == "package" &&
188+
def binaryOnly(owner: Symbol, name: TermName)(using Context): Boolean =
189+
name == nme.PACKAGEkw &&
183190
(owner.fullName.toString == "scala" || owner.fullName.toString == "scala.reflect")
184191

185192
/** Initialize toplevel class and module symbols in `owner` from class path representation `classRep`
186193
*/
187194
def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation)(using Context): Unit =
188195
((classRep.binary, classRep.source): @unchecked) match {
189-
case (Some(bin), Some(src)) if needCompile(bin, src) && !binaryOnly(owner, classRep.name) =>
196+
case (Some(bin), Some(src)) if needCompile(bin, src) && !binaryOnly(owner, nameOf(classRep)) =>
190197
if (ctx.settings.verbose.value) report.inform("[symloader] picked up newer source file for " + src.path)
191-
enterToplevelsFromSource(owner, classRep.name, src)
198+
enterToplevelsFromSource(owner, nameOf(classRep), src)
192199
case (None, Some(src)) =>
193200
if (ctx.settings.verbose.value) report.inform("[symloader] no class, picked up source file for " + src.path)
194-
enterToplevelsFromSource(owner, classRep.name, src)
201+
enterToplevelsFromSource(owner, nameOf(classRep), src)
195202
case (Some(bin), _) =>
196-
enterClassAndModule(owner, classRep.name, ctx.platform.newClassLoader(bin))
203+
enterClassAndModule(owner, nameOf(classRep), ctx.platform.newClassLoader(bin))
197204
}
198205

199206
def needCompile(bin: AbstractFile, src: AbstractFile): Boolean =
200207
src.lastModified >= bin.lastModified
201208

209+
private def nameOf(classRep: ClassRepresentation)(using Context): TermName =
210+
ctx.withNameBuffer(classRep.nameChars)
211+
202212
/** Load contents of a package
203213
*/
204214
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)