diff --git a/bin/common b/bin/common index d1a9db482333..2796be5aec56 100755 --- a/bin/common +++ b/bin/common @@ -115,6 +115,11 @@ else echo "Failed to parse .packages file" build_all fi + + if [ ! -f "$INTERFACES_JAR" -o ! -f "$MAIN_JAR" -o ! -f "$DOTTY_LIB_JAR" -o ! -f "$TEST_JAR" ]; then + echo ".packages file corrupted, rebuilding" + build_all + fi fi ################# After this point, jar variables will be set ################# @@ -142,10 +147,6 @@ if [ "$SCALA_LIBRARY_JAR" == "" ]; then SCALA_LIBRARY_JAR=$(find_jar "$HOME/.ivy2/cache/org.scala-lang/scala-library/jars" "scala-library-$SCALA_VERSION.jar") fi -if [ "$SCALA_REFLECT_JAR" == "" ]; then - SCALA_REFLECT_JAR=$(find_jar "$HOME/.ivy2/cache/org.scala-lang/scala-reflect/jars" "scala-reflect-$SCALA_VERSION.jar") -fi - if [ "$SCALA_ASM_JAR" == "" ]; then SCALA_ASM_JAR=$(find_jar "$HOME/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles" "scala-asm-$SCALA_ASM_VERSION.jar") fi diff --git a/bin/dotc b/bin/dotc index 39e3f8074b53..47fe1cea6a46 100755 --- a/bin/dotc +++ b/bin/dotc @@ -20,11 +20,10 @@ CompilerMain=dotty.tools.dotc.Main FromTasty=dotty.tools.dotc.FromTasty ReplMain=dotty.tools.dotc.repl.Main -if [ ! -f "$SCALA_LIBRARY_JAR" -o ! -f "$SCALA_REFLECT_JAR" -o ! -f "$SCALA_ASM_JAR" -o ! -f "$SBT_INTERFACE_JAR" ] +if [ ! -f "$SCALA_LIBRARY_JAR" -o ! -f "$SCALA_ASM_JAR" -o ! -f "$SBT_INTERFACE_JAR" ] then echo To use this script please set echo SCALA_LIBRARY_JAR to point to scala-library-$SCALA_VERSION.jar "(currently $SCALA_LIBRARY_JAR)" - echo SCALA_REFLECT_JAR to point to scala-reflect-$SCALA_VERSION.jar "(currently $SCALA_REFLECT_JAR)" echo SCALA_ASM_JAR to point to scala-asm-$SCALA_ASM_VERSION.jar "(currently $SCALA_ASM_JAR)" echo SBT_INTERFACE_JAR to point to interface-$SBT_VERSION.jar "(currently $SBT_INTERFACE_JAR)" fi @@ -116,9 +115,9 @@ trap onExit INT classpathArgs () { if [[ "true" == "$bootstrapped" ]]; then check_jar "dotty-bootstrapped" "$DOTTY_JAR" "target" 'build_jar "test:runMain dotc.build" target' &> /dev/null - toolchain="$DOTTY_JAR:$DOTTY_LIB_JAR:$SCALA_LIBRARY_JAR:$SCALA_REFLECT_JAR:$SCALA_ASM_JAR:$SBT_INTERFACE_JAR" + toolchain="$DOTTY_JAR:$DOTTY_LIB_JAR:$SCALA_LIBRARY_JAR:$SCALA_ASM_JAR:$SBT_INTERFACE_JAR" else - toolchain="$SCALA_LIBRARY_JAR:$DOTTY_LIB_JAR:$SCALA_REFLECT_JAR:$SCALA_ASM_JAR:$SBT_INTERFACE_JAR" + toolchain="$SCALA_LIBRARY_JAR:$DOTTY_LIB_JAR:$SCALA_ASM_JAR:$SBT_INTERFACE_JAR" fi bcpJars="$INTERFACES_JAR:$MAIN_JAR:$DOTTY_LIB_JAR" cpJars="$INTERFACES_JAR:$MAIN_JAR:$DOTTY_LIB_JAR:$TEST_JAR" diff --git a/bin/test/TestScripts.scala b/bin/test/TestScripts.scala index 6543ac7b7899..be0633450e7f 100644 --- a/bin/test/TestScripts.scala +++ b/bin/test/TestScripts.scala @@ -17,9 +17,8 @@ class TestScripts { private def executeScript(script: String): (Int, String) = { val sb = new StringBuilder - val ret = Process(script) ! ProcessLogger { line => println(line); sb.append(line) } + val ret = Process(script) ! ProcessLogger { line => println(line); sb.append(line + "\n") } val output = sb.toString - println(output) // For CI, otherwise "terminal inactive for 5m0s, build cancelled" (ret, output) } @@ -59,7 +58,7 @@ class TestScripts { val (retDotr, dotrOutput) = executeScript("./bin/dotr HelloWorld") assert( - retDotr == 0 && dotrOutput == "hello world", + retDotr == 0 && dotrOutput == "hello world\n", s"Running hello world exited with status: $retDotr and output: $dotrOutput" ) } @@ -93,8 +92,19 @@ class TestScripts { /** dotc script should work after corrupting .packages */ @Test def reCreatesPackagesIfNecessary = doUnlessWindows { - executeScript("sed -i.old 's/2.1/2.X/' ./.packages") // That's going to replace 2.11 with 2.X1 - val (retFirstBuild, _) = executeScript("./bin/dotc ./tests/pos/HelloWorld.scala") + import java.nio.file.{Paths, Files} + import java.nio.charset.StandardCharsets + val contents = + """|/Users/fixel/Projects/dotty/interfaces/target/dotty-interfaces-0.1.1-bin-SNAPSHOT-X.jar + |/Users/fixel/Projects/dotty/compiler/target/scala-2.11/dotty-compiler_2.1X-0.1.1-bin-SNAPSHOT.jar + |/Users/fixel/Projects/dotty/library/target/scala-2.11/dotty-library_2.1X-0.1.1-bin-SNAPSHOT.jar + |/Users/fixel/Projects/dotty/doc-tool/target/scala-2.11/dotty-doc_2.1X-0.1.1-bin-SNAPSHOT-tests.jar""" + .stripMargin + + Files.write(Paths.get("./.packages"), contents.getBytes(StandardCharsets.UTF_8)) + + val (retFirstBuild, output) = executeScript("./bin/dotc ./tests/pos/HelloWorld.scala") + assert(output.contains(".packages file corrupted")) assert(retFirstBuild == 0, "building dotc failed") } } diff --git a/compiler/sjs/backend/sjs/JSCodeGen.scala b/compiler/sjs/backend/sjs/JSCodeGen.scala index 69a5651fc5c3..25974a641251 100644 --- a/compiler/sjs/backend/sjs/JSCodeGen.scala +++ b/compiler/sjs/backend/sjs/JSCodeGen.scala @@ -182,7 +182,7 @@ class JSCodeGen()(implicit ctx: Context) { private def getFileFor(cunit: CompilationUnit, sym: Symbol, suffix: String) = { - import scala.reflect.io._ + import dotty.tools.io._ val outputDirectory: AbstractFile = // TODO Support virtual files new PlainDirectory(new Directory(new java.io.File(ctx.settings.d.value))) diff --git a/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala b/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala index abcbbbb83059..bd3a1894c2cc 100644 --- a/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala +++ b/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala @@ -15,7 +15,7 @@ import java.io.{File => JFile} import scala.collection.generic.Clearable import scala.collection.mutable import scala.reflect.ClassTag -import scala.reflect.io.{Directory, PlainDirectory, AbstractFile} +import dotty.tools.io.{Directory, PlainDirectory, AbstractFile} import scala.tools.asm.{ClassVisitor, FieldVisitor, MethodVisitor} import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface} import dotty.tools.dotc.core._ diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 923f0a95a632..709ce4616f9b 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -12,7 +12,7 @@ import scala.collection.generic.Clearable import scala.collection.mutable import scala.reflect.ClassTag import scala.reflect.internal.util.WeakHashSet -import scala.reflect.io.{AbstractFile, Directory, PlainDirectory} +import dotty.tools.io.{AbstractFile, Directory, PlainDirectory} import scala.tools.asm.{AnnotationVisitor, ClassVisitor, FieldVisitor, MethodVisitor} import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface} import dotty.tools.dotc.core._ diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 90f13f83a791..c1100f4f1e6b 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -33,7 +33,7 @@ import dotty.tools.dotc.util.{DotClass, Positions} import tpd._ import StdNames._ -import scala.reflect.io.{AbstractFile, Directory, PlainDirectory} +import dotty.tools.io.{AbstractFile, Directory, PlainDirectory} class GenBCode extends Phase { def phaseName: String = "genBCode" @@ -62,10 +62,10 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter val sourceFile = ctx.compilationUnit.source - /** Convert a `scala.reflect.io.AbstractFile` into a + /** Convert a `dotty.tools.io.AbstractFile` into a * `dotty.tools.dotc.interfaces.AbstractFile`. */ - private[this] def convertAbstractFile(absfile: scala.reflect.io.AbstractFile): interfaces.AbstractFile = + private[this] def convertAbstractFile(absfile: dotty.tools.io.AbstractFile): interfaces.AbstractFile = new interfaces.AbstractFile { override def name = absfile.name override def path = absfile.path diff --git a/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala b/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala index 1950d300d915..d4f09e99159a 100644 --- a/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala +++ b/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala @@ -16,7 +16,7 @@ import scala.collection.generic.Clearable import scala.collection.mutable import scala.collection.mutable.{ListBuffer, ArrayBuffer} import scala.reflect.ClassTag -import scala.reflect.io.{Directory, PlainDirectory, AbstractFile} +import dotty.tools.io.{Directory, PlainDirectory, AbstractFile} import scala.tools.asm.{ClassVisitor, FieldVisitor, MethodVisitor} import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface} import dotty.tools.dotc.core._ diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 23c45e354b6c..acd885dd3afe 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -18,7 +18,7 @@ import java.io.{BufferedWriter, OutputStreamWriter} import printing.XprintMode import scala.annotation.tailrec -import scala.reflect.io.VirtualFile +import dotty.tools.io.VirtualFile import scala.util.control.NonFatal /** A compiler run. Exports various methods to compile source files */ diff --git a/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala index ec3e8fdf4bdd..c6b15d773614 100644 --- a/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala @@ -1,15 +1,13 @@ /* * Copyright (c) 2014 Contributor. All rights reserved. */ -package dotty.tools.dotc.classpath +package dotty.tools +package dotc.classpath import java.net.URL import scala.annotation.tailrec import scala.collection.mutable.ArrayBuffer -import scala.reflect.internal.FatalError -import scala.reflect.io.AbstractFile -import dotty.tools.io.ClassPath -import dotty.tools.io.ClassRepresentation +import dotty.tools.io.{ AbstractFile, ClassPath, ClassRepresentation } /** * A classpath unifying multiple class- and sourcepath entries. diff --git a/compiler/src/dotty/tools/dotc/classpath/ClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/ClassPath.scala index 129c6b9feb25..89c68ae67112 100644 --- a/compiler/src/dotty/tools/dotc/classpath/ClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/ClassPath.scala @@ -3,7 +3,7 @@ */ package dotty.tools.dotc.classpath -import scala.reflect.io.AbstractFile +import dotty.tools.io.AbstractFile import dotty.tools.io.ClassRepresentation case class ClassPathEntries(packages: Seq[PackageEntry], classesAndSources: Seq[ClassRepresentation]) diff --git a/compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala b/compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala index ac8fc633fdc0..86dde8a23cd7 100644 --- a/compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala +++ b/compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala @@ -3,8 +3,8 @@ */ package dotty.tools.dotc.classpath -import scala.reflect.io.{AbstractFile, VirtualDirectory} -import scala.reflect.io.Path.string2path +import dotty.tools.io.{AbstractFile, VirtualDirectory} +import dotty.tools.io.Path.string2path import dotty.tools.dotc.config.Settings import FileUtils.AbstractFileOps import dotty.tools.io.ClassPath diff --git a/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala index 1ed233ed72fe..0a3496be4f50 100644 --- a/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala @@ -10,8 +10,7 @@ import java.util.function.IntFunction import java.util import java.util.Comparator -import scala.reflect.io.{AbstractFile, PlainFile} -import dotty.tools.io.{ClassPath, ClassRepresentation, PlainNioFile} +import dotty.tools.io.{AbstractFile, PlainFile, ClassPath, ClassRepresentation, PlainNioFile} import FileUtils._ import scala.collection.JavaConverters._ @@ -118,7 +117,7 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo } else Array() } protected def getName(f: File): String = f.getName - protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new scala.reflect.io.File(f)) + protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new dotty.tools.io.File(f)) protected def isPackage(f: File): Boolean = f.isPackage assert(dir != null, "Directory file in DirectoryFileLookup cannot be null") @@ -208,7 +207,7 @@ case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileE val relativePath = FileUtils.dirPath(className) val classFile = new File(s"$dir/$relativePath.class") if (classFile.exists) { - val wrappedClassFile = new scala.reflect.io.File(classFile) + val wrappedClassFile = new dotty.tools.io.File(classFile) val abstractClassFile = new PlainFile(wrappedClassFile) Some(abstractClassFile) } else None @@ -235,7 +234,7 @@ case class DirectorySourcePath(dir: File) extends JFileDirectoryLookup[SourceFil .collectFirst { case file if file.exists() => file } sourceFile.map { file => - val wrappedSourceFile = new scala.reflect.io.File(file) + val wrappedSourceFile = new dotty.tools.io.File(file) val abstractSourceFile = new PlainFile(wrappedSourceFile) abstractSourceFile } diff --git a/compiler/src/dotty/tools/dotc/classpath/FileUtils.scala b/compiler/src/dotty/tools/dotc/classpath/FileUtils.scala index 823efbb9df52..ef571dbbf4b5 100644 --- a/compiler/src/dotty/tools/dotc/classpath/FileUtils.scala +++ b/compiler/src/dotty/tools/dotc/classpath/FileUtils.scala @@ -1,12 +1,12 @@ /* * Copyright (c) 2014 Contributor. All rights reserved. */ -package dotty.tools.dotc.classpath +package dotty.tools +package dotc.classpath import java.io.{File => JFile, FileFilter} import java.net.URL -import scala.reflect.internal.FatalError -import scala.reflect.io.AbstractFile +import dotty.tools.io.AbstractFile /** * Common methods related to Java files and abstract files used in the context of classpath diff --git a/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala index 5b08555540b7..51a97ef7ac14 100644 --- a/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala @@ -1,11 +1,10 @@ package dotty.tools.dotc.classpath import dotty.tools.io.ClassRepresentation -import scala.reflect.io.{AbstractFile, Path, PlainFile, VirtualDirectory} +import dotty.tools.io.{AbstractFile, Path, PlainFile, VirtualDirectory} import FileUtils._ import java.net.URL -import scala.reflect.internal.util.AbstractFileClassLoader import dotty.tools.io.ClassPath case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { diff --git a/compiler/src/dotty/tools/dotc/classpath/ZipAndJarFileLookupFactory.scala b/compiler/src/dotty/tools/dotc/classpath/ZipAndJarFileLookupFactory.scala index 5210c699efc2..648fb59497c0 100644 --- a/compiler/src/dotty/tools/dotc/classpath/ZipAndJarFileLookupFactory.scala +++ b/compiler/src/dotty/tools/dotc/classpath/ZipAndJarFileLookupFactory.scala @@ -6,8 +6,7 @@ package dotty.tools.dotc.classpath import java.io.File import java.net.URL import scala.annotation.tailrec -import scala.reflect.io.{AbstractFile, FileZipArchive, ManifestResources} -import dotty.tools.io.ClassPath +import dotty.tools.io.{AbstractFile, ClassPath, FileZipArchive, ManifestResources} import dotty.tools.dotc.config.Settings import dotty.tools.dotc.core.Contexts.Context import FileUtils._ diff --git a/compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala b/compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala index 8184708ad704..352a566097fe 100644 --- a/compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala +++ b/compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala @@ -6,8 +6,7 @@ package dotty.tools.dotc.classpath import java.io.File import java.net.URL import scala.collection.Seq -import scala.reflect.io.AbstractFile -import scala.reflect.io.FileZipArchive +import dotty.tools.io.{ AbstractFile, FileZipArchive } import FileUtils.AbstractFileOps import dotty.tools.io.{ClassPath, ClassRepresentation} diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 310838d8aa73..d3865d1e0ce1 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -8,7 +8,7 @@ import NameOps._, NameKinds._ import Scopes.Scope import collection.mutable import collection.BitSet -import scala.reflect.io.AbstractFile +import dotty.tools.io.AbstractFile import Decorators.SymbolIteratorDecorator import ast._ import annotation.tailrec diff --git a/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala index 98f098fbcbf3..3010f7761763 100644 --- a/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala +++ b/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala @@ -15,7 +15,7 @@ import scala.collection.mutable.{ListBuffer, HashSet, ArrayBuffer} //import ast.parser.SyntaxAnalyzer import io.{PlainFile, VirtualDirectory} -import scala.reflect.io.{PlainDirectory, Directory} +import dotty.tools.io.{PlainDirectory, Directory} import reporting.{ConsoleReporter, Reporter} import core.Flags import util.{SourceFile, NameTransformer} diff --git a/compiler/src/dotty/tools/dotc/repl/REPL.scala b/compiler/src/dotty/tools/dotc/repl/REPL.scala index 211e3c93196a..11ea7636d1a4 100644 --- a/compiler/src/dotty/tools/dotc/repl/REPL.scala +++ b/compiler/src/dotty/tools/dotc/repl/REPL.scala @@ -5,7 +5,7 @@ package repl import core.Contexts.Context import reporting.Reporter import io.{AbstractFile, PlainFile, VirtualDirectory} -import scala.reflect.io.{PlainDirectory, Directory} +import dotty.tools.io.{PlainDirectory, Directory} import java.io.{BufferedReader, File => JFile, FileReader, PrintWriter} import java.net.{URL, URLClassLoader} diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index fcef88eb3481..5992fe350c7d 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -8,7 +8,7 @@ import Names._, NameOps._, StdNames._ import scala.collection.{Set, mutable} -import dotty.tools.io.{AbstractFile, Path, ZipArchive} +import dotty.tools.io.{AbstractFile, Path, ZipArchive, PlainFile} import java.io.File import java.util.{Arrays, Comparator} @@ -101,7 +101,7 @@ class ExtractDependencies extends Phase { val classSegments = Path(ze.path).segments binaryDependency(zipFile, className(classSegments)) } - case pf: scala.reflect.io.PlainFile => + case pf: PlainFile => val packages = dep.ownersIterator .filter(x => x.is(PackageClass) && !x.isEffectiveRoot).length // We can recover the fully qualified name of a classfile from diff --git a/compiler/src/dotty/tools/dotc/util/WeakHashSet.scala b/compiler/src/dotty/tools/dotc/util/WeakHashSet.scala new file mode 100644 index 000000000000..641dc7654439 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/WeakHashSet.scala @@ -0,0 +1,410 @@ +/** Taken from the original implementation of WeakHashSet in scala-reflect + * + * @author: Eugene Burmako + */ +package dotty.tools.dotc.util + +import java.lang.ref.{WeakReference, ReferenceQueue} +import scala.annotation.tailrec +import scala.collection.generic.Clearable +import scala.collection.mutable.{Set => MSet} + +/** + * A HashSet where the elements are stored weakly. Elements in this set are elligible for GC if no other + * hard references are associated with them. Its primary use case is as a canonical reference + * identity holder (aka "hash-consing") via findEntryOrUpdate + * + * This Set implementation cannot hold null. Any attempt to put a null in it will result in a NullPointerException + * + * This set implmeentation is not in general thread safe without external concurrency control. However it behaves + * properly when GC concurrently collects elements in this set. + */ +final class WeakHashSet[A >: Null <: AnyRef](val initialCapacity: Int, val loadFactor: Double) extends Set[A] with Function1[A, Boolean] with MSet[A] { + + import WeakHashSet._ + + def this() = this(initialCapacity = WeakHashSet.defaultInitialCapacity, loadFactor = WeakHashSet.defaultLoadFactor) + + type This = WeakHashSet[A] + + /** + * queue of Entries that hold elements scheduled for GC + * the removeStaleEntries() method works through the queue to remeove + * stale entries from the table + */ + private[this] val queue = new ReferenceQueue[A] + + /** + * the number of elements in this set + */ + private[this] var count = 0 + + /** + * from a specified initial capacity compute the capacity we'll use as being the next + * power of two equal to or greater than the specified initial capacity + */ + private def computeCapacity = { + if (initialCapacity < 0) throw new IllegalArgumentException("initial capacity cannot be less than 0"); + var candidate = 1 + while (candidate < initialCapacity) { + candidate *= 2 + } + candidate + } + + /** + * the underlying table of entries which is an array of Entry linked lists + */ + private[this] var table = new Array[Entry[A]](computeCapacity) + + /** + * the limit at which we'll increase the size of the hash table + */ + var threshhold = computeThreshHold + + private[this] def computeThreshHold: Int = (table.size * loadFactor).ceil.toInt + + /** + * find the bucket associated with an elements's hash code + */ + private[this] def bucketFor(hash: Int): Int = { + // spread the bits around to try to avoid accidental collisions using the + // same algorithm as java.util.HashMap + var h = hash + h ^= h >>> 20 ^ h >>> 12 + h ^= h >>> 7 ^ h >>> 4 + + // this is finding h % table.length, but takes advantage of the + // fact that table length is a power of 2, + // if you don't do bit flipping in your head, if table.length + // is binary 100000.. (with n 0s) then table.length - 1 + // is 1111.. with n 1's. + // In other words this masks on the last n bits in the hash + h & (table.length - 1) + } + + /** + * remove a single entry from a linked list in a given bucket + */ + private[this] def remove(bucket: Int, prevEntry: Entry[A], entry: Entry[A]): Unit = { + prevEntry match { + case null => table(bucket) = entry.tail + case _ => prevEntry.tail = entry.tail + } + count -= 1 + } + + /** + * remove entries associated with elements that have been gc'ed + */ + private[this] def removeStaleEntries(): Unit = { + def poll(): Entry[A] = queue.poll().asInstanceOf[Entry[A]] + + @tailrec + def queueLoop(): Unit = { + val stale = poll() + if (stale != null) { + val bucket = bucketFor(stale.hash) + + @tailrec + def linkedListLoop(prevEntry: Entry[A], entry: Entry[A]): Unit = if (stale eq entry) remove(bucket, prevEntry, entry) + else if (entry != null) linkedListLoop(entry, entry.tail) + + linkedListLoop(null, table(bucket)) + + queueLoop() + } + } + + queueLoop() + } + + /** + * Double the size of the internal table + */ + private[this] def resize(): Unit = { + val oldTable = table + table = new Array[Entry[A]](oldTable.size * 2) + threshhold = computeThreshHold + + @tailrec + def tableLoop(oldBucket: Int): Unit = if (oldBucket < oldTable.size) { + @tailrec + def linkedListLoop(entry: Entry[A]): Unit = entry match { + case null => () + case _ => { + val bucket = bucketFor(entry.hash) + val oldNext = entry.tail + entry.tail = table(bucket) + table(bucket) = entry + linkedListLoop(oldNext) + } + } + linkedListLoop(oldTable(oldBucket)) + + tableLoop(oldBucket + 1) + } + tableLoop(0) + } + + // from scala.reflect.internal.Set, find an element or null if it isn't contained + override def findEntry(elem: A): A = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + + @tailrec + def linkedListLoop(entry: Entry[A]): A = entry match { + case null => null.asInstanceOf[A] + case _ => { + val entryElem = entry.get + if (elem == entryElem) entryElem + else linkedListLoop(entry.tail) + } + } + + linkedListLoop(table(bucket)) + } + } + // add an element to this set unless it's already in there and return the element + def findEntryOrUpdate(elem: A): A = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + val oldHead = table(bucket) + + def add() = { + table(bucket) = new Entry(elem, hash, oldHead, queue) + count += 1 + if (count > threshhold) resize() + elem + } + + @tailrec + def linkedListLoop(entry: Entry[A]): A = entry match { + case null => add() + case _ => { + val entryElem = entry.get + if (elem == entryElem) entryElem + else linkedListLoop(entry.tail) + } + } + + linkedListLoop(oldHead) + } + } + + // add an element to this set unless it's already in there and return this set + override def +(elem: A): this.type = elem match { + case null => throw new NullPointerException("WeakHashSet cannot hold nulls") + case _ => { + removeStaleEntries() + val hash = elem.hashCode + val bucket = bucketFor(hash) + val oldHead = table(bucket) + + def add() = { + table(bucket) = new Entry(elem, hash, oldHead, queue) + count += 1 + if (count > threshhold) resize() + } + + @tailrec + def linkedListLoop(entry: Entry[A]): Unit = entry match { + case null => add() + case _ if (elem == entry.get) => () + case _ => linkedListLoop(entry.tail) + } + + linkedListLoop(oldHead) + this + } + } + + def +=(elem: A) = this + elem + + // from scala.reflect.interanl.Set + override def addEntry(x: A) = { this += x } + + // remove an element from this set and return this set + override def -(elem: A): this.type = elem match { + case null => this + case _ => { + removeStaleEntries() + val bucket = bucketFor(elem.hashCode) + + + + @tailrec + def linkedListLoop(prevEntry: Entry[A], entry: Entry[A]): Unit = entry match { + case null => () + case _ if (elem == entry.get) => remove(bucket, prevEntry, entry) + case _ => linkedListLoop(entry, entry.tail) + } + + linkedListLoop(null, table(bucket)) + this + } + } + + def -=(elem: A) = this - elem + + // empty this set + override def clear(): Unit = { + table = new Array[Entry[A]](table.size) + threshhold = computeThreshHold + count = 0 + + // drain the queue - doesn't do anything because we're throwing away all the values anyway + @tailrec def queueLoop(): Unit = if (queue.poll() != null) queueLoop() + queueLoop() + } + + // true if this set is empty + override def empty: This = new WeakHashSet[A](initialCapacity, loadFactor) + + // the number of elements in this set + override def size: Int = { + removeStaleEntries() + count + } + + override def apply(x: A): Boolean = this contains x + + override def foreach[U](f: A => U): Unit = iterator foreach f + + // It has the `()` because iterator runs `removeStaleEntries()` + override def toList(): List[A] = iterator.toList + + // Iterator over all the elements in this set in no particular order + override def iterator: Iterator[A] = { + removeStaleEntries() + + new Iterator[A] { + + /** + * the bucket currently being examined. Initially it's set past the last bucket and will be decremented + */ + private[this] var currentBucket: Int = table.size + + /** + * the entry that was last examined + */ + private[this] var entry: Entry[A] = null + + /** + * the element that will be the result of the next call to next() + */ + private[this] var lookaheadelement: A = null.asInstanceOf[A] + + @tailrec + def hasNext: Boolean = { + while (entry == null && currentBucket > 0) { + currentBucket -= 1 + entry = table(currentBucket) + } + + if (entry == null) false + else { + lookaheadelement = entry.get + if (lookaheadelement == null) { + // element null means the weakref has been cleared since we last did a removeStaleEntries(), move to the next entry + entry = entry.tail + hasNext + } else { + true + } + } + } + + def next(): A = if (lookaheadelement == null) + throw new IndexOutOfBoundsException("next on an empty iterator") + else { + val result = lookaheadelement + lookaheadelement = null.asInstanceOf[A] + entry = entry.tail + result + } + } + } + + /** + * Diagnostic information about the internals of this set. Not normally + * needed by ordinary code, but may be useful for diagnosing performance problems + */ + private[util] class Diagnostics { + /** + * Verify that the internal structure of this hash set is fully consistent. + * Throws an assertion error on any problem. In order for it to be reliable + * the entries must be stable. If any are garbage collected during validation + * then an assertion may inappropriately fire. + */ + def fullyValidate: Unit = { + var computedCount = 0 + var bucket = 0 + while (bucket < table.size) { + var entry = table(bucket) + while (entry != null) { + assert(entry.get != null, s"$entry had a null value indicated that gc activity was happening during diagnostic validation or that a null value was inserted") + computedCount += 1 + val cachedHash = entry.hash + val realHash = entry.get.hashCode + assert(cachedHash == realHash, s"for $entry cached hash was $cachedHash but should have been $realHash") + val computedBucket = bucketFor(realHash) + assert(computedBucket == bucket, s"for $entry the computed bucket was $computedBucket but should have been $bucket") + + entry = entry.tail + } + + bucket += 1 + } + + assert(computedCount == count, s"The computed count was $computedCount but should have been $count") + } + + /** + * Produces a diagnostic dump of the table that underlies this hash set. + */ + def dump = table.deep + + /** + * Number of buckets that hold collisions. Useful for diagnosing performance issues. + */ + def collisionBucketsCount: Int = + (table filter (entry => entry != null && entry.tail != null)).size + + /** + * Number of buckets that are occupied in this hash table. + */ + def fullBucketsCount: Int = + (table filter (entry => entry != null)).size + + /** + * Number of buckets in the table + */ + def bucketsCount: Int = table.size + } + + private[util] def diagnostics = new Diagnostics +} + +/** + * Companion object for WeakHashSet + */ +object WeakHashSet { + /** + * A single entry in a WeakHashSet. It's a WeakReference plus a cached hash code and + * a link to the next Entry in the same bucket + */ + private class Entry[A](element: A, val hash:Int, var tail: Entry[A], queue: ReferenceQueue[A]) extends WeakReference[A](element, queue) + + val defaultInitialCapacity = 16 + val defaultLoadFactor = .75 + + def apply[A >: Null <: AnyRef](initialCapacity: Int = WeakHashSet.defaultInitialCapacity, loadFactor: Double = WeakHashSet.defaultLoadFactor) = + new WeakHashSet[A](initialCapacity, defaultLoadFactor) +} diff --git a/compiler/src/dotty/tools/io/AbstractFile.scala b/compiler/src/dotty/tools/io/AbstractFile.scala new file mode 100644 index 000000000000..1ae9bcea423b --- /dev/null +++ b/compiler/src/dotty/tools/io/AbstractFile.scala @@ -0,0 +1,265 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ + +package dotty.tools.io + +import java.io.{ + IOException, InputStream, OutputStream, BufferedOutputStream, + ByteArrayOutputStream +} +import java.net.URL + +/** + * An abstraction over files for use in the reflection/compiler libraries. + * + * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' + * + * @author Philippe Altherr + * @version 1.0, 23/03/2004 + */ +object AbstractFile { + /** Returns "getFile(new File(path))". */ + def getFile(path: String): AbstractFile = getFile(File(path)) + def getFile(path: Path): AbstractFile = getFile(path.toFile) + + /** + * If the specified File exists and is a regular file, returns an + * abstract regular file backed by it. Otherwise, returns `null`. + */ + def getFile(file: File): AbstractFile = + if (file.isFile) new PlainFile(file) else null + + /** Returns "getDirectory(new File(path))". */ + def getDirectory(path: Path): AbstractFile = getDirectory(path.toFile) + + /** + * If the specified File exists and is either a directory or a + * readable zip or jar archive, returns an abstract directory + * backed by it. Otherwise, returns `null`. + */ + def getDirectory(file: File): AbstractFile = + if (file.isDirectory) new PlainFile(file) + else if (file.isFile && Path.isExtensionJarOrZip(file.jfile)) ZipArchive fromFile file + else null + + /** + * If the specified URL exists and is a regular file or a directory, returns an + * abstract regular file or an abstract directory, respectively, backed by it. + * Otherwise, returns `null`. + */ + def getURL(url: URL): AbstractFile = + if (url.getProtocol == "file") { + val f = new java.io.File(url.getPath) + if (f.isDirectory) getDirectory(f) + else getFile(f) + } else null + + def getResources(url: URL): AbstractFile = ZipArchive fromManifestURL url +} + +/** + *
+ * This class and its children serve to unify handling of files and + * directories. These files and directories may or may not have some + * real counter part within the file system. For example, some file + * handles reference files within a zip archive or virtual ones + * that exist only in memory. + *
+ *+ * Every abstract file has a path (i.e. a full name) and a name + * (i.e. a short name) and may be backed by some real File. There are + * two different kinds of abstract files: regular files and + * directories. Regular files may be read and have a last modification + * time. Directories may list their content and look for subfiles with + * a specified name or path and of a specified kind. + *
+ *+ * The interface does not allow to access the content. + * The class `symtab.classfile.AbstractFileReader` accesses + * bytes, knowing that the character set of classfiles is UTF-8. For + * all other cases, the class `SourceFile` is used, which honors + * `global.settings.encoding.value`. + *
+ * + * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' + */ +abstract class AbstractFile extends Iterable[AbstractFile] { + + /** Returns the name of this abstract file. */ + def name: String + + /** Returns the path of this abstract file. */ + def path: String + + /** Returns the path of this abstract file in a canonical form. */ + def canonicalPath: String = if (file == null) path else file.getCanonicalPath + + /** Checks extension case insensitively. */ + def hasExtension(other: String) = extension == other.toLowerCase + private lazy val extension: String = Path.extension(name) + + /** The absolute file, if this is a relative file. */ + def absolute: AbstractFile + + /** Returns the containing directory of this abstract file */ + def container : AbstractFile + + /** Returns the underlying File if any and null otherwise. */ + def file: JFile + + /** 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 + } + + /** Does this abstract file represent something which can contain classfiles? */ + def isClassContainer = isDirectory || (file != null && (extension == "jar" || extension == "zip")) + + /** Create a file on disk, if one does not exist already. */ + def create(): Unit + + /** Delete the underlying file or directory (recursively). */ + def delete(): Unit + + /** Is this abstract file a directory? */ + def isDirectory: Boolean + + /** Does this abstract file correspond to something on-disk? */ + def isVirtual: Boolean = false + + /** Returns the time that this abstract file was last modified. */ + def lastModified: Long + + /** returns an input stream so the file can be read */ + def input: InputStream + + /** Returns an output stream for writing the file */ + def output: OutputStream + + /** Returns a buffered output stream for writing the file - defaults to out */ + def bufferedOutput: BufferedOutputStream = new BufferedOutputStream(output) + + /** 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. + */ + @throws(classOf[IOException]) + def toCharArray = new String(toByteArray).toCharArray + + /** Returns contents of file (if applicable) in a byte array. + */ + @throws(classOf[IOException]) + def toByteArray: Array[Byte] = { + val in = input + sizeOption match { + case Some(size) => + var rest = size + 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 + case None => + val out = new ByteArrayOutputStream() + var c = in.read() + while(c != -1) { + out.write(c) + c = in.read() + } + in.close() + out.toByteArray() + } + } + + /** Returns all abstract subfiles of this abstract directory. */ + def iterator: Iterator[AbstractFile] + + /** Returns the abstract file in this abstract directory with the specified + * name. If there is no such file, returns `null`. The argument + * `directory` tells whether to look for a directory or + * a regular file. + */ + def lookupName(name: String, directory: Boolean): AbstractFile + + /** Returns an abstract file with the given name. It does not + * check that it exists. + */ + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile + + /** 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) + } + + private def lookup(getFile: (AbstractFile, String, Boolean) => AbstractFile, + path0: String, + directory: Boolean): AbstractFile = { + val separator = java.io.File.separatorChar + // 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 index = path.indexOf(separator, start) + assert(index < 0 || start < index, ((path, directory, 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 + } + file + } + + 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 (isDir) jfile.mkdirs() else jfile.createNewFile() + new PlainFile(jfile) + } + } + + /** + * Get the file in this directory with the given name, + * creating an empty file if it does not already existing. + */ + def fileNamed(name: String): AbstractFile = { + assert(isDirectory, "Tried to find '%s' in '%s' but it is not a directory".format(name, path)) + fileOrSubdirectoryNamed(name, isDir = false) + } + + /** + * Get the subdirectory with a given name, creating it if it + * does not already exist. + */ + def subdirectoryNamed(name: String): AbstractFile = { + assert (isDirectory, "Tried to find '%s' in '%s' but it is not a directory".format(name, path)) + fileOrSubdirectoryNamed(name, isDir = true) + } + + protected def unsupported(): Nothing = unsupported(null) + protected def unsupported(msg: String): Nothing = throw new UnsupportedOperationException(msg) + + /** Returns the path of this abstract file. */ + override def toString() = path + +} diff --git a/compiler/src/dotty/tools/io/DaemonThreadFactory.scala b/compiler/src/dotty/tools/io/DaemonThreadFactory.scala deleted file mode 100644 index ae0cda260014..000000000000 --- a/compiler/src/dotty/tools/io/DaemonThreadFactory.scala +++ /dev/null @@ -1,16 +0,0 @@ -package dotty.tools -package io - -import java.util.concurrent._ - -class DaemonThreadFactory extends ThreadFactory { - def newThread(r: Runnable): Thread = { - val thread = new Thread(r) - thread setDaemon true - thread - } -} - -object DaemonThreadFactory { - def newPool() = Executors.newCachedThreadPool(new DaemonThreadFactory) -} diff --git a/compiler/src/dotty/tools/io/Directory.scala b/compiler/src/dotty/tools/io/Directory.scala new file mode 100644 index 000000000000..6c23fd413194 --- /dev/null +++ b/compiler/src/dotty/tools/io/Directory.scala @@ -0,0 +1,66 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package dotty.tools.io + +/** + * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' + */ +object Directory { + import scala.util.Properties.userDir + + private def normalizePath(s: String) = Some(apply(Path(s).normalize)) + def Current: Option[Directory] = if (userDir == "") None else normalizePath(userDir) + + def apply(path: Path): Directory = path.toDirectory + + // Like File.makeTemp but creates a directory instead + def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null): Directory = { + val path = File.makeTemp(prefix, suffix, dir) + path.delete() + path.createDirectory() + } +} + +/** An abstraction for directories. + * + * @author Paul Phillips + * @since 2.8 + * + * ''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) { + 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 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 + } + + def dirs: Iterator[Directory] = list collect { case x: Directory => x } + def files: Iterator[File] = list collect { case x: File => x } + + override def walkFilter(cond: Path => Boolean): Iterator[Path] = + list filter cond flatMap (_ walkFilter cond) + + def deepFiles: Iterator[File] = Path.onlyFiles(deepList()) + + /** If optional depth argument is not given, will recurse + * until it runs out of contents. + */ + def deepList(depth: Int = -1): Iterator[Path] = + if (depth < 0) list ++ (dirs flatMap (_ deepList (depth))) + else if (depth == 0) Iterator.empty + else list ++ (dirs flatMap (_ deepList (depth - 1))) +} diff --git a/compiler/src/dotty/tools/io/File.scala b/compiler/src/dotty/tools/io/File.scala new file mode 100644 index 000000000000..61dcb1a1b6e0 --- /dev/null +++ b/compiler/src/dotty/tools/io/File.scala @@ -0,0 +1,114 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package dotty.tools.io + +import java.io.{ + FileInputStream, FileOutputStream, BufferedWriter, OutputStreamWriter, + BufferedOutputStream, IOException, PrintWriter +} + +import scala.io.Codec +/** + * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' + */ +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) + + // 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) + jfile.deleteOnExit() + apply(jfile) + } +} + +/** An abstraction for files. For character data, a Codec + * can be supplied at either creation time or when a method + * involving character data is called (with the latter taking + * precedence if supplied.) If neither is available, the value + * of scala.io.Codec.default is used. + * + * @author Paul Phillips + * @since 2.8 + * + * ''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 { + 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 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) + + /** Obtains a OutputStream. */ + def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append) + def bufferedOutput(append: Boolean = false) = new BufferedOutputStream(outputStream(append)) + + /** Obtains an OutputStreamWriter wrapped around a FileOutputStream. + * This should behave like a less broken version of java.io.FileWriter, + * in that unlike the java version you can specify the encoding. + */ + def writer(append: Boolean, codec: Codec): OutputStreamWriter = + new OutputStreamWriter(outputStream(append), codec.charSet) + + /** Wraps a BufferedWriter around the result of writer(). + */ + def bufferedWriter(): BufferedWriter = bufferedWriter(append = false) + def bufferedWriter(append: Boolean): BufferedWriter = bufferedWriter(append, creationCodec) + def bufferedWriter(append: Boolean, codec: Codec): BufferedWriter = + new BufferedWriter(writer(append, codec)) + + def printWriter(): PrintWriter = new PrintWriter(bufferedWriter(), true) + + /** Creates a new file and writes all the Strings to it. */ + def writeAll(strings: String*): Unit = { + val out = bufferedWriter() + try strings foreach (out write _) + finally out.close() + } + + def appendAll(strings: String*): Unit = { + val out = bufferedWriter(append = true) + try strings foreach (out write _) + finally out.close() + } + + /** Calls println on each string (so it adds a newline in the PrintWriter fashion.) */ + def printlnAll(strings: String*): Unit = { + val out = printWriter() + try strings foreach (out println _) + finally out.close() + } + + def safeSlurp(): Option[String] = + try Some(slurp()) + catch { case _: IOException => None } + + /** Reflection since we're into the java 6+ API. + */ + def setExecutable(executable: Boolean, ownerOnly: Boolean = true): Boolean = { + type JBoolean = java.lang.Boolean + val method = + 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 + catch { case _: Exception => false } + } +} diff --git a/compiler/src/dotty/tools/io/FileOperationException.scala b/compiler/src/dotty/tools/io/FileOperationException.scala new file mode 100644 index 000000000000..533384317732 --- /dev/null +++ b/compiler/src/dotty/tools/io/FileOperationException.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + + +package dotty.tools.io +/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ +case class FileOperationException(msg: String) extends RuntimeException(msg) diff --git a/compiler/src/dotty/tools/io/Fileish.scala b/compiler/src/dotty/tools/io/Fileish.scala deleted file mode 100644 index 0fcb133075b2..000000000000 --- a/compiler/src/dotty/tools/io/Fileish.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Paul Phillips - */ - -package dotty.tools -package io - -import java.io.{ InputStream } -import java.util.jar.JarEntry -import language.postfixOps - -/** A common interface for File-based things and Stream-based things. - * (In particular, io.File and JarEntry.) - */ -class Fileish(val path: Path, val input: () => InputStream) extends Streamable.Chars { - def inputStream() = input() - - def parent = path.parent - def name = path.name - def isSourceFile = path.hasExtension("java", "scala") - - private lazy val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } - lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") - lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "." - - override def toString = path.path -} - -object Fileish { - def apply(f: File): Fileish = new Fileish(f, () => f.inputStream()) - def apply(f: JarEntry, in: () => InputStream): Fileish = new Fileish(Path(f.getName), in) - def apply(path: String, in: () => InputStream): Fileish = new Fileish(Path(path), in) -} diff --git a/compiler/src/dotty/tools/io/Jar.scala b/compiler/src/dotty/tools/io/Jar.scala index 142226ea57a9..850a8f3dbca7 100644 --- a/compiler/src/dotty/tools/io/Jar.scala +++ b/compiler/src/dotty/tools/io/Jar.scala @@ -40,6 +40,8 @@ class Jar(file: File) extends Iterable[JarEntry] { protected def errorFn(msg: String): Unit = Console println msg + private implicit def enrichManifest(m: JManifest): Jar.WManifest = Jar.WManifest(m) + lazy val jarFile = new JarFile(file.jfile) lazy val manifest = withJarInput(s => Option(s.getManifest)) @@ -65,7 +67,6 @@ class Jar(file: File) extends Iterable[JarEntry] { Iterator continually in.getNextJarEntry() takeWhile (_ != null) foreach f } override def iterator: Iterator[JarEntry] = this.toList.iterator - def fileishIterator: Iterator[Fileish] = jarFile.entries.asScala map (x => Fileish(x, () => getEntryStream(x))) private def getEntryStream(entry: JarEntry) = jarFile getInputStream entry match { case null => errorFn("No such entry: " + entry) ; null diff --git a/compiler/src/dotty/tools/io/NoAbstractFile.scala b/compiler/src/dotty/tools/io/NoAbstractFile.scala new file mode 100644 index 000000000000..3b2bbeb58022 --- /dev/null +++ b/compiler/src/dotty/tools/io/NoAbstractFile.scala @@ -0,0 +1,33 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package dotty.tools.io + +import java.io.InputStream + +/** A distinguished object so you can avoid both null + * and Option. + * + * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' + */ +object NoAbstractFile extends AbstractFile { + def absolute: AbstractFile = this + def container: AbstractFile = this + def create(): Unit = ??? + def delete(): Unit = ??? + def file: java.io.File = null + def input: InputStream = null + def isDirectory: Boolean = false + override def isVirtual: Boolean = true + def iterator: Iterator[AbstractFile] = Iterator.empty + def lastModified: Long = 0L + def lookupName(name: String, directory: Boolean): AbstractFile = null + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = null + def name: String = "" + def output: java.io.OutputStream = null + def path: String = "" + override def toByteArray = Array[Byte]() + override def toString = "