From 48e96634892328aa2c2cf4a85fd67edaa3e5aeed Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 12 May 2011 07:48:28 +0000 Subject: Reverting the rest of it until I can look with ... Reverting the rest of it until I can look with clear eyes, no review. --- src/compiler/scala/tools/nsc/io/AbstractFile.scala | 54 +++++++-------- src/compiler/scala/tools/nsc/io/Directory.scala | 2 +- src/compiler/scala/tools/nsc/io/File.scala | 36 +++++----- src/compiler/scala/tools/nsc/io/Path.scala | 19 +++--- src/compiler/scala/tools/nsc/io/PlainFile.scala | 30 ++++++--- src/compiler/scala/tools/nsc/io/Streamable.scala | 78 ++++++++-------------- src/compiler/scala/tools/nsc/io/package.scala | 9 +++ src/compiler/scala/tools/nsc/util/ClassPath.scala | 35 +++++----- .../scala/tools/partest/nest/FileManager.scala | 2 +- src/partest/scala/tools/partest/nest/Worker.scala | 4 +- 10 files changed, 134 insertions(+), 135 deletions(-) diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala index 4b196877a3..059ca9bd9e 100644 --- a/src/compiler/scala/tools/nsc/io/AbstractFile.scala +++ b/src/compiler/scala/tools/nsc/io/AbstractFile.scala @@ -10,7 +10,7 @@ package io import java.io.{ FileOutputStream, IOException, InputStream, OutputStream, BufferedOutputStream } import java.net.URL import PartialFunction._ -import java.io.File.{ separatorChar => separator } + import scala.collection.mutable.ArrayBuffer /** @@ -84,6 +84,7 @@ object AbstractFile { *

*/ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] { + /** Returns the name of this abstract file. */ def name: String @@ -136,8 +137,6 @@ abstract class AbstractFile extends AnyRef with 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 - /** Returns contents of file (if applicable) in a Char array. * warning: use Global.getSourceFile() to use the proper * encoding when converting to the char array. @@ -187,47 +186,43 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] { * @return ... */ def lookupPath(path: String, directory: Boolean): AbstractFile = { - lookupPathInternal(path, directory, false) + lookup((f, p, dir) => f.lookupName(p, dir), path, directory) } /** Return an abstract file that does not check that `path' denotes * an existing file. */ def lookupPathUnchecked(path: String, directory: Boolean): AbstractFile = { - lookupPathInternal(path, directory, true) + lookup((f, p, dir) => f.lookupNameUnchecked(p, dir), path, directory) } - private def lookupPathInternal(path0: String, directory: Boolean, unchecked: Boolean): AbstractFile = { + private def lookup(getFile: (AbstractFile, String, Boolean) => AbstractFile, + path0: String, + directory: Boolean): AbstractFile = { + val separator = JFile.separatorChar // trim trailing '/'s - val path = ( - if (path0.charAt(path0.length - 1) != separator) path0 - else path0.substring(0, path0.length - 1) - ) - def loop(file: AbstractFile, start: Int): AbstractFile = { + val path: String = if (path0.last == separator) path0 dropRight 1 else path0 + val length = path.length() + assert(length > 0 && !(path.last == separator), path) + var file = this + var start = 0 + while (true) { val index = path.indexOf(separator, start) - if (index < 0) { - if (unchecked) file.lookupNameUnchecked(path, directory) - else file.lookupName(path, directory) - } - else { - val name = path.substring(start, index) - val next = ( - if (unchecked) file.lookupNameUnchecked(name, true) - else file.lookupName(name, true) - ) - if (next == null) null - else loop(next, index + 1) - } + assert(index < 0 || start < index) + val name = path.substring(start, if (index < 0) length else index) + file = getFile(file, name, if (index < 0) directory else true) + if ((file eq null) || index < 0) return file + start = index + 1 } - loop(this, 0) + file } - private def fileOrSubdirectoryNamed(name: String, directory: Boolean): AbstractFile = { - val lookup = lookupName(name, directory) + private def fileOrSubdirectoryNamed(name: String, isDir: Boolean): AbstractFile = { + val lookup = lookupName(name, isDir) if (lookup != null) lookup else { val jfile = new JFile(file, name) - if (directory) jfile.mkdirs() else jfile.createNewFile() + if (isDir) jfile.mkdirs() else jfile.createNewFile() new PlainFile(jfile) } } @@ -254,5 +249,6 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] { protected def unsupported(msg: String): Nothing = throw new UnsupportedOperationException(msg) /** Returns the path of this abstract file. */ - override def toString = path + override def toString() = path + } diff --git a/src/compiler/scala/tools/nsc/io/Directory.scala b/src/compiler/scala/tools/nsc/io/Directory.scala index 64b7711686..b4ceba682a 100644 --- a/src/compiler/scala/tools/nsc/io/Directory.scala +++ b/src/compiler/scala/tools/nsc/io/Directory.scala @@ -12,7 +12,7 @@ package io object Directory { import scala.util.Properties.{ tmpDir, userHome, userDir } - private def normalizePath(s: String) = Some(Directory(s).toAbsolute) + private def normalizePath(s: String) = Some(apply(Path(s).normalize)) def Current: Option[Directory] = if (userDir == "") None else normalizePath(userDir) def Home: Option[Directory] = if (userHome == "") None else normalizePath(userHome) def TmpDir: Option[Directory] = if (tmpDir == "") None else normalizePath(tmpDir) diff --git a/src/compiler/scala/tools/nsc/io/File.scala b/src/compiler/scala/tools/nsc/io/File.scala index e5969f318e..b11151ab7e 100644 --- a/src/compiler/scala/tools/nsc/io/File.scala +++ b/src/compiler/scala/tools/nsc/io/File.scala @@ -17,14 +17,14 @@ import java.nio.channels.{ Channel, FileChannel } import scala.io.Codec object File { - def pathSeparator = java.io.File.pathSeparator - def separator = java.io.File.separator + def pathSeparator = JFile.pathSeparator + def separator = JFile.separator def apply(path: Path)(implicit codec: Codec) = new File(path.jfile)(codec) // Create a temporary file, which will be deleted upon jvm exit. def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null) = { - val jfile = java.io.File.createTempFile(prefix, suffix, dir) + val jfile = JFile.createTempFile(prefix, suffix, dir) jfile.deleteOnExit() apply(jfile) } @@ -43,20 +43,20 @@ object File { // trigger java.lang.InternalErrors later when using it concurrently. We ignore all // the exceptions so as not to cause spurious failures when no write access is available, // e.g. google app engine. - // try { - // import Streamable.closing - // val tmp = java.io.File.createTempFile("bug6503430", null, null) - // try closing(new FileInputStream(tmp)) { in => - // val inc = in.getChannel() - // closing(new FileOutputStream(tmp, true)) { out => - // out.getChannel().transferFrom(inc, 0, 0) - // } - // } - // finally tmp.delete() - // } - // catch { - // case _: IllegalArgumentException | _: IllegalStateException | _: IOException | _: SecurityException => () - // } + try { + import Streamable.closing + val tmp = JFile.createTempFile("bug6503430", null, null) + try closing(new FileInputStream(tmp)) { in => + val inc = in.getChannel() + closing(new FileOutputStream(tmp, true)) { out => + out.getChannel().transferFrom(inc, 0, 0) + } + } + finally tmp.delete() + } + catch { + case _: IllegalArgumentException | _: IllegalStateException | _: IOException | _: SecurityException => () + } } import File._ import Path._ @@ -145,7 +145,7 @@ class File(jfile: JFile)(implicit constructorCodec: Codec) extends Path(jfile) w val CHUNK = 1024 * 1024 * 16 // 16 MB val dest = destPath.toFile if (!isValid) fail("Source %s is not a valid file." format name) - if (this isSame dest) fail("Source and destination are the same.") + if (this.normalize == dest.normalize) fail("Source and destination are the same.") if (!dest.parent.exists) fail("Destination cannot be created.") if (dest.exists && !dest.canWrite) fail("Destination exists but is not writable.") if (dest.isDirectory) fail("Destination exists but is a directory.") diff --git a/src/compiler/scala/tools/nsc/io/Path.scala b/src/compiler/scala/tools/nsc/io/Path.scala index f95790785c..86839437f4 100644 --- a/src/compiler/scala/tools/nsc/io/Path.scala +++ b/src/compiler/scala/tools/nsc/io/Path.scala @@ -11,7 +11,6 @@ import java.io.{ BufferedInputStream, BufferedOutputStream, RandomAccessFile } import java.net.{ URI, URL } import scala.util.Random.alphanumeric -import java.io.File.{ separatorChar, separator => separatorStr } /** An abstraction for filesystem paths. The differences between * Path, File, and Directory are primarily to communicate intent. @@ -66,9 +65,9 @@ object Path { def onlyFiles(xs: Iterator[Path]): Iterator[File] = xs filter (_.isFile) map (_.toFile) def onlyFiles(xs: List[Path]): List[File] = xs filter (_.isFile) map (_.toFile) - def roots: List[Path] = java.io.File.listRoots().toList map Path.apply + def roots: List[Path] = JFile.listRoots().toList map Path.apply - def apply(segments: Seq[String]): Path = apply(segments mkString separatorStr) + def apply(segments: Seq[String]): Path = apply(segments mkString JFile.separator) def apply(path: String): Path = apply(new JFile(path)) def apply(jfile: JFile): Path = if (jfile.isFile) new File(jfile) @@ -85,6 +84,9 @@ import Path._ * semantics regarding how a Path might relate to the world. */ class Path private[io] (val jfile: JFile) { + val separator = JFile.separatorChar + val separatorStr = JFile.separator + // Validation: this verifies that the type of this object and the // contents of the filesystem are in agreement. All objects are // valid except File objects whose path points to a directory and @@ -95,7 +97,6 @@ class Path private[io] (val jfile: JFile) { def toFile: File = new File(jfile) def toDirectory: Directory = new Directory(jfile) def toAbsolute: Path = if (isAbsolute) this else Path(jfile.getAbsolutePath()) - def toCanonical: Path = Path(jfile.getCanonicalPath()) def toURI: URI = jfile.toURI() def toURL: URL = toURI.toURL() /** If this path is absolute, returns it: otherwise, returns an absolute @@ -129,7 +130,7 @@ class Path private[io] (val jfile: JFile) { // identity def name: String = jfile.getName() def path: String = jfile.getPath() - def normalize: Path = toCanonical + def normalize: Path = Path(jfile.getCanonicalPath()) def isRootPath: Boolean = roots exists (_ isSame this) def resolve(other: Path) = if (other.isAbsolute || isEmpty) other else /(other) @@ -139,7 +140,7 @@ class Path private[io] (val jfile: JFile) { def createRelativePath(baseSegs: List[String], otherSegs: List[String]) : String = { (baseSegs, otherSegs) match { case (b :: bs, o :: os) if b == o => createRelativePath(bs, os) - case (bs, os) => ((".."+separatorChar)*bs.length)+os.mkString(separatorStr) + case (bs, os) => ((".."+separator)*bs.length)+os.mkString(separatorStr) } } @@ -148,7 +149,7 @@ class Path private[io] (val jfile: JFile) { // derived from identity def root: Option[Path] = roots find (this startsWith _) - def segments: List[String] = (path split separatorChar).toList filterNot (_.length == 0) + def segments: List[String] = (path split separator).toList filterNot (_.length == 0) /** * @return The path of the parent directory, or root if path is already root */ @@ -211,7 +212,7 @@ class Path private[io] (val jfile: JFile) { def isHidden = jfile.isHidden() def isSymlink = { val x = parent / name - x.toCanonical != x.toAbsolute + x.normalize != x.toAbsolute } def isEmpty = path.length == 0 @@ -223,7 +224,7 @@ class Path private[io] (val jfile: JFile) { // Boolean path comparisons def endsWith(other: Path) = segments endsWith other.segments def startsWith(other: Path) = segments startsWith other.segments - def isSame(other: Path) = toCanonical == other.toCanonical + def isSame(other: Path) = normalize == other.normalize def isFresher(other: Path) = lastModified > other.lastModified // creations diff --git a/src/compiler/scala/tools/nsc/io/PlainFile.scala b/src/compiler/scala/tools/nsc/io/PlainFile.scala index fb19ec120b..ce4bca490f 100644 --- a/src/compiler/scala/tools/nsc/io/PlainFile.scala +++ b/src/compiler/scala/tools/nsc/io/PlainFile.scala @@ -3,9 +3,13 @@ * @author Martin Odersky */ + package scala.tools.nsc package io +import java.io.{ FileInputStream, FileOutputStream, IOException } +import PartialFunction._ + object PlainFile { /** * If the specified File exists, returns an abstract file backed @@ -20,22 +24,28 @@ object PlainFile { class PlainFile(val givenPath: Path) extends AbstractFile { assert(path ne null) - def name = givenPath.name - def path = givenPath.path - def file = givenPath.jfile - def absolute = new PlainFile(givenPath.toAbsolute) - + val file = givenPath.jfile override def underlyingSource = Some(this) + + private val fpath = try givenPath.normalize catch { case _: IOException => givenPath.toAbsolute } + + /** Returns the name of this abstract file. */ + def name = givenPath.name + + /** Returns the path of this abstract file. */ + def path = givenPath.path + + /** The absolute file. */ + def absolute = new PlainFile(givenPath.normalize) + override def container: AbstractFile = new PlainFile(givenPath.parent) override def input = givenPath.toFile.inputStream() override def output = givenPath.toFile.outputStream() override def sizeOption = Some(givenPath.length.toInt) - override def hashCode(): Int = path.hashCode - override def equals(that: Any): Boolean = that match { - case other: PlainFile => path == other.path - case _ => false - } + override def hashCode(): Int = fpath.hashCode + override def equals(that: Any): Boolean = + cond(that) { case other: PlainFile => fpath == other.fpath } /** Is this abstract file a directory? */ def isDirectory: Boolean = givenPath.isDirectory diff --git a/src/compiler/scala/tools/nsc/io/Streamable.scala b/src/compiler/scala/tools/nsc/io/Streamable.scala index c0a56eeda6..03318674ee 100644 --- a/src/compiler/scala/tools/nsc/io/Streamable.scala +++ b/src/compiler/scala/tools/nsc/io/Streamable.scala @@ -20,60 +20,14 @@ import Path.fail */ object Streamable { - /** For reading a stream of unknown length. Doesn't buffer the - * input stream: call the two argument version. - */ - def toByteArray(in: InputStream): Array[Byte] = { - val bytes = new ArrayBuffer[Byte]() - val buf = new Array[Byte](4096) - - def loop(): Array[Byte] = { - val bytesRead = in.read(buf) - if (bytesRead < 0) sys.error("read error") - else if (bytesRead == 0) bytes.toArray - else { - bytes ++= buf.slice(0, bytesRead) - loop() - } - } - try loop() - finally in.close() - } - /** This method aspires to be the fastest way to read - * a stream of known length into memory. - */ - def toByteArray(input: InputStream, len: Int): Array[Byte] = { - val in = new BufferedInputStream(input) - if (len < 0) - return toByteArray(in) - - val arr = new Array[Byte](len) - var offset = 0 - - def loop() { - if (offset < len) { - val read = in.read(arr, offset, len - offset) - if (read >= 0) { - offset += read - loop() - } - } - } - try loop() - finally in.close() - - if (offset == arr.length) arr - else sys.error("Could not read entire source (%d of %d bytes)".format(offset, len)) - } - /** Traits which can be viewed as a sequence of bytes. Source types * which know their length should override def length: Long for more * efficient method implementations. */ trait Bytes { def inputStream(): InputStream - def length: Long = -1 + def bufferedInput() = new BufferedInputStream(inputStream()) def bytes(): Iterator[Byte] = bytesAsInts() map (_.toByte) def bytesAsInts(): Iterator[Int] = { @@ -81,9 +35,33 @@ object Streamable { Iterator continually in.read() takeWhile (_ != -1) } - def toByteArray() = { - require(length <= Int.MaxValue, length + " larger than Int.MaxValue") - Streamable.toByteArray(inputStream(), length.toInt) + /** This method aspires to be the fastest way to read + * a stream of known length into memory. + */ + def toByteArray(): Array[Byte] = { + // if we don't know the length, fall back on relative inefficiency + if (length == -1L) + return (new ArrayBuffer[Byte]() ++= bytes()).toArray + + val arr = new Array[Byte](length.toInt) + val len = arr.length + lazy val in = bufferedInput() + var offset = 0 + + def loop() { + if (offset < len) { + val read = in.read(arr, offset, len - offset) + if (read >= 0) { + offset += read + loop() + } + } + } + try loop() + finally in.close() + + if (offset == arr.length) arr + else fail("Could not read entire source (%d of %d bytes)".format(offset, len)) } } diff --git a/src/compiler/scala/tools/nsc/io/package.scala b/src/compiler/scala/tools/nsc/io/package.scala index 91cf715e33..29174b161f 100644 --- a/src/compiler/scala/tools/nsc/io/package.scala +++ b/src/compiler/scala/tools/nsc/io/package.scala @@ -12,6 +12,15 @@ import java.util.jar.{ Attributes } package object io { type JManifest = java.util.jar.Manifest private[io] type JFile = java.io.File + // grimly bulldozing through #4338 + private[io] object JFile { + import java.io.{ File => JJFile } // the irony of JFile being ambiguous is not overlooked + val createTempFile = JJFile.createTempFile(_: String, _: String, _: JFile) + def pathSeparator = JJFile.pathSeparator + def separator = JJFile.separator + def separatorChar = JJFile.separatorChar + def listRoots() = JJFile.listRoots() + } private[io] implicit def installManifestOps(m: JManifest) = new ManifestOps(m) class ManifestOps(manifest: JManifest) { def attrs = manifest.getMainAttributes() diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index be31844e60..25c09b6c15 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -165,7 +165,7 @@ object ClassPath { class JavaContext extends ClassPathContext[AbstractFile] { def toBinaryName(rep: AbstractFile) = { val name = rep.name - assert(endsClass(name), name) + assert(name.length > 6 && name.substring(name.length - 6) == ".class", name) name.substring(0, name.length - 6) } def newClassPath(dir: AbstractFile) = new DirectoryClassPath(dir, this) @@ -175,18 +175,16 @@ object ClassPath { override def isValidName(name: String) = !isTraitImplementation(name) } - @inline private def endsClass(s: String) = s.length > 6 && s.substring(s.length - 6) == ".class" - @inline private def endsScala(s: String) = s.length > 6 && s.substring(s.length - 6) == ".scala" - @inline private def endsJava(s: String) = s.length > 5 && s.substring(s.length - 5) == ".java" - /** From the source file to its identifier. */ def toSourceName(f: AbstractFile): String = { val name = f.name - - if (endsScala(name)) name.substring(0, name.length - 6) - else if (endsJava(name)) name.substring(0, name.length - 5) - else throw new FatalError("Unexpected source file ending: " + name) + if (name.length > 6 && name.substring(name.length - 6) == ".scala") + name.substring(0, name.length - 6) + else if (name.length > 5 && name.substring(name.length - 5) == ".java") + name.substring(0, name.length - 5) + else + throw new FatalError("Unexpected source file ending: " + name) } } import ClassPath._ @@ -261,9 +259,10 @@ abstract class ClassPath[T] { /** Filters for assessing validity of various entities. */ - def validClassFile(name: String) = endsClass(name) && context.isValidName(name) - def validPackage(name: String) = (name != "META-INF") && (name != "") && (name.charAt(0) != '.') - def validSourceFile(name: String) = endsScala(name) || endsJava(name) + def validClassFile(name: String) = (name endsWith ".class") && context.isValidName(name) + def validPackage(name: String) = (name != "META-INF") && (name != "") && (name(0) != '.') + def validSourceFile(name: String) = validSourceExtensions exists (name endsWith _) + def validSourceExtensions = List(".scala", ".java") /** * Find a ClassRep given a class name of the form "package.subpackage.ClassName". @@ -292,7 +291,7 @@ abstract class ClassPath[T] { case x: ClassPath[_] => this.sortString == x.sortString case _ => false } - override def hashCode = sortString.hashCode() + override def hashCode = sortString.## } /** @@ -301,7 +300,10 @@ abstract class ClassPath[T] { class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends ClassPath[T] { def name = dir.name override def origin = dir.underlyingSource map (_.path) - def asURLs = if (dir.file == null) Nil else List(dir.toURL) + def asURLs = dir.file match { + case null => Nil + case file => File(file).toURL :: Nil + } def asClasspathString = dir.path val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq(dir) @@ -324,7 +326,10 @@ class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext[AbstractFile]) extends ClassPath[AbstractFile] { def name = dir.name override def origin = dir.underlyingSource map (_.path) - def asURLs = if (dir.file == null) Nil else List(dir.toURL) + def asURLs = dir.file match { + case null => Nil + case file => File(file).toURL :: Nil + } def asClasspathString = dir.path val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq() diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index 6cf0a9d94c..e23063640f 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -27,7 +27,7 @@ trait FileManager { */ def compareFiles(f1: File, f2: File): String = { val diffWriter = new StringWriter - val args = Array(f1.getPath(), f2.getPath()) + val args = Array(f1.getCanonicalPath(), f2.getCanonicalPath()) DiffPrint.doDiff(args, diffWriter) val res = diffWriter.toString diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index b894d7086b..bfa6427d21 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -976,7 +976,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor react { case Timeout(file) => - updateStatus(file.getAbsolutePath, TestState.Timeout) + updateStatus(file.getCanonicalPath, TestState.Timeout) val swr = new StringWriter val wr = new PrintWriter(swr, true) printInfoStart(file, wr) @@ -988,7 +988,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor case Result(file, logs) => val state = if (succeeded) TestState.Ok else TestState.Fail - updateStatus(file.getAbsolutePath, state) + updateStatus(file.getCanonicalPath, state) reportResult( state, logs.file, -- cgit v1.2.3