Skip to content

Commit

Permalink
Modularized the repl.
Browse files Browse the repository at this point in the history
Following in the footsteps of scaladoc and interactive.
The interpreter sources move into src/repl, and are given
a separate build target. As with the others, at present
they are still packaged into scala-compiler.jar.

A summary of changes:

 - repl requires use of ReplGlobal (this was already implied)
 - macro code's repl-specific classloader hack pulled into overridable
   method and overridden in ReplGlobal
 - removed -Ygen-javap option to eliminate backend's dependency on javap
 - removed -Yrepl-debug option (can still be enabled with -Dscala.repl.debug)
 - pushed javap code into src/repl so javax.tools dependency can bee
   weakened to the repl only
 - removed some "show pickled" related code which hasn't worked right
   in a while and isn't the right way to do it anymore anyway. Will
   return to fix showPickled and provide it with some tests.
  • Loading branch information
paulp committed Mar 12, 2013
1 parent 1b6297f commit 48cc8b4
Show file tree
Hide file tree
Showing 60 changed files with 823 additions and 811 deletions.
39 changes: 35 additions & 4 deletions build.xml
Expand Up @@ -1095,6 +1095,32 @@ QUICK BUILD (QUICK)
<stopwatch name="quick.comp.timer" action="total"/>
</target>

<target name="quick.pre-repl" depends="quick.comp">
<uptodate property="quick.repl.available" targetfile="${build-quick.dir}/repl.complete">
<srcfiles dir="${src.dir}/repl" />
</uptodate>
</target>

<target name="quick.repl" depends="quick.pre-repl" unless="quick.repl.available">
<mkdir dir="${build-quick.dir}/classes/repl"/>
<scalacfork
destdir="${build-quick.dir}/classes/repl"
compilerpathref="quick.classpath"
params="${scalac.args.quick}"
srcdir="${src.dir}/repl"
jvmargs="${scalacfork.jvmargs}">
<include name="**/*.scala"/>
<compilationpath>
<pathelement location="${build-quick.dir}/classes/library"/>
<pathelement location="${build-quick.dir}/classes/reflect"/>
<pathelement location="${build-quick.dir}/classes/compiler"/>
<pathelement location="${build-quick.dir}/classes/repl"/>
<pathelement location="${jline.jar}"/>
</compilationpath>
</scalacfork>
<touch file="${build-quick.dir}/repl.complete" verbose="no"/>
</target>

<target name="quick.swing" depends="quick.comp" if="has.java6" unless="quick.comp.available">
<scalacfork
destdir="${build-quick.dir}/classes/library"
Expand All @@ -1107,7 +1133,7 @@ QUICK BUILD (QUICK)
</scalacfork>
</target>

<target name="quick.pre-plugins" depends="quick.comp">
<target name="quick.pre-plugins" depends="quick.repl" unless="quick.repl.available">
<uptodate property="quick.plugins.available" targetfile="${build-quick.dir}/plugins.complete">
<srcfiles dir="${src.dir}/continuations"/>
</uptodate>
Expand Down Expand Up @@ -1229,6 +1255,7 @@ QUICK BUILD (QUICK)
<pathelement location="${build-quick.dir}/classes/library"/>
<pathelement location="${build-quick.dir}/classes/reflect"/>
<pathelement location="${build-quick.dir}/classes/compiler"/>
<pathelement location="${build-quick.dir}/classes/repl"/>
<pathelement location="${build-quick.dir}/classes/scalap"/>
<pathelement location="${build-quick.dir}/classes/partest"/>
<path refid="asm.classpath"/>
Expand All @@ -1247,6 +1274,7 @@ QUICK BUILD (QUICK)
<pathelement location="${build-quick.dir}/classes/library"/>
<pathelement location="${build-quick.dir}/classes/reflect"/>
<pathelement location="${build-quick.dir}/classes/compiler"/>
<pathelement location="${build-quick.dir}/classes/repl"/>
<pathelement location="${build-quick.dir}/classes/scalap"/>
<pathelement location="${build-quick.dir}/classes/partest"/>
<pathelement location="${ant.jar}"/>
Expand Down Expand Up @@ -1358,11 +1386,12 @@ QUICK BUILD (QUICK)
<pathelement location="${build-quick.dir}/classes/library"/>
<pathelement location="${build-quick.dir}/classes/reflect"/>
<pathelement location="${build-quick.dir}/classes/compiler"/>
<pathelement location="${build-quick.dir}/classes/repl"/>
<pathelement location="${build-quick.dir}/classes/scalap"/>
<pathelement location="${jline.jar}"/>
<path refid="asm.classpath"/>
<path refid="forkjoin.classpath"/>
<path refid="aux.libs"/>
<path refid="asm.classpath"/>
<pathelement location="${jline.jar}"/>
</path>
<taskdef name="quick-bin" classname="scala.tools.ant.ScalaTool" classpathref="quick.bin.classpath"/>
<mkdir dir="${build-quick.dir}/bin"/>
Expand Down Expand Up @@ -1488,6 +1517,7 @@ PACKED QUICK BUILD (PACK)
<fileset dir="${build-quick.dir}/classes/compiler"/>
<fileset dir="${build-quick.dir}/classes/scaladoc"/>
<fileset dir="${build-quick.dir}/classes/interactive"/>
<fileset dir="${build-quick.dir}/classes/repl"/>
<fileset dir="${build-asm.dir}/classes"/>
</jar>
<copy file="${jline.jar}" toDir="${build-pack.dir}/lib"/>
Expand Down Expand Up @@ -1998,10 +2028,11 @@ SBT Compiler Interface
jvmargs="${scalacfork.jvmargs}">
<include name="**/*.scala"/>
<compilationpath>
<pathelement location="${build-quick.dir}/classes/scaladoc"/>
<pathelement location="${build-quick.dir}/classes/library"/>
<pathelement location="${build-quick.dir}/classes/reflect"/>
<pathelement location="${build-quick.dir}/classes/compiler"/>
<pathelement location="${build-quick.dir}/classes/scaladoc"/>
<pathelement location="${build-quick.dir}/classes/repl"/>
<pathelement location="${sbt.interface.jar}"/>
<path refid="forkjoin.classpath"/>
</compilationpath>
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/Global.scala
Expand Up @@ -999,7 +999,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)

object typeDeconstruct extends {
val global: Global.this.type = Global.this
} with interpreter.StructuredTypeStrings
} with typechecker.StructuredTypeStrings

/** There are common error conditions where when the exception hits
* here, currentRun.currentUnit is null. This robs us of the knowledge
Expand Down
30 changes: 0 additions & 30 deletions src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
Expand Up @@ -9,7 +9,6 @@ package backend.jvm
import java.io.{ DataOutputStream, FileOutputStream, OutputStream, File => JFile }
import scala.tools.nsc.io._
import scala.tools.nsc.util.ScalaClassLoader
import scala.tools.util.{ Javap, JavapClass }
import java.util.jar.Attributes.Name
import scala.language.postfixOps

Expand Down Expand Up @@ -59,35 +58,6 @@ trait BytecodeWriters {
override def close() = writer.close()
}

/** To be mixed-in with the BytecodeWriter that generates
* the class file to be disassembled.
*/
trait JavapBytecodeWriter extends BytecodeWriter {
val baseDir = Directory(settings.Ygenjavap.value).createDirectory()
val cl = ScalaClassLoader.appLoader

def emitJavap(classFile: AbstractFile, javapFile: File) {
val pw = javapFile.printWriter()
try {
val javap = new JavapClass(cl, pw) {
override def findBytes(path: String): Array[Byte] = classFile.toByteArray
}
javap(Seq("-verbose", "-protected", classFile.name)) foreach (_.show())
} finally pw.close()
}
abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
super.writeClass(label, jclassName, jclassBytes, sym)

val classFile = getFile(sym, jclassName, ".class")
val segments = jclassName.split("[./]")
val javapFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "javap" toFile;
javapFile.parent.createDirectory()

if (Javap.isAvailable(cl)) emitJavap(classFile, javapFile)
else warning("No javap on classpath, skipping javap output.")
}
}

trait ClassBytecodeWriter extends BytecodeWriter {
def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) {
val outfile = getFile(sym, jclassName, ".class")
Expand Down
59 changes: 25 additions & 34 deletions src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
Expand Up @@ -72,19 +72,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
new DirectToJarfileWriter(f.file)

case _ =>
import scala.tools.util.Javap
if (settings.Ygenjavap.isDefault) {
if(settings.Ydumpclasses.isDefault)
new ClassBytecodeWriter { }
else
new ClassBytecodeWriter with DumpBytecodeWriter { }
}
else if (Javap.isAvailable()) new ClassBytecodeWriter with JavapBytecodeWriter { }
else {
warning("No javap on classpath, skipping javap output.")
if (settings.Ydumpclasses.isDefault)
new ClassBytecodeWriter { }
}

else
new ClassBytecodeWriter with DumpBytecodeWriter { }
// TODO A ScalapBytecodeWriter could take asm.util.Textifier as starting point.
// Three areas where javap ouput is less than ideal (e.g. when comparing versions of the same classfile) are:
// (a) unreadable pickle;
Expand Down Expand Up @@ -2519,7 +2510,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
if (nextBlock != whereto)
jcode goTo labels(whereto)
// SI-6102: Determine whether eliding this JUMP results in an empty range being covered by some EH.
// If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range"
// If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range"
else if (newNormal.isJumpOnly(b) && m.exh.exists(eh => eh.covers(b))) {
debugwarn("Had a jump only block that wasn't collapsed")
emit(asm.Opcodes.NOP)
Expand Down Expand Up @@ -3084,7 +3075,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
assert(nonICode.hasNext, "empty block")
nonICode.next.isInstanceOf[JUMP]
}

/**
* Returns the list of instructions in a block that follow all ICode only instructions,
* where an ICode only instruction is one that won't make it to the JVM
Expand All @@ -3101,7 +3092,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
* Returns the target of a block that is "jump only" which is defined
* as being a block that consists only of 0 or more instructions that
* won't make it to the JVM followed by a JUMP.
*
*
* @param b The basic block to examine
* @return Some(target) if b is a "jump only" block or None if it's not
*/
Expand Down Expand Up @@ -3150,12 +3141,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {

def rephraseGotos(detour: mutable.Map[BasicBlock, BasicBlock]) {
def lookup(b: BasicBlock) = detour.getOrElse(b, b)

m.code.startBlock = lookup(m.code.startBlock)

for(eh <- m.exh)
eh.setStartBlock(lookup(eh.startBlock))

for (b <- m.blocks) {
def replaceLastInstruction(i: Instruction) = {
if (b.lastInstruction != i) {
Expand All @@ -3164,18 +3155,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
b.replaceInstruction(idxLast, i)
}
}

b.lastInstruction match {
case JUMP(whereto) =>
replaceLastInstruction(JUMP(lookup(whereto)))
case CJUMP(succ, fail, cond, kind) =>
replaceLastInstruction(CJUMP(lookup(succ), lookup(fail), cond, kind))
case CZJUMP(succ, fail, cond, kind) =>
case CZJUMP(succ, fail, cond, kind) =>
replaceLastInstruction(CZJUMP(lookup(succ), lookup(fail), cond, kind))
case SWITCH(tags, labels) =>
val newLabels = (labels map lookup)
replaceLastInstruction(SWITCH(tags, newLabels))
case _ => ()
case _ => ()
}
}
}
Expand Down Expand Up @@ -3203,7 +3194,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// blocks
for (key <- detour.keySet) {
// we use the Robert Floyd's classic Tortoise and Hare algorithm
@tailrec
@tailrec
def findDestination(tortoise: BasicBlock, hare: BasicBlock): BasicBlock = {
if (tortoise == hare)
// cycle detected, map key to key
Expand All @@ -3227,41 +3218,41 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
detour
}

val detour = computeDetour
rephraseGotos(detour)

if (settings.debug.value) {
val (remappings, cycles) = detour partition {case (source, target) => source != target}
for ((source, target) <- remappings) {
debuglog(s"Will elide jump only block $source because it can be jumped around to get to $target.")
if (m.startBlock == source) debugwarn("startBlock should have been re-wired by now")
if (m.startBlock == source) debugwarn("startBlock should have been re-wired by now")
}
val sources = remappings.keySet
val targets = remappings.values.toSet
val intersection = sources intersect targets

if (intersection.nonEmpty) debugwarn(s"contradiction: we seem to have some source and target overlap in blocks ${intersection.mkString}. Map was ${detour.mkString}")

for ((source, _) <- cycles) {
debuglog(s"Block $source is in a do-nothing infinite loop. Did the user write 'while(true){}'?")
}
}
}

/**
* Removes all blocks that are unreachable in a method using a standard reachability analysis.
*/
def elimUnreachableBlocks(m: IMethod) {
assert(m.hasCode, "code-less method")
assert(m.hasCode, "code-less method")

// assume nothing is reachable until we prove it can be reached
val reachable = mutable.Set[BasicBlock]()

// the set of blocks that we know are reachable but have
// yet to be marked reachable, initially only the start block
val worklist = mutable.Set(m.startBlock)

while (worklist.nonEmpty) {
val block = worklist.head
worklist remove block
Expand All @@ -3271,7 +3262,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// think are unreachable
worklist ++= (block.successors filterNot reachable)
}

// exception handlers need to be told not to cover unreachable blocks
// and exception handlers that no longer cover any blocks need to be
// removed entirely
Expand All @@ -3282,9 +3273,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
unusedExceptionHandlers += exh
}
}

// remove the unusued exception handler references
if (settings.debug.value)
if (settings.debug.value)
for (exh <- unusedExceptionHandlers) debuglog(s"eliding exception handler $exh because it does not cover any reachable blocks")
m.exh = m.exh filterNot unusedExceptionHandlers

Expand Down
1 change: 0 additions & 1 deletion src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Expand Up @@ -188,7 +188,6 @@ trait ScalaSettings extends AbsScalaSettings
val Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.")
val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.")
val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.")
val Yrepldebug = BooleanSetting("-Yrepl-debug", "Trace all repl activity.") andThen (interpreter.replProps.debug setValue _)
val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.")
val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.")

Expand Down
23 changes: 8 additions & 15 deletions src/compiler/scala/tools/nsc/typechecker/Macros.scala
Expand Up @@ -43,8 +43,15 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
import definitions._
import treeInfo.{isRepeatedParamType => _, _}
import MacrosStats._

def globalSettings = global.settings

protected def findMacroClassLoader(): ClassLoader = {
val classpath = global.classPath.asURLs
macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath))
ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
}

/** `MacroImplBinding` and its companion module are responsible for
* serialization/deserialization of macro def -> impl bindings.
*
Expand Down Expand Up @@ -474,21 +481,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
* Loads classes from from -cp (aka the library classpath).
* Is also capable of detecting REPL and reusing its classloader.
*/
lazy val macroClassloader: ClassLoader = {
val classpath = global.classPath.asURLs
macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath))
val loader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)

// a heuristic to detect the REPL
if (global.settings.exposeEmptyPackage.value) {
macroLogVerbose("macro classloader: initializing from a REPL classloader: %s".format(global.classPath.asURLs))
import scala.tools.nsc.interpreter._
val virtualDirectory = global.settings.outputDirs.getSingleOutput.get
new AbstractFileClassLoader(virtualDirectory, loader) {}
} else {
loader
}
}
lazy val macroClassloader: ClassLoader = findMacroClassLoader()

/** Produces a function that can be used to invoke macro implementation for a given macro definition:
* 1) Looks up macro implementation symbol in this universe.
Expand Down

0 comments on commit 48cc8b4

Please sign in to comment.