Skip to content

Commit 171ad8e

Browse files
committed
Make Global closeable
Close Global-scoped JARs and ClassLoaders.
1 parent 837f924 commit 171ad8e

28 files changed

+288
-113
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala.tools.nsc
14+
15+
import scala.util.control.NonFatal
16+
17+
/** Registry for resources to close when `Global` is closed */
18+
final class CloseableRegistry {
19+
private[this] var closeables: List[java.io.Closeable] = Nil
20+
final def registerClosable(c: java.io.Closeable): Unit = {
21+
closeables ::= c
22+
}
23+
24+
def close(): Unit = {
25+
for (c <- closeables) {
26+
try {
27+
c.close()
28+
} catch {
29+
case NonFatal(_) =>
30+
}
31+
}
32+
closeables = Nil
33+
}
34+
}

src/compiler/scala/tools/nsc/GenericRunnerSettings.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@ import java.net.URL
1616
import scala.tools.util.PathResolver
1717

1818
class GenericRunnerSettings(error: String => Unit) extends Settings(error) {
19-
lazy val classpathURLs: Seq[URL] = new PathResolver(this).resultAsURLs
19+
lazy val classpathURLs: Seq[URL] = {
20+
val registry = new CloseableRegistry
21+
try {
22+
new PathResolver(this, new CloseableRegistry).resultAsURLs
23+
} finally {
24+
registry.close()
25+
}
26+
}
2027

2128
val howtorun =
2229
ChoiceSetting(

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@ import scala.language.postfixOps
4040
import scala.tools.nsc.ast.{TreeGen => AstTreeGen}
4141
import scala.tools.nsc.classpath._
4242
import scala.tools.nsc.profile.Profiler
43+
import scala.util.control.NonFatal
44+
import java.io.Closeable
4345

4446
class Global(var currentSettings: Settings, reporter0: LegacyReporter)
4547
extends SymbolTable
48+
with Closeable
4649
with CompilationUnits
4750
with Plugins
4851
with PhaseAssembly
@@ -814,7 +817,7 @@ class Global(var currentSettings: Settings, reporter0: LegacyReporter)
814817

815818
/** Extend classpath of `platform` and rescan updated packages. */
816819
def extendCompilerClassPath(urls: URL*): Unit = {
817-
val urlClasspaths = urls.map(u => ClassPathFactory.newClassPath(AbstractFile.getURL(u), settings))
820+
val urlClasspaths = urls.map(u => ClassPathFactory.newClassPath(AbstractFile.getURL(u), settings, closeableRegistry))
818821
val newClassPath = AggregateClassPath.createAggregate(platform.classPath +: urlClasspaths : _*)
819822
platform.currentClassPath = Some(newClassPath)
820823
invalidateClassPathEntries(urls.map(_.getPath): _*)
@@ -876,7 +879,7 @@ class Global(var currentSettings: Settings, reporter0: LegacyReporter)
876879
}
877880
entries(classPath) find matchesCanonical match {
878881
case Some(oldEntry) =>
879-
Some(oldEntry -> ClassPathFactory.newClassPath(dir, settings))
882+
Some(oldEntry -> ClassPathFactory.newClassPath(dir, settings, closeableRegistry))
880883
case None =>
881884
error(s"Error adding entry to classpath. During invalidation, no entry named $path in classpath $classPath")
882885
None
@@ -1685,6 +1688,13 @@ class Global(var currentSettings: Settings, reporter0: LegacyReporter)
16851688
}
16861689

16871690
def createJavadoc = false
1691+
1692+
final val closeableRegistry: CloseableRegistry = new CloseableRegistry
1693+
1694+
def close(): Unit = {
1695+
perRunCaches.clearAll()
1696+
closeableRegistry.close()
1697+
}
16881698
}
16891699

16901700
object Global {

src/compiler/scala/tools/nsc/backend/JavaPlatform.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ trait JavaPlatform extends Platform {
2727
private[nsc] var currentClassPath: Option[ClassPath] = None
2828

2929
private[nsc] def classPath: ClassPath = {
30-
if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings).result)
30+
if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings, global.closeableRegistry).result)
3131
currentClassPath.get
3232
}
3333

src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
6565
override def asClassPathStrings: Seq[String] = aggregates.map(_.asClassPathString).distinct
6666

6767
override def asSourcePathString: String = ClassPath.join(aggregates map (_.asSourcePathString): _*)
68-
6968
override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = {
7069
val aggregatedPackages = aggregates.flatMap(_.packages(inPackage)).distinct
7170
aggregatedPackages

src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,19 @@ package scala.tools.nsc.classpath
1414

1515
import scala.reflect.io.{AbstractFile, VirtualDirectory}
1616
import scala.reflect.io.Path.string2path
17-
import scala.tools.nsc.Settings
17+
import scala.tools.nsc.{CloseableRegistry, Settings}
1818
import FileUtils.AbstractFileOps
1919
import scala.tools.nsc.util.ClassPath
2020

2121
/**
2222
* Provides factory methods for classpath. When creating classpath instances for a given path,
2323
* it uses proper type of classpath depending on a types of particular files containing sources or classes.
2424
*/
25-
class ClassPathFactory(settings: Settings) {
25+
class ClassPathFactory(settings: Settings, closeableRegistry: CloseableRegistry) {
2626
/**
2727
* Create a new classpath based on the abstract file.
2828
*/
29-
def newClassPath(file: AbstractFile): ClassPath = ClassPathFactory.newClassPath(file, settings)
29+
def newClassPath(file: AbstractFile): ClassPath = ClassPathFactory.newClassPath(file, settings, closeableRegistry)
3030

3131
/**
3232
* Creators for sub classpaths which preserve this context.
@@ -70,19 +70,19 @@ class ClassPathFactory(settings: Settings) {
7070

7171
private def createSourcePath(file: AbstractFile): ClassPath =
7272
if (file.isJarOrZip)
73-
ZipAndJarSourcePathFactory.create(file, settings)
73+
ZipAndJarSourcePathFactory.create(file, settings, closeableRegistry)
7474
else if (file.isDirectory)
7575
DirectorySourcePath(file.file)
7676
else
7777
throw new IllegalArgumentException(s"Unsupported sourcepath element: $file")
7878
}
7979

8080
object ClassPathFactory {
81-
def newClassPath(file: AbstractFile, settings: Settings): ClassPath = file match {
81+
def newClassPath(file: AbstractFile, settings: Settings, closeableRegistry: CloseableRegistry): ClassPath = file match {
8282
case vd: VirtualDirectory => VirtualDirectoryClassPath(vd)
8383
case _ =>
8484
if (file.isJarOrZip)
85-
ZipAndJarClassPathFactory.create(file, settings)
85+
ZipAndJarClassPathFactory.create(file, settings, closeableRegistry)
8686
else if (file.isDirectory)
8787
DirectoryClassPath(file.file)
8888
else

src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
package scala.tools.nsc.classpath
1414

15-
import java.io.File
15+
import java.io.{Closeable, File}
1616
import java.net.URL
1717
import java.nio.file.{FileSystems, Files}
1818
import java.util
@@ -22,6 +22,7 @@ import scala.tools.nsc.util.{ClassPath, ClassRepresentation}
2222
import FileUtils._
2323
import scala.collection.JavaConverters._
2424
import scala.reflect.internal.JDK9Reflectors
25+
import scala.tools.nsc.CloseableRegistry
2526
import scala.tools.nsc.classpath.PackageNameUtils.{packageContains, separatePkgAndClassNames}
2627

2728
/**
@@ -58,6 +59,7 @@ trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath {
5859

5960
private[nsc] def packages(inPackage: String): Seq[PackageEntry] = {
6061
val dirForPackage = getDirectory(inPackage)
62+
6163
val nestedDirs: Array[F] = dirForPackage match {
6264
case None => emptyFiles
6365
case Some(directory) => listChildren(directory, Some(isPackage))
@@ -134,7 +136,7 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
134136

135137
object JrtClassPath {
136138
import java.nio.file._, java.net.URI
137-
def apply(release: Option[String]): Option[ClassPath] = {
139+
def apply(release: Option[String], closeableRegistry: CloseableRegistry): Option[ClassPath] = {
138140
import scala.util.Properties._
139141
if (!isJavaAtLeast("9")) None
140142
else {
@@ -151,7 +153,11 @@ object JrtClassPath {
151153
try {
152154
val ctSym = Paths.get(javaHome).resolve("lib").resolve("ct.sym")
153155
if (Files.notExists(ctSym)) None
154-
else Some(new CtSymClassPath(ctSym, v.toInt))
156+
else {
157+
val classPath = new CtSymClassPath(ctSym, v.toInt)
158+
closeableRegistry.registerClosable(classPath)
159+
Some(classPath)
160+
}
155161
} catch {
156162
case _: Throwable => None
157163
}
@@ -227,7 +233,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
227233
/**
228234
* Implementation `ClassPath` based on the $JAVA_HOME/lib/ct.sym backing http://openjdk.java.net/jeps/247
229235
*/
230-
final class CtSymClassPath(ctSym: java.nio.file.Path, release: Int) extends ClassPath with NoSourcePaths {
236+
final class CtSymClassPath(ctSym: java.nio.file.Path, release: Int) extends ClassPath with NoSourcePaths with Closeable {
231237
import java.nio.file.Path, java.nio.file._
232238

233239
private val fileSystem: FileSystem = FileSystems.newFileSystem(ctSym, null)
@@ -273,7 +279,7 @@ final class CtSymClassPath(ctSym: java.nio.file.Path, release: Int) extends Clas
273279

274280
def asURLs: Seq[URL] = Nil
275281
def asClassPathStrings: Seq[String] = Nil
276-
282+
override def close(): Unit = fileSystem.close()
277283
def findClassFile(className: String): Option[AbstractFile] = {
278284
if (!className.contains(".")) None
279285
else {

src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi
3737
// mimic the behavior of the old nsc.util.DirectoryClassPath
3838
def asURLs: Seq[URL] = Seq(new URL(dir.name))
3939
def asClassPathStrings: Seq[String] = Seq(dir.path)
40-
4140
override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl
4241

4342
def findClassFile(className: String): Option[AbstractFile] = {

0 commit comments

Comments
 (0)