Skip to content

Use nio.Path instead of io.File to properly support PlainNioFile #3395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 7, 2017
44 changes: 22 additions & 22 deletions compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
*/
package dotty.tools.dotc.classpath

import java.io.File
import java.io.{File => JFile}
import java.net.{URI, URL}
import java.nio.file.{FileSystems, Files, SimpleFileVisitor}
import java.util.function.IntFunction
import java.util
import java.util.Comparator

import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation, PlainNioFile}
import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation}
import FileUtils._
import scala.collection.JavaConverters._

Expand Down Expand Up @@ -86,15 +86,15 @@ trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath {
}

trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends DirectoryLookup[FileEntryType] {
type F = File
type F = JFile

protected def emptyFiles: Array[File] = Array.empty
protected def getSubDir(packageDirName: String): Option[File] = {
val packageDir = new File(dir, packageDirName)
protected def emptyFiles: Array[JFile] = Array.empty
protected def getSubDir(packageDirName: String): Option[JFile] = {
val packageDir = new JFile(dir, packageDirName)
if (packageDir.exists && packageDir.isDirectory) Some(packageDir)
else None
}
protected def listChildren(dir: File, filter: Option[File => Boolean]): Array[File] = {
protected def listChildren(dir: JFile, filter: Option[JFile => Boolean]): Array[JFile] = {
val listing = filter match {
case Some(f) => dir.listFiles(mkFileFilter(f))
case None => dir.listFiles()
Expand All @@ -112,15 +112,15 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
// Note this behaviour can be enabled in javac with `javac -XDsortfiles`, but that's only
// intended to improve determinism of the compiler for compiler hackers.
java.util.Arrays.sort(listing,
new java.util.Comparator[File] {
def compare(o1: File, o2: File) = o1.getName.compareTo(o2.getName)
new java.util.Comparator[JFile] {
def compare(o1: JFile, o2: JFile) = o1.getName.compareTo(o2.getName)
})
listing
} else Array()
}
protected def getName(f: File): String = f.getName
protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new dotty.tools.io.File(f))
protected def isPackage(f: File): Boolean = f.isPackage
protected def getName(f: JFile): String = f.getName
protected def toAbstractFile(f: JFile): AbstractFile = new PlainFile(new dotty.tools.io.File(f.toPath))
protected def isPackage(f: JFile): Boolean = f.isPackage

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

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

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

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

def findClassFile(className: String): Option[AbstractFile] = {
val relativePath = FileUtils.dirPath(className)
val classFile = new File(s"$dir/$relativePath.class")
val classFile = new JFile(s"$dir/$relativePath.class")
if (classFile.exists) {
val wrappedClassFile = new dotty.tools.io.File(classFile)
val wrappedClassFile = new dotty.tools.io.File(classFile.toPath)
val abstractClassFile = new PlainFile(wrappedClassFile)
Some(abstractClassFile)
} else None
}

protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
protected def isMatchingFile(f: File): Boolean = f.isClass
protected def isMatchingFile(f: JFile): Boolean = f.isClass

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

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

protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file)
protected def isMatchingFile(f: File): Boolean = endsScalaOrJava(f.getName)
protected def isMatchingFile(f: JFile): Boolean = endsScalaOrJava(f.getName)

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

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

sourceFile.map { file =>
val wrappedSourceFile = new dotty.tools.io.File(file)
val wrappedSourceFile = new dotty.tools.io.File(file.toPath)
val abstractSourceFile = new PlainFile(wrappedSourceFile)
abstractSourceFile
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ trait ZipArchiveFileLookup[FileEntryType <: ClassRepresentation] extends ClassPa
override def asURLs: Seq[URL] = Seq(zipFile.toURI.toURL)
override def asClassPathStrings: Seq[String] = Seq(zipFile.getPath)

private val archive = new FileZipArchive(zipFile)
private val archive = new FileZipArchive(zipFile.toPath)

override private[dotty] def packages(inPackage: String): Seq[PackageEntry] = {
val prefix = PackageNameUtils.packagePrefix(inPackage)
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,21 @@ class ExtractAPI extends Phase {
val dumpInc = ctx.settings.YdumpSbtInc.value
val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value
if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) {
val sourceFile = unit.source.file.file
val sourceFile = unit.source.file
val apiTraverser = new ExtractAPICollector
val source = apiTraverser.apiSource(unit.tpdTree)

if (dumpInc) {
// Append to existing file that should have been created by ExtractDependencies
val pw = new PrintWriter(Path(sourceFile).changeExtension("inc").toFile
val pw = new PrintWriter(Path(sourceFile.jpath).changeExtension("inc").toFile
.bufferedWriter(append = true), true)
try {
pw.println(DefaultShowAPI(source))
} finally pw.close()
}

if (ctx.sbtCallback != null)
ctx.sbtCallback.api(sourceFile, source)
ctx.sbtCallback.api(sourceFile.file, source)
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class ExtractDependencies extends Phase {
val dumpInc = ctx.settings.YdumpSbtInc.value
val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value
if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) {
val sourceFile = unit.source.file.file
val sourceFile = unit.source.file
val extractDeps = new ExtractDependenciesCollector
extractDeps.traverse(unit.tpdTree)

Expand All @@ -59,7 +59,7 @@ class ExtractDependencies extends Phase {
Arrays.sort(deps)
Arrays.sort(inhDeps)

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

if (ctx.sbtCallback != null) {
extractDeps.usedNames.foreach(name =>
ctx.sbtCallback.usedName(sourceFile, name.toString))
ctx.sbtCallback.usedName(sourceFile.file, name.toString))
extractDeps.topLevelDependencies.foreach(dep =>
recordDependency(sourceFile, dep, DependencyContext.DependencyByMemberRef))
recordDependency(sourceFile.file, dep, DependencyContext.DependencyByMemberRef))
extractDeps.topLevelInheritanceDependencies.foreach(dep =>
recordDependency(sourceFile, dep, DependencyContext.DependencyByInheritance))
recordDependency(sourceFile.file, dep, DependencyContext.DependencyByInheritance))
}
}
}
Expand Down
29 changes: 20 additions & 9 deletions compiler/src/dotty/tools/io/AbstractFile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import java.io.{
ByteArrayOutputStream
}
import java.net.URL
import java.nio.file.{FileAlreadyExistsException, Files}

/**
* An abstraction over files for use in the reflection/compiler libraries.
Expand Down Expand Up @@ -41,7 +42,7 @@ object AbstractFile {
*/
def getDirectory(file: File): AbstractFile =
if (file.isDirectory) new PlainFile(file)
else if (file.isFile && Path.isExtensionJarOrZip(file.jfile)) ZipArchive fromFile file
else if (file.isFile && Path.isExtensionJarOrZip(file.jpath)) ZipArchive fromFile file
else null

/**
Expand Down Expand Up @@ -94,7 +95,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
def path: String

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

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

/** Returns the underlying File if any and null otherwise. */
def file: JFile
def file: JFile = try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we should remove this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will be for next step. That might need changes outside of the Path/File/Directory abstractions. I would prefer to keep the changes only inside these abstractions for this first step.

if (jpath == null) null
else jpath.toFile
} catch {
case _: UnsupportedOperationException => null
}

/** Returns the underlying Path if any and null otherwise. */
def jpath: JPath

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

/** Does this abstract file denote an existing file? */
def exists: Boolean = {
(file eq null) || file.exists
(jpath eq null) || Files.exists(jpath)
}

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

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

def toURL: URL = if (file == null) null else file.toURI.toURL
def toURL: URL = if (jpath == null) null else jpath.toUri.toURL

/** Returns contents of file (if applicable) in a Char array.
* warning: use `Global.getSourceFile()` to use the proper
Expand Down Expand Up @@ -232,9 +241,11 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
val lookup = lookupName(name, isDir)
if (lookup != null) lookup
else {
val jfile = new JFile(file, name)
if (isDir) jfile.mkdirs() else jfile.createNewFile()
new PlainFile(jfile)
Files.createDirectories(jpath)
val path = jpath.resolve(name)
if (isDir) Files.createDirectory(path)
else Files.createFile(path)
new PlainFile(new File(path))
}
}

Expand Down
16 changes: 10 additions & 6 deletions compiler/src/dotty/tools/io/Directory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

package dotty.tools.io

import java.nio.file.Files
import java.util.stream.Collectors

import scala.collection.JavaConverters._

/**
* ''Note: This library is considered experimental and should not be used unless you know what you are doing.''
*/
Expand All @@ -18,6 +23,7 @@ object Directory {
def Current: Option[Directory] = if (userDir == "") None else normalizePath(userDir)

def apply(path: Path): Directory = path.toDirectory
def apply(jpath: JPath): Directory = new Directory(jpath)

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

/** An iterator over the contents of this directory.
*/
def list: Iterator[Path] =
jfile.listFiles match {
case null => Iterator.empty
case xs => xs.iterator map Path.apply
}
if (isDirectory) Files.list(jpath).iterator.asScala.map(Path.apply)
else Iterator.empty

def dirs: Iterator[Directory] = list collect { case x: Directory => x }
def files: Iterator[File] = list collect { case x: File => x }
Expand Down
16 changes: 10 additions & 6 deletions compiler/src/dotty/tools/io/File.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import java.io.{
FileInputStream, FileOutputStream, BufferedWriter, OutputStreamWriter,
BufferedOutputStream, IOException, PrintWriter
}
import java.nio.file.Files
import java.nio.file.StandardOpenOption._

import scala.io.Codec
/**
Expand All @@ -20,7 +22,7 @@ import scala.io.Codec
object File {
def pathSeparator = java.io.File.pathSeparator
def separator = java.io.File.separator
def apply(path: Path)(implicit codec: Codec) = new File(path.jfile)(codec)
def apply(path: Path)(implicit codec: Codec) = new File(path.jpath)(codec)

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

override def addExtension(ext: String): File = super.addExtension(ext).toFile
override def toAbsolute: File = if (isAbsolute) this else super.toAbsolute.toFile
override def toDirectory: Directory = new Directory(jfile)
override def toDirectory: Directory = new Directory(jpath)
override def toFile: File = this
override def normalize: File = super.normalize.toFile
override def length = super[Path].length
override def walkFilter(cond: Path => Boolean): Iterator[Path] =
if (cond(this)) Iterator.single(this) else Iterator.empty

/** Obtains an InputStream. */
def inputStream() = new FileInputStream(jfile)
def inputStream() = Files.newInputStream(jpath)

/** Obtains a OutputStream. */
def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append)
def outputStream(append: Boolean = false) =
if (append) Files.newOutputStream(jpath, APPEND)
else Files.newOutputStream(jpath)
def bufferedOutput(append: Boolean = false) = new BufferedOutputStream(outputStream(append))

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

try method.invoke(jfile, executable: JBoolean, ownerOnly: JBoolean).asInstanceOf[JBoolean].booleanValue
try method.invoke(jpath.toFile, executable: JBoolean, ownerOnly: JBoolean).asInstanceOf[JBoolean].booleanValue
catch { case _: Exception => false }
}
}
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/io/Jar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Jar(file: File) extends Iterable[JarEntry] {

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

lazy val jarFile = new JarFile(file.jfile)
lazy val jarFile = new JarFile(file.jpath.toFile)
lazy val manifest = withJarInput(s => Option(s.getManifest))

def mainClass = manifest map (f => f(Name.MAIN_CLASS))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/io/NoAbstractFile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object NoAbstractFile extends AbstractFile {
def container: AbstractFile = this
def create(): Unit = ???
def delete(): Unit = ???
def file: java.io.File = null
def jpath: JPath = null
def input: InputStream = null
def isDirectory: Boolean = false
override def isVirtual: Boolean = true
Expand Down
Loading