diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/io/AbstractFile.scala | 68 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/io/Directory.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/io/File.scala | 36 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/io/Path.scala | 19 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/io/PlainFile.scala | 30 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/io/Streamable.scala | 78 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/io/package.scala | 9 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/ClassPath.scala | 35 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/FileManager.scala | 2 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/Worker.scala | 4 |
10 files changed, 136 insertions, 147 deletions
diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala index 059ca9bd9e..bafb78e9f2 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,7 +84,6 @@ object AbstractFile { * </p> */ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] { - /** Returns the name of this abstract file. */ def name: String @@ -137,6 +136,8 @@ 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 <code>Global.getSourceFile()</code> to use the proper * encoding when converting to the char array. @@ -147,19 +148,7 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] { /** Returns contents of file (if applicable) in a byte array. */ @throws(classOf[IOException]) - def toByteArray: Array[Byte] = { - val in = input - var rest = sizeOption.get - val arr = new Array[Byte](rest) - while (rest > 0) { - val res = in.read(arr, arr.length - rest, rest) - if (res == -1) - throw new IOException("read error") - rest -= res - } - in.close() - arr - } + def toByteArray: Array[Byte] = Streamable.toByteArray(input, sizeOption getOrElse -1) /** Returns all abstract subfiles of this abstract directory. */ def iterator: Iterator[AbstractFile] @@ -186,43 +175,47 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] { * @return ... */ def lookupPath(path: String, directory: Boolean): AbstractFile = { - lookup((f, p, dir) => f.lookupName(p, dir), path, directory) + lookupPathInternal(path, directory, false) } /** Return an abstract file that does not check that `path' denotes * an existing file. */ def lookupPathUnchecked(path: String, directory: Boolean): AbstractFile = { - lookup((f, p, dir) => f.lookupNameUnchecked(p, dir), path, directory) + lookupPathInternal(path, directory, true) } - private def lookup(getFile: (AbstractFile, String, Boolean) => AbstractFile, - path0: String, - directory: Boolean): AbstractFile = { - val separator = JFile.separatorChar + private def lookupPathInternal(path0: String, directory: Boolean, unchecked: Boolean): AbstractFile = { // trim trailing '/'s - 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 path = ( + if (path0.charAt(path0.length - 1) != separator) path0 + else path0.substring(0, path0.length - 1) + ) + def loop(file: AbstractFile, start: Int): AbstractFile = { val index = path.indexOf(separator, start) - 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 + 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) + } } - file + loop(this, 0) } - private def fileOrSubdirectoryNamed(name: String, isDir: Boolean): AbstractFile = { - val lookup = lookupName(name, isDir) + private def fileOrSubdirectoryNamed(name: String, directory: Boolean): AbstractFile = { + val lookup = lookupName(name, directory) if (lookup != null) lookup else { val jfile = new JFile(file, name) - if (isDir) jfile.mkdirs() else jfile.createNewFile() + if (directory) jfile.mkdirs() else jfile.createNewFile() new PlainFile(jfile) } } @@ -249,6 +242,5 @@ 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 b4ceba682a..64b7711686 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(apply(Path(s).normalize)) + private def normalizePath(s: String) = Some(Directory(s).toAbsolute) 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 b11151ab7e..e5969f318e 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 = JFile.pathSeparator - def separator = JFile.separator + def pathSeparator = java.io.File.pathSeparator + def separator = java.io.File.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 = JFile.createTempFile(prefix, suffix, dir) + val jfile = java.io.File.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 = 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 => () - } + // 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 => () + // } } 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.normalize == dest.normalize) fail("Source and destination are the same.") + if (this isSame dest) 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 86839437f4..f95790785c 100644 --- a/src/compiler/scala/tools/nsc/io/Path.scala +++ b/src/compiler/scala/tools/nsc/io/Path.scala @@ -11,6 +11,7 @@ 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. @@ -65,9 +66,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] = JFile.listRoots().toList map Path.apply + def roots: List[Path] = java.io.File.listRoots().toList map Path.apply - def apply(segments: Seq[String]): Path = apply(segments mkString JFile.separator) + def apply(segments: Seq[String]): Path = apply(segments mkString separatorStr) def apply(path: String): Path = apply(new JFile(path)) def apply(jfile: JFile): Path = if (jfile.isFile) new File(jfile) @@ -84,9 +85,6 @@ 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 @@ -97,6 +95,7 @@ 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 @@ -130,7 +129,7 @@ class Path private[io] (val jfile: JFile) { // identity def name: String = jfile.getName() def path: String = jfile.getPath() - def normalize: Path = Path(jfile.getCanonicalPath()) + def normalize: Path = toCanonical def isRootPath: Boolean = roots exists (_ isSame this) def resolve(other: Path) = if (other.isAbsolute || isEmpty) other else /(other) @@ -140,7 +139,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) => ((".."+separator)*bs.length)+os.mkString(separatorStr) + case (bs, os) => ((".."+separatorChar)*bs.length)+os.mkString(separatorStr) } } @@ -149,7 +148,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 separator).toList filterNot (_.length == 0) + def segments: List[String] = (path split separatorChar).toList filterNot (_.length == 0) /** * @return The path of the parent directory, or root if path is already root */ @@ -212,7 +211,7 @@ class Path private[io] (val jfile: JFile) { def isHidden = jfile.isHidden() def isSymlink = { val x = parent / name - x.normalize != x.toAbsolute + x.toCanonical != x.toAbsolute } def isEmpty = path.length == 0 @@ -224,7 +223,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) = normalize == other.normalize + def isSame(other: Path) = toCanonical == other.toCanonical 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 ce4bca490f..fb19ec120b 100644 --- a/src/compiler/scala/tools/nsc/io/PlainFile.scala +++ b/src/compiler/scala/tools/nsc/io/PlainFile.scala @@ -3,13 +3,9 @@ * @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 @@ -24,28 +20,22 @@ object PlainFile { class PlainFile(val givenPath: Path) extends AbstractFile { assert(path ne null) - 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) + def name = givenPath.name + def path = givenPath.path + def file = givenPath.jfile + def absolute = new PlainFile(givenPath.toAbsolute) + override def underlyingSource = Some(this) 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 = fpath.hashCode - override def equals(that: Any): Boolean = - cond(that) { case other: PlainFile => fpath == other.fpath } + override def hashCode(): Int = path.hashCode + override def equals(that: Any): Boolean = that match { + case other: PlainFile => path == other.path + case _ => false + } /** 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 03318674ee..c0a56eeda6 100644 --- a/src/compiler/scala/tools/nsc/io/Streamable.scala +++ b/src/compiler/scala/tools/nsc/io/Streamable.scala @@ -20,14 +20,60 @@ 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 length: Long = -1 def bufferedInput() = new BufferedInputStream(inputStream()) def bytes(): Iterator[Byte] = bytesAsInts() map (_.toByte) def bytesAsInts(): Iterator[Int] = { @@ -35,33 +81,9 @@ object Streamable { Iterator continually in.read() takeWhile (_ != -1) } - /** 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)) + def toByteArray() = { + require(length <= Int.MaxValue, length + " larger than Int.MaxValue") + Streamable.toByteArray(inputStream(), length.toInt) } } diff --git a/src/compiler/scala/tools/nsc/io/package.scala b/src/compiler/scala/tools/nsc/io/package.scala index 29174b161f..91cf715e33 100644 --- a/src/compiler/scala/tools/nsc/io/package.scala +++ b/src/compiler/scala/tools/nsc/io/package.scala @@ -12,15 +12,6 @@ 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 25c09b6c15..be31844e60 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(name.length > 6 && name.substring(name.length - 6) == ".class", name) + assert(endsClass(name), name) name.substring(0, name.length - 6) } def newClassPath(dir: AbstractFile) = new DirectoryClassPath(dir, this) @@ -175,16 +175,18 @@ 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 (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) + + 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) } } import ClassPath._ @@ -259,10 +261,9 @@ abstract class ClassPath[T] { /** Filters for assessing validity of various entities. */ - 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") + 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) /** * Find a ClassRep given a class name of the form "package.subpackage.ClassName". @@ -291,7 +292,7 @@ abstract class ClassPath[T] { case x: ClassPath[_] => this.sortString == x.sortString case _ => false } - override def hashCode = sortString.## + override def hashCode = sortString.hashCode() } /** @@ -300,10 +301,7 @@ 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 = dir.file match { - case null => Nil - case file => File(file).toURL :: Nil - } + def asURLs = if (dir.file == null) Nil else List(dir.toURL) def asClasspathString = dir.path val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq(dir) @@ -326,10 +324,7 @@ 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 = dir.file match { - case null => Nil - case file => File(file).toURL :: Nil - } + def asURLs = if (dir.file == null) Nil else List(dir.toURL) 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 e23063640f..6cf0a9d94c 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.getCanonicalPath(), f2.getCanonicalPath()) + val args = Array(f1.getPath(), f2.getPath()) 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 bfa6427d21..b894d7086b 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.getCanonicalPath, TestState.Timeout) + updateStatus(file.getAbsolutePath, 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.getCanonicalPath, state) + updateStatus(file.getAbsolutePath, state) reportResult( state, logs.file, |