Skip to content

Commit 42577bb

Browse files
Merge pull request #3395 from dotty-staging/update-to-nio-paths
Use nio.Path instead of io.File to properly support PlainNioFile
2 parents 45387e2 + 5065cf2 commit 42577bb

19 files changed

+189
-240
lines changed

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

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
*/
44
package dotty.tools.dotc.classpath
55

6-
import java.io.File
6+
import java.io.{File => JFile}
77
import java.net.{URI, URL}
88
import java.nio.file.{FileSystems, Files, SimpleFileVisitor}
99
import java.util.function.IntFunction
1010
import java.util
1111
import java.util.Comparator
1212

13-
import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation, PlainNioFile}
13+
import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation}
1414
import FileUtils._
1515
import scala.collection.JavaConverters._
1616

@@ -86,15 +86,15 @@ trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath {
8686
}
8787

8888
trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends DirectoryLookup[FileEntryType] {
89-
type F = File
89+
type F = JFile
9090

91-
protected def emptyFiles: Array[File] = Array.empty
92-
protected def getSubDir(packageDirName: String): Option[File] = {
93-
val packageDir = new File(dir, packageDirName)
91+
protected def emptyFiles: Array[JFile] = Array.empty
92+
protected def getSubDir(packageDirName: String): Option[JFile] = {
93+
val packageDir = new JFile(dir, packageDirName)
9494
if (packageDir.exists && packageDir.isDirectory) Some(packageDir)
9595
else None
9696
}
97-
protected def listChildren(dir: File, filter: Option[File => Boolean]): Array[File] = {
97+
protected def listChildren(dir: JFile, filter: Option[JFile => Boolean]): Array[JFile] = {
9898
val listing = filter match {
9999
case Some(f) => dir.listFiles(mkFileFilter(f))
100100
case None => dir.listFiles()
@@ -112,15 +112,15 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
112112
// Note this behaviour can be enabled in javac with `javac -XDsortfiles`, but that's only
113113
// intended to improve determinism of the compiler for compiler hackers.
114114
java.util.Arrays.sort(listing,
115-
new java.util.Comparator[File] {
116-
def compare(o1: File, o2: File) = o1.getName.compareTo(o2.getName)
115+
new java.util.Comparator[JFile] {
116+
def compare(o1: JFile, o2: JFile) = o1.getName.compareTo(o2.getName)
117117
})
118118
listing
119119
} else Array()
120120
}
121-
protected def getName(f: File): String = f.getName
122-
protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new dotty.tools.io.File(f))
123-
protected def isPackage(f: File): Boolean = f.isPackage
121+
protected def getName(f: JFile): String = f.getName
122+
protected def toAbstractFile(f: JFile): AbstractFile = new PlainFile(new dotty.tools.io.File(f.toPath))
123+
protected def isPackage(f: JFile): Boolean = f.isPackage
124124

125125
assert(dir != null, "Directory file in DirectoryFileLookup cannot be null")
126126

@@ -178,7 +178,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
178178
else {
179179
packageToModuleBases.getOrElse(inPackage, Nil).flatMap(x =>
180180
Files.list(x.resolve(inPackage.replace('.', '/'))).iterator().asScala.filter(_.getFileName.toString.endsWith(".class"))).map(x =>
181-
ClassFileEntryImpl(new PlainNioFile(x))).toVector
181+
ClassFileEntryImpl(new PlainFile(new dotty.tools.io.File(x)))).toVector
182182
}
183183
}
184184

@@ -197,49 +197,49 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No
197197
val inPackage = packageOf(className)
198198
packageToModuleBases.getOrElse(inPackage, Nil).iterator.flatMap{x =>
199199
val file = x.resolve(className.replace('.', '/') + ".class")
200-
if (Files.exists(file)) new PlainNioFile(file) :: Nil else Nil
200+
if (Files.exists(file)) new PlainFile(new dotty.tools.io.File(file)) :: Nil else Nil
201201
}.take(1).toList.headOption
202202
}
203203
}
204204
private def packageOf(dottedClassName: String): String =
205205
dottedClassName.substring(0, dottedClassName.lastIndexOf("."))
206206
}
207207

208-
case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths {
208+
case class DirectoryClassPath(dir: JFile) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths {
209209
override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl
210210

211211
def findClassFile(className: String): Option[AbstractFile] = {
212212
val relativePath = FileUtils.dirPath(className)
213-
val classFile = new File(s"$dir/$relativePath.class")
213+
val classFile = new JFile(s"$dir/$relativePath.class")
214214
if (classFile.exists) {
215-
val wrappedClassFile = new dotty.tools.io.File(classFile)
215+
val wrappedClassFile = new dotty.tools.io.File(classFile.toPath)
216216
val abstractClassFile = new PlainFile(wrappedClassFile)
217217
Some(abstractClassFile)
218218
} else None
219219
}
220220

221221
protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
222-
protected def isMatchingFile(f: File): Boolean = f.isClass
222+
protected def isMatchingFile(f: JFile): Boolean = f.isClass
223223

224224
private[dotty] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
225225
}
226226

227-
case class DirectorySourcePath(dir: File) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths {
227+
case class DirectorySourcePath(dir: JFile) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths {
228228
def asSourcePathString: String = asClassPathString
229229

230230
protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file)
231-
protected def isMatchingFile(f: File): Boolean = endsScalaOrJava(f.getName)
231+
protected def isMatchingFile(f: JFile): Boolean = endsScalaOrJava(f.getName)
232232

233233
override def findClass(className: String): Option[ClassRepresentation] = findSourceFile(className) map SourceFileEntryImpl
234234

235235
private def findSourceFile(className: String): Option[AbstractFile] = {
236236
val relativePath = FileUtils.dirPath(className)
237237
val sourceFile = Stream("scala", "java")
238-
.map(ext => new File(s"$dir/$relativePath.$ext"))
238+
.map(ext => new JFile(s"$dir/$relativePath.$ext"))
239239
.collectFirst { case file if file.exists() => file }
240240

241241
sourceFile.map { file =>
242-
val wrappedSourceFile = new dotty.tools.io.File(file)
242+
val wrappedSourceFile = new dotty.tools.io.File(file.toPath)
243243
val abstractSourceFile = new PlainFile(wrappedSourceFile)
244244
abstractSourceFile
245245
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ trait ZipArchiveFileLookup[FileEntryType <: ClassRepresentation] extends ClassPa
2323
override def asURLs: Seq[URL] = Seq(zipFile.toURI.toURL)
2424
override def asClassPathStrings: Seq[String] = Seq(zipFile.getPath)
2525

26-
private val archive = new FileZipArchive(zipFile)
26+
private val archive = new FileZipArchive(zipFile.toPath)
2727

2828
override private[dotty] def packages(inPackage: String): Seq[PackageEntry] = {
2929
val prefix = PackageNameUtils.packagePrefix(inPackage)

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,21 @@ class ExtractAPI extends Phase {
4545
val dumpInc = ctx.settings.YdumpSbtInc.value
4646
val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value
4747
if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) {
48-
val sourceFile = unit.source.file.file
48+
val sourceFile = unit.source.file
4949
val apiTraverser = new ExtractAPICollector
5050
val source = apiTraverser.apiSource(unit.tpdTree)
5151

5252
if (dumpInc) {
5353
// Append to existing file that should have been created by ExtractDependencies
54-
val pw = new PrintWriter(Path(sourceFile).changeExtension("inc").toFile
54+
val pw = new PrintWriter(Path(sourceFile.jpath).changeExtension("inc").toFile
5555
.bufferedWriter(append = true), true)
5656
try {
5757
pw.println(DefaultShowAPI(source))
5858
} finally pw.close()
5959
}
6060

6161
if (ctx.sbtCallback != null)
62-
ctx.sbtCallback.api(sourceFile, source)
62+
ctx.sbtCallback.api(sourceFile.file, source)
6363
}
6464
}
6565
}

compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class ExtractDependencies extends Phase {
4747
val dumpInc = ctx.settings.YdumpSbtInc.value
4848
val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value
4949
if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) {
50-
val sourceFile = unit.source.file.file
50+
val sourceFile = unit.source.file
5151
val extractDeps = new ExtractDependenciesCollector
5252
extractDeps.traverse(unit.tpdTree)
5353

@@ -59,7 +59,7 @@ class ExtractDependencies extends Phase {
5959
Arrays.sort(deps)
6060
Arrays.sort(inhDeps)
6161

62-
val pw = Path(sourceFile).changeExtension("inc").toFile.printWriter()
62+
val pw = Path(sourceFile.jpath).changeExtension("inc").toFile.printWriter()
6363
try {
6464
pw.println(s"// usedNames: ${names.mkString(",")}")
6565
pw.println(s"// topLevelDependencies: ${deps.mkString(",")}")
@@ -69,11 +69,11 @@ class ExtractDependencies extends Phase {
6969

7070
if (ctx.sbtCallback != null) {
7171
extractDeps.usedNames.foreach(name =>
72-
ctx.sbtCallback.usedName(sourceFile, name.toString))
72+
ctx.sbtCallback.usedName(sourceFile.file, name.toString))
7373
extractDeps.topLevelDependencies.foreach(dep =>
74-
recordDependency(sourceFile, dep, DependencyContext.DependencyByMemberRef))
74+
recordDependency(sourceFile.file, dep, DependencyContext.DependencyByMemberRef))
7575
extractDeps.topLevelInheritanceDependencies.foreach(dep =>
76-
recordDependency(sourceFile, dep, DependencyContext.DependencyByInheritance))
76+
recordDependency(sourceFile.file, dep, DependencyContext.DependencyByInheritance))
7777
}
7878
}
7979
}

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

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import java.io.{
1010
ByteArrayOutputStream
1111
}
1212
import java.net.URL
13+
import java.nio.file.{FileAlreadyExistsException, Files}
1314

1415
/**
1516
* An abstraction over files for use in the reflection/compiler libraries.
@@ -41,7 +42,7 @@ object AbstractFile {
4142
*/
4243
def getDirectory(file: File): AbstractFile =
4344
if (file.isDirectory) new PlainFile(file)
44-
else if (file.isFile && Path.isExtensionJarOrZip(file.jfile)) ZipArchive fromFile file
45+
else if (file.isFile && Path.isExtensionJarOrZip(file.jpath)) ZipArchive fromFile file
4546
else null
4647

4748
/**
@@ -94,7 +95,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
9495
def path: String
9596

9697
/** Returns the path of this abstract file in a canonical form. */
97-
def canonicalPath: String = if (file == null) path else file.getCanonicalPath
98+
def canonicalPath: String = if (jpath == null) path else jpath.normalize.toString
9899

99100
/** Checks extension case insensitively. */
100101
def hasExtension(other: String) = extension == other.toLowerCase
@@ -107,18 +108,26 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
107108
def container : AbstractFile
108109

109110
/** Returns the underlying File if any and null otherwise. */
110-
def file: JFile
111+
def file: JFile = try {
112+
if (jpath == null) null
113+
else jpath.toFile
114+
} catch {
115+
case _: UnsupportedOperationException => null
116+
}
117+
118+
/** Returns the underlying Path if any and null otherwise. */
119+
def jpath: JPath
111120

112121
/** An underlying source, if known. Mostly, a zip/jar file. */
113122
def underlyingSource: Option[AbstractFile] = None
114123

115124
/** Does this abstract file denote an existing file? */
116125
def exists: Boolean = {
117-
(file eq null) || file.exists
126+
(jpath eq null) || Files.exists(jpath)
118127
}
119128

120129
/** Does this abstract file represent something which can contain classfiles? */
121-
def isClassContainer = isDirectory || (file != null && (extension == "jar" || extension == "zip"))
130+
def isClassContainer = isDirectory || (jpath != null && (extension == "jar" || extension == "zip"))
122131

123132
/** Create a file on disk, if one does not exist already. */
124133
def create(): Unit
@@ -147,7 +156,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
147156
/** size of this file if it is a concrete file. */
148157
def sizeOption: Option[Int] = None
149158

150-
def toURL: URL = if (file == null) null else file.toURI.toURL
159+
def toURL: URL = if (jpath == null) null else jpath.toUri.toURL
151160

152161
/** Returns contents of file (if applicable) in a Char array.
153162
* warning: use `Global.getSourceFile()` to use the proper
@@ -232,9 +241,11 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
232241
val lookup = lookupName(name, isDir)
233242
if (lookup != null) lookup
234243
else {
235-
val jfile = new JFile(file, name)
236-
if (isDir) jfile.mkdirs() else jfile.createNewFile()
237-
new PlainFile(jfile)
244+
Files.createDirectories(jpath)
245+
val path = jpath.resolve(name)
246+
if (isDir) Files.createDirectory(path)
247+
else Files.createFile(path)
248+
new PlainFile(new File(path))
238249
}
239250
}
240251

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88

99
package dotty.tools.io
1010

11+
import java.nio.file.Files
12+
import java.util.stream.Collectors
13+
14+
import scala.collection.JavaConverters._
15+
1116
/**
1217
* ''Note: This library is considered experimental and should not be used unless you know what you are doing.''
1318
*/
@@ -18,6 +23,7 @@ object Directory {
1823
def Current: Option[Directory] = if (userDir == "") None else normalizePath(userDir)
1924

2025
def apply(path: Path): Directory = path.toDirectory
26+
def apply(jpath: JPath): Directory = new Directory(jpath)
2127

2228
// Like File.makeTemp but creates a directory instead
2329
def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null): Directory = {
@@ -34,19 +40,17 @@ object Directory {
3440
*
3541
* ''Note: This is library is considered experimental and should not be used unless you know what you are doing.''
3642
*/
37-
class Directory(jfile: JFile) extends Path(jfile) {
43+
class Directory(jpath: JPath) extends Path(jpath) {
3844
override def toAbsolute: Directory = if (isAbsolute) this else super.toAbsolute.toDirectory
3945
override def toDirectory: Directory = this
40-
override def toFile: File = new File(jfile)
46+
override def toFile: File = new File(jpath)
4147
override def normalize: Directory = super.normalize.toDirectory
4248

4349
/** An iterator over the contents of this directory.
4450
*/
4551
def list: Iterator[Path] =
46-
jfile.listFiles match {
47-
case null => Iterator.empty
48-
case xs => xs.iterator map Path.apply
49-
}
52+
if (isDirectory) Files.list(jpath).iterator.asScala.map(Path.apply)
53+
else Iterator.empty
5054

5155
def dirs: Iterator[Directory] = list collect { case x: Directory => x }
5256
def files: Iterator[File] = list collect { case x: File => x }

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import java.io.{
1212
FileInputStream, FileOutputStream, BufferedWriter, OutputStreamWriter,
1313
BufferedOutputStream, IOException, PrintWriter
1414
}
15+
import java.nio.file.Files
16+
import java.nio.file.StandardOpenOption._
1517

1618
import scala.io.Codec
1719
/**
@@ -20,7 +22,7 @@ import scala.io.Codec
2022
object File {
2123
def pathSeparator = java.io.File.pathSeparator
2224
def separator = java.io.File.separator
23-
def apply(path: Path)(implicit codec: Codec) = new File(path.jfile)(codec)
25+
def apply(path: Path)(implicit codec: Codec) = new File(path.jpath)(codec)
2426

2527
// Create a temporary file, which will be deleted upon jvm exit.
2628
def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null) = {
@@ -41,23 +43,25 @@ object File {
4143
*
4244
* ''Note: This is library is considered experimental and should not be used unless you know what you are doing.''
4345
*/
44-
class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) with Streamable.Chars {
46+
class File(jpath: JPath)(implicit constructorCodec: Codec) extends Path(jpath) with Streamable.Chars {
4547
override val creationCodec = constructorCodec
4648

4749
override def addExtension(ext: String): File = super.addExtension(ext).toFile
4850
override def toAbsolute: File = if (isAbsolute) this else super.toAbsolute.toFile
49-
override def toDirectory: Directory = new Directory(jfile)
51+
override def toDirectory: Directory = new Directory(jpath)
5052
override def toFile: File = this
5153
override def normalize: File = super.normalize.toFile
5254
override def length = super[Path].length
5355
override def walkFilter(cond: Path => Boolean): Iterator[Path] =
5456
if (cond(this)) Iterator.single(this) else Iterator.empty
5557

5658
/** Obtains an InputStream. */
57-
def inputStream() = new FileInputStream(jfile)
59+
def inputStream() = Files.newInputStream(jpath)
5860

5961
/** Obtains a OutputStream. */
60-
def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append)
62+
def outputStream(append: Boolean = false) =
63+
if (append) Files.newOutputStream(jpath, APPEND)
64+
else Files.newOutputStream(jpath)
6165
def bufferedOutput(append: Boolean = false) = new BufferedOutputStream(outputStream(append))
6266

6367
/** Obtains an OutputStreamWriter wrapped around a FileOutputStream.
@@ -108,7 +112,7 @@ class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) w
108112
try classOf[JFile].getMethod("setExecutable", classOf[Boolean], classOf[Boolean])
109113
catch { case _: NoSuchMethodException => return false }
110114

111-
try method.invoke(jfile, executable: JBoolean, ownerOnly: JBoolean).asInstanceOf[JBoolean].booleanValue
115+
try method.invoke(jpath.toFile, executable: JBoolean, ownerOnly: JBoolean).asInstanceOf[JBoolean].booleanValue
112116
catch { case _: Exception => false }
113117
}
114118
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Jar(file: File) extends Iterable[JarEntry] {
4242

4343
private implicit def enrichManifest(m: JManifest): Jar.WManifest = Jar.WManifest(m)
4444

45-
lazy val jarFile = new JarFile(file.jfile)
45+
lazy val jarFile = new JarFile(file.jpath.toFile)
4646
lazy val manifest = withJarInput(s => Option(s.getManifest))
4747

4848
def mainClass = manifest map (f => f(Name.MAIN_CLASS))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object NoAbstractFile extends AbstractFile {
1717
def container: AbstractFile = this
1818
def create(): Unit = ???
1919
def delete(): Unit = ???
20-
def file: java.io.File = null
20+
def jpath: JPath = null
2121
def input: InputStream = null
2222
def isDirectory: Boolean = false
2323
override def isVirtual: Boolean = true

0 commit comments

Comments
 (0)