summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLex Spoon <lex@lexspoon.org>2008-05-05 14:09:00 +0000
committerLex Spoon <lex@lexspoon.org>2008-05-05 14:09:00 +0000
commit06efde1f28ec6ad3f0008e3aaf08f292f0ae452f (patch)
treed1c81a7504a4ada89ff193282d638d3bd79a5c02
parent72615dc18e18dabd211a684a5b395daf3373dfed (diff)
downloadscala-06efde1f28ec6ad3f0008e3aaf08f292f0ae452f.tar.gz
scala-06efde1f28ec6ad3f0008e3aaf08f292f0ae452f.tar.bz2
scala-06efde1f28ec6ad3f0008e3aaf08f292f0ae452f.zip
The interpreter no longer generates class files...
The interpreter no longer generates class files to disk. It instead uses an in-memory virtual directory.
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala49
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala28
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala32
-rw-r--r--src/compiler/scala/tools/nsc/io/AbstractFile.scala37
-rw-r--r--src/compiler/scala/tools/nsc/io/PlainFile.scala3
-rw-r--r--src/compiler/scala/tools/nsc/io/VirtualDirectory.scala67
-rw-r--r--src/compiler/scala/tools/nsc/io/VirtualFile.scala23
-rw-r--r--src/compiler/scala/tools/nsc/io/ZipArchive.scala2
8 files changed, 200 insertions, 41 deletions
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index f0a3750aac..713b4c7b99 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -15,11 +15,12 @@ import scala.collection.mutable
import scala.collection.mutable.{ListBuffer, HashSet, ArrayBuffer}
//import ast.parser.SyntaxAnalyzer
-import io.PlainFile
+import io.{PlainFile, VirtualDirectory}
import reporters.{ConsoleReporter, Reporter}
import symtab.Flags
import util.{SourceFile,BatchSourceFile,ClassPath,NameTransformer}
import nsc.{InterpreterResults=>IR}
+import scala.tools.nsc.interpreter._
/** <p>
* An interpreter for Scala code.
@@ -68,6 +69,9 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
this.settings.target.value = "jvm-1.4"
}
+ /** directory to save .class files to */
+ val virtualDirectory = new VirtualDirectory("(memory)", None)
+
/** the compiler to compile expressions with */
val compiler: scala.tools.nsc.Global = newCompiler(settings, reporter)
@@ -109,14 +113,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** interpreter settings */
val isettings = new InterpreterSettings
- /** directory to save .class files to */
- val classfilePath = File.createTempFile("scalaint", "")
- classfilePath.delete // the file is created as a file; make it a directory
- classfilePath.mkdirs
-
- /* set up the compiler's output directory */
- settings.outdir.value = classfilePath.getPath
-
object reporter extends ConsoleReporter(settings, null, out) {
//override def printMessage(msg: String) { out.println(clean(msg)) }
override def printMessage(msg: String) { out.print(clean(msg) + "\n"); out.flush() }
@@ -124,8 +120,11 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** Instantiate a compiler. Subclasses can override this to
* change the compiler class used by this interpreter. */
- protected def newCompiler(settings: Settings, reporter: Reporter) =
- new scala.tools.nsc.Global(settings, reporter)
+ protected def newCompiler(settings: Settings, reporter: Reporter) = {
+ val comp = new scala.tools.nsc.Global(settings, reporter)
+ comp.genJVM.outputDir = virtualDirectory
+ comp
+ }
/** the compiler's classpath, as URL's */
@@ -133,7 +132,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
ClassPath.expandPath(compiler.settings.classpath.value).
map(s => new File(s).toURL)
- /** class loader used to load compiled code */
/* A single class loader is used for all commands interpreted by this Interpreter.
It would also be possible to create a new class loader for each command
to interpret. The advantages of the current approach are:
@@ -147,12 +145,16 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
shadow the old ones, and old code objects refer to the old
definitions.
*/
- private val classLoader =
- if (parentClassLoader eq null)
- new URLClassLoader((classfilePath.toURL :: compilerClasspath).toArray)
- else
- new URLClassLoader((classfilePath.toURL :: compilerClasspath).toArray,
- parentClassLoader)
+ /** class loader used to load compiled code */
+ private val classLoader = {
+ val parent =
+ if (parentClassLoader == null)
+ new URLClassLoader(compilerClasspath.toArray)
+ else
+ new URLClassLoader(compilerClasspath.toArray,
+ parentClassLoader)
+ new AbstractFileClassLoader(virtualDirectory, parent)
+ }
/** Set the current Java "context" class loader to this
* interpreter's class loader */
@@ -548,19 +550,10 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** <p>
* This instance is no longer needed, so release any resources
- * it is using.
- * </p>
- * <p>
- * Specifically, this deletes the temporary directory used for holding
- * class files for this instance. This cannot safely be done after
- * each command is executed because of Java's demand loading.
- * </p>
- * <p>
- * Also, this flushes the reporter's output.
+ * it is using. The reporter's output gets flushed.
* </p>
*/
def close() {
- Interpreter.deleteRecursively(classfilePath)
reporter.flush()
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 0813bd88ce..280246a6bd 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -7,11 +7,12 @@
package scala.tools.nsc.backend.jvm
-import java.io.File
+import java.io.{DataOutputStream, File, OutputStream}
import java.nio.ByteBuffer
import scala.collection.immutable.{Set, ListSet}
import scala.collection.mutable.{Map, HashMap, HashSet}
+import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.symtab._
import scala.tools.nsc.util.{Position, NoPosition}
@@ -29,6 +30,12 @@ abstract class GenJVM extends SubComponent {
val phaseName = "jvm"
+ /**
+ * Directory where output will be written. By default it
+ * is the directory specified by Settings.outdir.
+ */
+ var outputDir: AbstractFile = AbstractFile.getDirectory(settings.outdir.value)
+
/** Create a new phase */
override def newPhase(p: Phase) = new JvmPhase(p)
@@ -124,9 +131,9 @@ abstract class GenJVM extends SubComponent {
addScalaAttr(if (isTopLevelModule(sym)) sym.sourceModule else sym);
addInnerClasses
- val outfile = getFile(jclass, ".class")
+ val outfile = new DataOutputStream(getFile(jclass, ".class"))
jclass.writeTo(outfile)
- val file = scala.tools.nsc.io.AbstractFile.getFile(outfile)
+ outfile.close()
informProgress("wrote " + outfile)
}
@@ -273,9 +280,9 @@ abstract class GenJVM extends SubComponent {
jcode.emitRETURN()
// write the bean information class file.
- val outfile = getFile(beanInfoClass, ".class")
+ val outfile = new DataOutputStream(getFile(beanInfoClass, ".class"))
beanInfoClass.writeTo(outfile)
- val file = scala.tools.nsc.io.AbstractFile.getFile(outfile)
+ outfile.close()
informProgress("wrote BeanInfo " + outfile)
}
@@ -1513,9 +1520,14 @@ abstract class GenJVM extends SubComponent {
res
}
- def getFile(cls: JClass, suffix: String): String = {
- val path = cls.getName().replace('.', File.separatorChar)
- settings.outdir.value + File.separatorChar + path + suffix
+ def getFile(cls: JClass, suffix: String): OutputStream = {
+ var dir: AbstractFile = outputDir
+ val pathParts = cls.getName().split("[./]").toList
+ for (part <- pathParts.init) {
+ dir = dir.subdirectoryNamed(part)
+ }
+ val file = dir.fileNamed(pathParts.last + suffix)
+ file.output
}
/** Emit a Local variable table for debugging purposes.
diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
new file mode 100644
index 0000000000..3aaead472a
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
@@ -0,0 +1,32 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2007 LAMP/EPFL
+ */
+// $Id$
+package scala.tools.nsc.interpreter
+import scala.tools.nsc.io.AbstractFile
+
+/**
+ * A class loader that loads files from a {@link scala.tools.nsc.io.AbstractFile}.
+ *
+ * @author Lex Spoon
+ */
+class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader)
+extends ClassLoader(parent)
+{
+ override def findClass(name: String): Class[_] = {
+ var file: AbstractFile = root
+ val pathParts = name.split("[./]").toList
+ for (dirPart <- pathParts.init) {
+ file = file.lookupName(dirPart, true)
+ if (file == null) {
+ throw new ClassNotFoundException(name)
+ }
+ }
+ file = file.lookupName(pathParts.last+".class", false)
+ if (file == null) {
+ throw new ClassNotFoundException(name)
+ }
+ val bytes = file.toByteArray
+ defineClass(name, bytes, 0, bytes.length)
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala
index 36c17ef6c9..26782dfeb9 100644
--- a/src/compiler/scala/tools/nsc/io/AbstractFile.scala
+++ b/src/compiler/scala/tools/nsc/io/AbstractFile.scala
@@ -7,7 +7,7 @@
package scala.tools.nsc.io
-import java.io.{File, IOException, InputStream}
+import java.io.{File, FileOutputStream, IOException, InputStream, OutputStream}
import java.net.URL
import scala.collection.mutable.ArrayBuffer
@@ -117,6 +117,9 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] {
/** returns an input stream so the file can be read */
def input: InputStream
+ /** Returns an output stream for writing the file */
+ def output: OutputStream
+
/** size of this file if it is a concrete file. */
def size: Option[Int] = None
@@ -187,6 +190,38 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] {
file
}
+ /**
+ * 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)
+ val existing = lookupName(name, false)
+ if (existing == null) {
+ val newFile = new File(file, name)
+ newFile.createNewFile()
+ new PlainFile(newFile)
+ } else {
+ existing
+ }
+ }
+
+ /**
+ * Get the subdirectory with a given name, creating it if it
+ * does not already exist.
+ */
+ def subdirectoryNamed(name: String): AbstractFile = {
+ assert (isDirectory)
+ val existing = lookupName(name, true)
+ if (existing == null) {
+ val dir = new File(file, name)
+ dir.mkdir()
+ new PlainFile(dir)
+ } else {
+ existing
+ }
+ }
+
/** Returns the path of this abstract file. */
override def toString() = path
diff --git a/src/compiler/scala/tools/nsc/io/PlainFile.scala b/src/compiler/scala/tools/nsc/io/PlainFile.scala
index a489dd1694..924822401e 100644
--- a/src/compiler/scala/tools/nsc/io/PlainFile.scala
+++ b/src/compiler/scala/tools/nsc/io/PlainFile.scala
@@ -7,7 +7,7 @@
package scala.tools.nsc.io
-import java.io.{File, FileInputStream, IOException}
+import java.io.{File, FileInputStream, FileOutputStream, IOException}
object PlainFile {
/**
@@ -37,6 +37,7 @@ class PlainFile(val file: File) extends AbstractFile {
override def container : AbstractFile = new PlainFile(file.getParentFile)
override def input = new FileInputStream(file)
+ override def output = new FileOutputStream(file)
override def size = Some(file.length.toInt)
diff --git a/src/compiler/scala/tools/nsc/io/VirtualDirectory.scala b/src/compiler/scala/tools/nsc/io/VirtualDirectory.scala
new file mode 100644
index 0000000000..c4ffa4892b
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/io/VirtualDirectory.scala
@@ -0,0 +1,67 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2007 LAMP/EPFL
+ */
+// $Id$
+package scala.tools.nsc.io
+import scala.collection.{mutable=>mut}
+
+/**
+ * An in-memory directory.
+ *
+ * @author Lex Spoon
+ */
+class VirtualDirectory(val name: String, maybeContainer: Option[VirtualDirectory])
+extends AbstractFile {
+ def path: String =
+ maybeContainer match {
+ case None => name
+ case Some(parent) => parent.path+'/'+ name
+ }
+ def container = maybeContainer.get
+ def isDirectory = true
+ var lastModified: Long = System.currentTimeMillis
+ private def updateLastModified {
+ lastModified = System.currentTimeMillis
+ }
+ override def file = null
+ override def input = error("directories cannot be read")
+ override def output = error("directories cannot be written")
+
+ private val files = mut.Map.empty[String, AbstractFile]
+
+ // the toList is so that the directory may continue to be
+ // modified while its elements are iterated
+ def elements = files.values.toList.elements
+
+ override def lookupName(name: String, directory: Boolean): AbstractFile = {
+ files.get(name) match {
+ case None => null
+ case Some(file) =>
+ if (file.isDirectory == directory)
+ file
+ else
+ null
+ }
+ }
+
+ override def fileNamed(name: String): AbstractFile = {
+ val existing = lookupName(name, false)
+ if (existing == null) {
+ val newFile = new VirtualFile(name, path+'/'+name)
+ files(name) = newFile
+ newFile
+ } else {
+ existing
+ }
+ }
+
+ override def subdirectoryNamed(name: String): AbstractFile = {
+ val existing = lookupName(name, true)
+ if (existing == null) {
+ val dir = new VirtualDirectory(name, Some(this))
+ dir
+ } else {
+ existing
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/io/VirtualFile.scala b/src/compiler/scala/tools/nsc/io/VirtualFile.scala
index ec1e4396c7..f8fcb0eba1 100644
--- a/src/compiler/scala/tools/nsc/io/VirtualFile.scala
+++ b/src/compiler/scala/tools/nsc/io/VirtualFile.scala
@@ -7,9 +7,10 @@
package scala.tools.nsc.io
-import java.io.{File, InputStream}
+import java.io.{ByteArrayInputStream, ByteArrayOutputStream,
+ File, InputStream, OutputStream}
-/** This class implements an empty abstract regular file.
+/** This class implements an in-memory file.
*
* @author Philippe Altherr
* @version 1.0, 23/03/2004
@@ -36,6 +37,11 @@ class VirtualFile(val name: String, _path: String) extends AbstractFile {
case _ => false
}
+
+ //########################################################################
+ // Private data
+ private var content = new Array[Byte](0)
+
//########################################################################
// Public Methods
@@ -44,7 +50,18 @@ class VirtualFile(val name: String, _path: String) extends AbstractFile {
/** Returns null. */
final def file: File = null
- def input : InputStream = throw new Error("not supported");
+ override def size: Option[Int] = Some(content.size)
+
+ def input : InputStream = new ByteArrayInputStream(content);
+
+ override def output: OutputStream = {
+ new ByteArrayOutputStream() {
+ override def close() {
+ super.close()
+ content = toByteArray()
+ }
+ }
+ }
def container : AbstractFile = throw new Error("not supported")
diff --git a/src/compiler/scala/tools/nsc/io/ZipArchive.scala b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
index 3267163363..2c7eadc19e 100644
--- a/src/compiler/scala/tools/nsc/io/ZipArchive.scala
+++ b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
@@ -238,6 +238,8 @@ final class URLZipArchive(url: URL) extends AbstractFile {
def input: InputStream = url.openStream()
+ def output = throw new Error("unsupported")
+
override def elements: Iterator[AbstractFile] = {
if (root eq null) load()
root.elements