diff options
-rw-r--r-- | build.xml | 39 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala | 30 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala | 59 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Macros.scala | 23 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala (renamed from src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala) | 18 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/AbstractFileClassLoader.scala (renamed from src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala) | 5 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/util/ShowPickled.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/reflect/StdTags.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/reflect/ToolBoxFactory.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/util/Javap.scala | 694 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/Interpreter.scala (renamed from src/compiler/scala/tools/nsc/Interpreter.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/InterpreterLoop.scala (renamed from src/compiler/scala/tools/nsc/InterpreterLoop.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/MainGenericRunner.scala (renamed from src/compiler/scala/tools/nsc/MainGenericRunner.scala) | 3 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala | 7 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala (renamed from src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ByteCode.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ByteCode.scala) | 10 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/CommandLine.scala (renamed from src/compiler/scala/tools/nsc/interpreter/CommandLine.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Completion.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Completion.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/CompletionAware.scala (renamed from src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/CompletionOutput.scala (renamed from src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Delimited.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Delimited.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ExprTyper.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Formatting.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Formatting.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ILoop.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ILoop.scala) | 3 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/IMain.scala (renamed from src/compiler/scala/tools/nsc/interpreter/IMain.scala) | 13 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ISettings.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ISettings.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Imports.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Imports.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala (renamed from src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala (renamed from src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/JLineReader.scala (renamed from src/compiler/scala/tools/nsc/interpreter/JLineReader.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/JavapClass.scala | 693 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Logger.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Logger.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/LoopCommands.scala (renamed from src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala (renamed from src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/NamedParam.scala (renamed from src/compiler/scala/tools/nsc/interpreter/NamedParam.scala) | 1 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Naming.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Naming.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Parsed.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Parsed.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Pasted.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Pasted.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Phased.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Phased.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Power.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Power.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ReplConfig.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ReplDir.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ReplDir.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala) | 8 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ReplProps.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ReplProps.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ReplReporter.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ReplStrings.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/ReplVals.scala (renamed from src/compiler/scala/tools/nsc/interpreter/ReplVals.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/Results.scala (renamed from src/compiler/scala/tools/nsc/interpreter/Results.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/RichClass.scala (renamed from src/compiler/scala/tools/nsc/interpreter/RichClass.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/SimpleReader.scala (renamed from src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/StdReplTags.scala | 15 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/package.scala (renamed from src/compiler/scala/tools/nsc/interpreter/package.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/session/FileBackedHistory.scala (renamed from src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/session/History.scala (renamed from src/compiler/scala/tools/nsc/interpreter/session/History.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/session/JLineHistory.scala (renamed from src/compiler/scala/tools/nsc/interpreter/session/JLineHistory.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala (renamed from src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala) | 0 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/session/package.scala (renamed from src/compiler/scala/tools/nsc/interpreter/session/package.scala) | 0 |
60 files changed, 823 insertions, 811 deletions
@@ -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" @@ -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> @@ -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"/> @@ -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}"/> @@ -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"/> @@ -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"/> @@ -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> diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index c0f611daa7..7ee3ee551f 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -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 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index 941ccd9a2d..c1cd3204e0 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -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 @@ -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") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 388efb4625..4a3d1805d9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -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; @@ -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) @@ -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 @@ -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 */ @@ -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) { @@ -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 _ => () } } } @@ -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 @@ -3227,7 +3218,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } detour } - + val detour = computeDetour rephraseGotos(detour) @@ -3235,33 +3226,33 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { 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 @@ -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 @@ -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 diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 2aee9bd4bc..9469113238 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -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.") diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 1693bdbc8c..91ebd798e1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -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. * @@ -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. diff --git a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala index 3ecd3b9ae4..65a3fedbd2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala @@ -4,7 +4,7 @@ */ package scala.tools.nsc -package interpreter +package typechecker import java.lang.{ reflect => r } import r.TypeVariable @@ -12,7 +12,6 @@ import scala.reflect.NameTransformer import NameTransformer._ import scala.reflect.runtime.{universe => ru} import scala.reflect.{ClassTag, classTag} -import typechecker.DestructureTypes /** A more principled system for turning types into strings. */ @@ -53,8 +52,7 @@ trait StructuredTypeStrings extends DestructureTypes { private def shortClass(x: Any) = { if (settings.debug.value) { val name = (x.getClass.getName split '.').last - val isAnon = name.reverse takeWhile (_ != '$') forall (_.isDigit) - val str = if (isAnon) name else (name split '$').last + val str = if (TypeStrings.isAnonClass(x.getClass)) name else (name split '$').last " // " + str } @@ -152,11 +150,11 @@ trait StructuredTypeStrings extends DestructureTypes { * "definition" is when you want strings like */ trait TypeStrings { + private type JClass = java.lang.Class[_] private val ObjectClass = classOf[java.lang.Object] private val primitives = Set[String]("byte", "char", "short", "int", "long", "float", "double", "boolean", "void") private val primitiveMap = primitives.toList map { x => val key = x match { - case "void" => "Void" case "int" => "Integer" case "char" => "Character" case s => s.capitalize @@ -169,6 +167,11 @@ trait TypeStrings { ("java.lang." + key) -> ("scala." + value) } toMap + def isAnonClass(cl: Class[_]) = { + val xs = cl.getName.reverse takeWhile (_ != '$') + xs.nonEmpty && xs.forall(_.isDigit) + } + def scalaName(s: String): String = { if (s endsWith MODULE_SUFFIX_STRING) s.init + ".type" else if (s == "void") "scala.Unit" @@ -178,17 +181,16 @@ trait TypeStrings { // Trying to put humpty dumpty back together again. def scalaName(clazz: JClass): String = { val name = clazz.getName - val isAnon = clazz.isScalaAnonymous val enclClass = clazz.getEnclosingClass def enclPre = enclClass.getName + MODULE_SUFFIX_STRING def enclMatch = name startsWith enclPre scalaName( - if (enclClass == null || isAnon || !enclMatch) name + if (enclClass == null || isAnonClass(clazz) || !enclMatch) name else enclClass.getName + "." + (name stripPrefix enclPre) ) } - def anyClass(x: Any): JClass = if (x == null) null else x.getClass + def anyClass(x: Any): JClass = if (x == null) null else x.getClass private def brackets(tps: String*): String = if (tps.isEmpty) "" diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/util/AbstractFileClassLoader.scala index e909cd945d..7aef87f387 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/util/AbstractFileClassLoader.scala @@ -3,10 +3,9 @@ */ package scala.tools.nsc -package interpreter +package util import scala.tools.nsc.io.AbstractFile -import util.ScalaClassLoader import java.net.{ URL, URLConnection, URLStreamHandler } import scala.collection.{ mutable, immutable } @@ -78,7 +77,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) case null => super.classBytes(name) case file => file.toByteArray } - override def findClass(name: String): JClass = { + override def findClass(name: String): Class[_] = { val bytes = classBytes(name) if (bytes.length == 0) throw new ClassNotFoundException(name) diff --git a/src/compiler/scala/tools/nsc/util/ShowPickled.scala b/src/compiler/scala/tools/nsc/util/ShowPickled.scala index 4bc393bd0b..f91e94471a 100644 --- a/src/compiler/scala/tools/nsc/util/ShowPickled.scala +++ b/src/compiler/scala/tools/nsc/util/ShowPickled.scala @@ -13,7 +13,6 @@ import java.lang.Float.intBitsToFloat import java.lang.Double.longBitsToDouble import scala.reflect.internal.{Flags, Names} import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } -import interpreter.ByteCode.scalaSigBytesForPath object ShowPickled extends Names { import PickleFormat._ @@ -272,7 +271,6 @@ object ShowPickled extends Names { } def fromFile(path: String) = fromBytes(io.File(path).toByteArray()) - def fromName(name: String) = fromBytes(scalaSigBytesForPath(name) getOrElse Array()) def fromBytes(data: => Array[Byte]): Option[PickleBuffer] = try Some(new PickleBuffer(data, 0, data.length)) catch { case _: Exception => None } @@ -287,7 +285,7 @@ object ShowPickled extends Names { def main(args: Array[String]) { args foreach { arg => - (fromFile(arg) orElse fromName(arg)) match { + fromFile(arg) match { case Some(pb) => show(arg + ":", pb) case _ => Console.println("Cannot read " + arg) } diff --git a/src/compiler/scala/tools/reflect/StdTags.scala b/src/compiler/scala/tools/reflect/StdTags.scala index 5c62819f04..6c1821f8aa 100644 --- a/src/compiler/scala/tools/reflect/StdTags.scala +++ b/src/compiler/scala/tools/reflect/StdTags.scala @@ -23,7 +23,7 @@ trait StdTags { } }) - private def tagOfStaticClass[T: ClassTag]: u.TypeTag[T] = + protected def tagOfStaticClass[T: ClassTag]: u.TypeTag[T] = u.TypeTag[T]( m, new TypeCreator { @@ -34,8 +34,6 @@ trait StdTags { lazy val tagOfString = tagOfStaticClass[String] lazy val tagOfFile = tagOfStaticClass[scala.tools.nsc.io.File] lazy val tagOfDirectory = tagOfStaticClass[scala.tools.nsc.io.Directory] - lazy val tagOfStdReplVals = tagOfStaticClass[scala.tools.nsc.interpreter.StdReplVals] - lazy val tagOfIMain = tagOfStaticClass[scala.tools.nsc.interpreter.IMain] lazy val tagOfThrowable = tagOfStaticClass[java.lang.Throwable] lazy val tagOfClassLoader = tagOfStaticClass[java.lang.ClassLoader] lazy val tagOfBigInt = tagOfStaticClass[BigInt] diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 3bde280681..e6bbe1dbed 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -5,7 +5,7 @@ import scala.tools.nsc.EXPRmode import scala.tools.nsc.reporters._ import scala.tools.nsc.CompilerCommand import scala.tools.nsc.io.VirtualDirectory -import scala.tools.nsc.interpreter.AbstractFileClassLoader +import scala.tools.nsc.util.AbstractFileClassLoader import scala.reflect.internal.Flags._ import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile} import java.lang.{Class => jClass} diff --git a/src/compiler/scala/tools/util/Javap.scala b/src/compiler/scala/tools/util/Javap.scala index 7e984fd1f7..3cfc1eb2a1 100644 --- a/src/compiler/scala/tools/util/Javap.scala +++ b/src/compiler/scala/tools/util/Javap.scala @@ -6,27 +6,14 @@ package scala.tools package util -import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } import scala.tools.nsc.util.ScalaClassLoader -import scala.tools.nsc.interpreter.IMain -import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, InputStream, - PrintWriter, Writer } -import java.util.{ Locale } -import java.util.regex.Pattern -import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, - ForwardingJavaFileManager, JavaFileManager, JavaFileObject, - SimpleJavaFileObject, StandardLocation } -import scala.reflect.io.{ AbstractFile, Directory, File, Path } -import java.io.{File => JFile} -import scala.io.Source -import scala.util.{ Try, Success, Failure } -import scala.util.Properties.lineSeparator -import scala.collection.JavaConverters -import scala.collection.generic.Clearable -import java.net.URL -import scala.language.reflectiveCalls +import java.io.PrintWriter -import Javap._ +trait JpResult { + def isError: Boolean + def value: Any + def show(): Unit +} trait Javap { def loader: ScalaClassLoader @@ -43,672 +30,3 @@ object NoJavap extends Javap { def tryFile(path: String): Option[Array[Byte]] = None def tryClass(path: String): Array[Byte] = Array() } - -class JavapClass( - val loader: ScalaClassLoader, - val printWriter: PrintWriter, - intp: Option[IMain] = None -) extends Javap { - import JavapTool.ToolArgs - import JavapClass._ - - lazy val tool = JavapTool() - - /** Run the tool. Option args start with "-". - * The default options are "-protected -verbose". - * Byte data for filename args is retrieved with findBytes. - */ - def apply(args: Seq[String]): List[JpResult] = { - val (options, claases) = args partition (s => (s startsWith "-") && s.length > 1) - val (flags, upgraded) = upgrade(options) - import flags.{ app, fun, help, raw } - val targets = if (fun && !help) FunFinder(loader, intp).funs(claases) else claases - if (help || claases.isEmpty) List(JpResult(JavapTool.helper(printWriter))) - else if (targets.isEmpty) List(JpResult("No anonfuns found.")) - else tool(raw, upgraded)(targets map (claas => claas -> bytesFor(claas, app))) - } - - /** Cull our tool options. */ - private def upgrade(options: Seq[String]): (ToolArgs, Seq[String]) = ToolArgs fromArgs options match { - case (t,s) if s.nonEmpty => (t,s) - case (t,s) => (t, JavapTool.DefaultOptions) - } - - /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). */ - private def bytesFor(path: String, app: Boolean) = Try { - def last = intp.get.mostRecentVar // fail if no intp - def req = if (path == "-") last else { - val s = path.splitHashMember - if (s._1.nonEmpty) s._1 - else s._2 getOrElse "#" - } - def asAppBody(s: String) = { - val (cls, fix) = s.splitSuffix - s"${cls}$$delayedInit$$body${fix}" - } - def todo = if (app) asAppBody(req) else req - val bytes = findBytes(todo) - if (bytes.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '${path}'") - else bytes - } - - def findBytes(path: String): Array[Byte] = tryFile(path) getOrElse tryClass(path) - - /** Assume the string is a path and try to find the classfile - * it represents. - */ - def tryFile(path: String): Option[Array[Byte]] = - (Try (File(path.asClassResource)) filter (_.exists) map (_.toByteArray())).toOption - - /** Assume the string is a fully qualified class name and try to - * find the class object it represents. - * There are other symbols of interest, too: - * - a definition that is wrapped in an enclosing class - * - a synthetic that is not in scope but its associated class is - */ - def tryClass(path: String): Array[Byte] = { - def load(name: String) = loader classBytes name - def loadable(name: String) = loader resourceable name - // if path has an interior dollar, take it as a synthetic - // if the prefix up to the dollar is a symbol in scope, - // result is the translated prefix + suffix - def desynthesize(s: String) = { - val i = s indexOf '$' - if (0 until s.length - 1 contains i) { - val name = s substring (0, i) - val sufx = s substring i - val tran = intp flatMap (_ translatePath name) - def loadableOrNone(strip: Boolean) = { - def suffix(strip: Boolean)(x: String) = - (if (strip && (x endsWith "$")) x.init else x) + sufx - val res = tran map (suffix(strip) _) - if (res.isDefined && loadable(res.get)) res else None - } - // try loading translated+suffix - val res = loadableOrNone(strip = false) - // some synthetics lack a dollar, (e.g., suffix = delayedInit$body) - // so as a hack, if prefix$$suffix fails, also try prefix$suffix - if (res.isDefined) res else loadableOrNone(strip = true) - } else None - } - val p = path.asClassName // scrub any suffix - // if repl, translate the name to something replish - // (for translate, would be nicer to get the sym and ask .isClass, - // instead of translatePath and then asking did I get a class back) - val q = if (intp.isEmpty) p else ( - // only simple names get the scope treatment - Some(p) filter (_ contains '.') - // take path as a Name in scope - orElse (intp flatMap (_ translatePath p) filter loadable) - // take path as a Name in scope and find its enclosing class - orElse (intp flatMap (_ translateEnclosingClass p) filter loadable) - // take path as a synthetic derived from some Name in scope - orElse desynthesize(p) - // just try it plain - getOrElse p - ) - load(q) - } - - /** Base class for javap tool adapters for java 6 and 7. */ - abstract class JavapTool { - type ByteAry = Array[Byte] - type Input = Pair[String, Try[ByteAry]] - - /** Run the tool. */ - def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] - - // Since the tool is loaded by reflection, check for catastrophic failure. - protected def failed: Boolean - implicit protected class Failer[A](a: =>A) { - def orFailed[B >: A](b: =>B) = if (failed) b else a - } - protected def noToolError = new JpError(s"No javap tool available: ${getClass.getName} failed to initialize.") - - // output filtering support - val writer = new CharArrayWriter - def written = { - writer.flush() - val w = writer.toString - writer.reset() - w - } - - /** Create a Showable with output massage. - * @param raw show ugly repl names - * @param target attempt to filter output to show region of interest - * @param preamble other messages to output - */ - def showWithPreamble(raw: Boolean, target: String, preamble: String = ""): Showable = new Showable { - // ReplStrippingWriter clips and scrubs on write(String) - // circumvent it by write(mw, 0, mw.length) or wrap it in withoutUnwrapping - def show() = - if (raw && intp.isDefined) intp.get withoutUnwrapping { writeLines() } - else writeLines() - private def writeLines() { - // take Foo# as Foo#apply for purposes of filtering. Useful for -fun Foo#; - // if apply is added here, it's for other than -fun: javap Foo#, perhaps m#? - val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } - var filtering = false // true if in region matching filter - // true to output - def checkFilter(line: String) = if (filterOn.isEmpty) true else { - // cheap heuristic, todo maybe parse for the java sig. - // method sigs end in paren semi - def isAnyMethod = line.endsWith(");") - def isOurMethod = { - val lparen = line.lastIndexOf('(') - val blank = line.lastIndexOf(' ', lparen) - (blank >= 0 && line.substring(blank+1, lparen) == filterOn.get) - } - filtering = if (filtering) { - // next blank line terminates section - // for -public, next line is next method, more or less - line.trim.nonEmpty && !isAnyMethod - } else { - isAnyMethod && isOurMethod - } - filtering - } - for (line <- Source.fromString(preamble + written).getLines(); if checkFilter(line)) - printWriter write line+lineSeparator - printWriter.flush() - } - } - } - - class JavapTool6 extends JavapTool { - import JavapTool._ - val EnvClass = loader.tryToInitializeClass[FakeEnvironment](Env).orNull - val PrinterClass = loader.tryToInitializeClass[FakePrinter](Printer).orNull - override protected def failed = (EnvClass eq null) || (PrinterClass eq null) - - val PrinterCtr = PrinterClass.getConstructor(classOf[InputStream], classOf[PrintWriter], EnvClass) orFailed null - val printWrapper = new PrintWriter(writer) - def newPrinter(in: InputStream, env: FakeEnvironment): FakePrinter = - PrinterCtr.newInstance(in, printWrapper, env) orFailed null - def showable(raw: Boolean, target: String, fp: FakePrinter): Showable = { - fp.asInstanceOf[{ def print(): Unit }].print() // run tool and flush to buffer - printWrapper.flush() // just in case - showWithPreamble(raw, target) - } - - lazy val parser = new JpOptions - def newEnv(opts: Seq[String]): FakeEnvironment = { - def result = { - val env: FakeEnvironment = EnvClass.newInstance() - parser(opts) foreach { case (name, value) => - val field = EnvClass getDeclaredField name - field setAccessible true - field.set(env, value.asInstanceOf[AnyRef]) - } - env - } - result orFailed null - } - - override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = - (inputs map { - case (claas, Success(ba)) => JpResult(showable(raw, claas, newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) - case (_, Failure(e)) => JpResult(e.toString) - }).toList orFailed List(noToolError) - } - - class JavapTool7 extends JavapTool { - - import JavapTool._ - type Task = { - def call(): Boolean // true = ok - //def run(args: Array[String]): Int // all args - //def handleOptions(args: Array[String]): Unit // options, then run() or call() - } - // result of Task.run - //object TaskResult extends Enumeration { - // val Ok, Error, CmdErr, SysErr, Abnormal = Value - //} - val TaskClaas = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull - override protected def failed = TaskClaas eq null - - val TaskCtor = TaskClaas.getConstructor( - classOf[Writer], - classOf[JavaFileManager], - classOf[DiagnosticListener[_]], - classOf[JIterable[String]], - classOf[JIterable[String]] - ) orFailed null - - class JavaReporter extends DiagnosticListener[JavaFileObject] with Clearable { - import scala.collection.mutable.{ ArrayBuffer, SynchronizedBuffer } - type D = Diagnostic[_ <: JavaFileObject] - val diagnostics = new ArrayBuffer[D] with SynchronizedBuffer[D] - override def report(d: Diagnostic[_ <: JavaFileObject]) { - diagnostics += d - } - override def clear() = diagnostics.clear() - /** All diagnostic messages. - * @param locale Locale for diagnostic messages, null by default. - */ - def messages(implicit locale: Locale = null) = (diagnostics map (_ getMessage locale)).toList - - def reportable(raw: Boolean): String = { - // don't filter this message if raw, since the names are likely to differ - val container = "Binary file .* contains .*".r - val m = if (raw) messages - else messages filter (_ match { case container() => false case _ => true }) - clear() - if (m.nonEmpty) m mkString ("", lineSeparator, lineSeparator) - else "" - } - } - val reporter = new JavaReporter - - // DisassemblerTool.getStandardFileManager(reporter,locale,charset) - val defaultFileManager: JavaFileManager = - (loader.tryToLoadClass[JavaFileManager]("com.sun.tools.javap.JavapFileManager").get getMethod ( - "create", - classOf[DiagnosticListener[_]], - classOf[PrintWriter] - ) invoke (null, reporter, new PrintWriter(System.err, true))).asInstanceOf[JavaFileManager] orFailed null - - // manages named arrays of bytes, which might have failed to load - class JavapFileManager(val managed: Seq[Input])(delegate: JavaFileManager = defaultFileManager) - extends ForwardingJavaFileManager[JavaFileManager](delegate) { - import JavaFileObject.Kind - import Kind._ - import StandardLocation._ - import JavaFileManager.Location - import java.net.URI - def uri(name: String): URI = new URI(name) // new URI("jfo:" + name) - - def inputNamed(name: String): Try[ByteAry] = (managed find (_._1 == name)).get._2 - def managedFile(name: String, kind: Kind) = kind match { - case CLASS => fileObjectForInput(name, inputNamed(name), kind) - case _ => null - } - // todo: just wrap it as scala abstractfile and adapt it uniformly - def fileObjectForInput(name: String, bytes: Try[ByteAry], kind: Kind): JavaFileObject = - new SimpleJavaFileObject(uri(name), kind) { - override def openInputStream(): InputStream = new ByteArrayInputStream(bytes.get) - // if non-null, ClassWriter wrongly requires scheme non-null - override def toUri: URI = null - override def getName: String = name - // suppress - override def getLastModified: Long = -1L - } - override def getJavaFileForInput(location: Location, className: String, kind: Kind): JavaFileObject = - location match { - case CLASS_PATH => managedFile(className, kind) - case _ => null - } - override def hasLocation(location: Location): Boolean = - location match { - case CLASS_PATH => true - case _ => false - } - } - def fileManager(inputs: Seq[Input]) = new JavapFileManager(inputs)() - - // show tool messages and tool output, with output massage - def showable(raw: Boolean, target: String): Showable = showWithPreamble(raw, target, reporter.reportable(raw)) - - // eventually, use the tool interface - def task(options: Seq[String], claases: Seq[String], inputs: Seq[Input]): Task = { - //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). - //getTask(writer, fileManager, reporter, options.asJava, claases.asJava) - import JavaConverters.asJavaIterableConverter - TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, claases.asJava) - .orFailed (throw new IllegalStateException) - } - // a result per input - private def applyOne(raw: Boolean, options: Seq[String], claas: String, inputs: Seq[Input]): Try[JpResult] = - Try { - task(options, Seq(claas), inputs).call() - } map { - case true => JpResult(showable(raw, claas)) - case _ => JpResult(reporter.reportable(raw)) - } recoverWith { - case e: java.lang.reflect.InvocationTargetException => e.getCause match { - case t: IllegalArgumentException => Success(JpResult(t.getMessage)) // bad option - case x => Failure(x) - } - } lastly { - reporter.clear() - } - override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { - case (claas, Success(_)) => applyOne(raw, options, claas, inputs).get - case (_, Failure(e)) => JpResult(e.toString) - }).toList orFailed List(noToolError) - } - - object JavapTool { - // >= 1.7 - val Tool = "com.sun.tools.javap.JavapTask" - - // < 1.7 - val Env = "sun.tools.javap.JavapEnvironment" - val Printer = "sun.tools.javap.JavapPrinter" - // "documentation" - type FakeEnvironment = AnyRef - type FakePrinter = AnyRef - - // support JavapEnvironment - class JpOptions { - private object Access { - final val PRIVATE = 0 - final val PROTECTED = 1 - final val PACKAGE = 2 - final val PUBLIC = 3 - } - private val envActionMap: Map[String, (String, Any)] = { - val map = Map( - "-l" -> (("showLineAndLocal", true)), - "-c" -> (("showDisassembled", true)), - "-s" -> (("showInternalSigs", true)), - "-verbose" -> (("showVerbose", true)), - "-private" -> (("showAccess", Access.PRIVATE)), - "-package" -> (("showAccess", Access.PACKAGE)), - "-protected" -> (("showAccess", Access.PROTECTED)), - "-public" -> (("showAccess", Access.PUBLIC)), - "-all" -> (("showallAttr", true)) - ) - map ++ List( - "-v" -> map("-verbose"), - "-p" -> map("-private") - ) - } - def apply(opts: Seq[String]): Seq[(String, Any)] = { - opts flatMap { opt => - envActionMap get opt match { - case Some(pair) => List(pair) - case _ => - val charOpts = opt.tail.toSeq map ("-" + _) - if (charOpts forall (envActionMap contains _)) - charOpts map envActionMap - else Nil - } - } - } - } - - case class ToolArgs(raw: Boolean = false, help: Boolean = false, app: Boolean = false, fun: Boolean = false) - - object ToolArgs { - def fromArgs(args: Seq[String]): (ToolArgs, Seq[String]) = ((ToolArgs(), Seq[String]()) /: (args flatMap massage)) { - case ((t,others), s) => s match { - case "-fun" => (t copy (fun=true), others) - case "-app" => (t copy (app=true), others) - case "-help" => (t copy (help=true), others) - case "-raw" => (t copy (raw=true), others) - case _ => (t, others :+ s) - } - } - } - - val helps = List( - "usage" -> ":javap [opts] [path or class or -]...", - "-help" -> "Prints this help message", - "-raw" -> "Don't unmangle REPL names", - "-app" -> "Show the DelayedInit body of Apps", - "-fun" -> "Show anonfuns for class or Class#method", - "-verbose/-v" -> "Stack size, number of locals, method args", - "-private/-p" -> "Private classes and members", - "-package" -> "Package-private classes and members", - "-protected" -> "Protected classes and members", - "-public" -> "Public classes and members", - "-l" -> "Line and local variable tables", - "-c" -> "Disassembled code", - "-s" -> "Internal type signatures", - "-sysinfo" -> "System info of class", - "-constants" -> "Static final constants" - ) - - // match prefixes and unpack opts, or -help on failure - def massage(arg: String): Seq[String] = { - require(arg startsWith "-") - // arg matches opt "-foo/-f" if prefix of -foo or exactly -f - val r = """(-[^/]*)(/(-.))?""".r - def maybe(opt: String, s: String): Option[String] = opt match { - // disambiguate by preferring short form - case r(lf,_,sf) if s == sf => Some(sf) - case r(lf,_,sf) if lf startsWith s => Some(lf) - case _ => None - } - def candidates(s: String) = (helps map (h => maybe(h._1, s))).flatten - // one candidate or one single-char candidate - def uniqueOf(maybes: Seq[String]) = { - def single(s: String) = s.length == 2 - if (maybes.length == 1) maybes - else if ((maybes count single) == 1) maybes filter single - else Nil - } - // each optchar must decode to exactly one option - def unpacked(s: String): Try[Seq[String]] = { - val ones = (s drop 1) map { c => - val maybes = uniqueOf(candidates(s"-$c")) - if (maybes.length == 1) Some(maybes.head) else None - } - Try(ones) filter (_ forall (_.isDefined)) map (_.flatten) - } - val res = uniqueOf(candidates(arg)) - if (res.nonEmpty) res - else (unpacked(arg) - getOrElse (Seq("-help"))) // or else someone needs help - } - - def helper(pw: PrintWriter) = new Showable { - def show() = helps foreach (p => pw write "%-12.12s%s%n".format(p._1,p._2)) - } - - val DefaultOptions = List("-protected", "-verbose") - - def isAvailable = Seq(Env, Tool) exists (cn => hasClass(loader, cn)) - - private def hasClass(cl: ScalaClassLoader, cn: String) = cl.tryToInitializeClass[AnyRef](cn).isDefined - - private def isTaskable(cl: ScalaClassLoader) = hasClass(cl, Tool) - - def apply() = if (isTaskable(loader)) new JavapTool7 else new JavapTool6 - } -} - -object JavapClass { - def apply( - loader: ScalaClassLoader = ScalaClassLoader.appLoader, - printWriter: PrintWriter = new PrintWriter(System.out, true), - intp: Option[IMain] = None - ) = new JavapClass(loader, printWriter, intp) - - // We enjoy flexibility in specifying either a fully-qualified class name com.acme.Widget - // or a resource path com/acme/Widget.class; but not widget.out - implicit class MaybeClassLike(val s: String) extends AnyVal { - /* private[this] final val suffix = ".class" */ - private def suffix = ".class" - def asClassName = (s stripSuffix suffix).replace('/', '.') - def asClassResource = if (s endsWith suffix) s else s.replace('.', '/') + suffix - def splitSuffix: (String, String) = if (s endsWith suffix) (s dropRight suffix.length, suffix) else (s, "") - def strippingSuffix(f: String => String): String = - if (s endsWith suffix) f(s dropRight suffix.length) else s - // e.g. Foo#bar. Foo# yields zero-length member part. - def splitHashMember: (String, Option[String]) = { - val i = s lastIndexOf '#' - if (i < 0) (s, None) - //else if (i >= s.length - 1) (s.init, None) - else (s take i, Some(s drop i+1)) - } - } - implicit class ClassLoaderOps(val cl: ClassLoader) extends AnyVal { - private def parentsOf(x: ClassLoader): List[ClassLoader] = if (x == null) Nil else x :: parentsOf(x.getParent) - def parents: List[ClassLoader] = parentsOf(cl) - /* all file locations */ - def locations = { - def alldirs = parents flatMap (_ match { - case ucl: ScalaClassLoader.URLClassLoader => ucl.classPathURLs - case jcl: java.net.URLClassLoader => jcl.getURLs - case _ => Nil - }) - val dirs = for (d <- alldirs; if d.getProtocol == "file") yield Path(new JFile(d.toURI)) - dirs - } - /* only the file location from which the given class is loaded */ - def locate(k: String): Option[Path] = { - Try { - val claas = try cl loadClass k catch { - case _: NoClassDefFoundError => null // let it snow - } - // cf ScalaClassLoader.originOfClass - claas.getProtectionDomain.getCodeSource.getLocation - } match { - case Success(null) => None - case Success(loc) if loc.isFile => Some(Path(new JFile(loc.toURI))) - case _ => None - } - } - /* would classBytes succeed with a nonempty array */ - def resourceable(className: String): Boolean = cl.getResource(className.asClassResource) != null - } - implicit class PathOps(val p: Path) extends AnyVal { - import scala.tools.nsc.io.Jar - def isJar = Jar isJarOrZip p - } - implicit class URLOps(val url: URL) extends AnyVal { - def isFile: Boolean = url.getProtocol == "file" - } - object FunFinder { - def apply(loader: ScalaClassLoader, intp: Option[IMain]) = new FunFinder(loader, intp) - } - class FunFinder(loader: ScalaClassLoader, intp: Option[IMain]) { - - // class k, candidate f without prefix - def isFunOfClass(k: String, f: String) = { - val p = (s"${Pattern quote k}\\$$+anonfun").r - (p findPrefixOf f).nonEmpty - } - // class k, candidate f without prefix, method m - def isFunOfMethod(k: String, m: String, f: String) = { - val p = (s"${Pattern quote k}\\$$+anonfun\\$$${Pattern quote m}\\$$").r - (p findPrefixOf f).nonEmpty - } - def isFunOfTarget(k: String, m: Option[String], f: String) = - if (m.isEmpty) isFunOfClass(k, f) - else isFunOfMethod(k, m.get, f) - def listFunsInAbsFile(k: String, m: Option[String], d: AbstractFile) = { - for (f <- d; if !f.isDirectory && isFunOfTarget(k, m, f.name)) yield f.name - } - // path prefix p, class k, dir d - def listFunsInDir(p: String, k: String, m: Option[String])(d: Directory) = { - val subdir = Path(p) - for (f <- (d / subdir).toDirectory.list; if f.isFile && isFunOfTarget(k, m, f.name)) - yield f.name - } - // path prefix p, class k, jar file f - def listFunsInJar(p: String, k: String, m: Option[String])(f: File) = { - import java.util.jar.JarEntry - import scala.tools.nsc.io.Jar - def maybe(e: JarEntry) = { - val (path, name) = { - val parts = e.getName split "/" - if (parts.length < 2) ("", e.getName) - else (parts.init mkString "/", parts.last) - } - if (path == p && isFunOfTarget(k, m, name)) Some(name) else None - } - (new Jar(f) map maybe).flatten - } - def loadable(name: String) = loader resourceable name - // translated class, optional member, opt member to filter on, whether it is repl output - def translate(s: String): (String, Option[String], Option[String], Boolean) = { - val (k0, m0) = s.splitHashMember - val k = k0.asClassName - val member = m0 filter (_.nonEmpty) // take Foo# as no member, not "" - val filter = m0 flatMap { case "" => Some("apply") case _ => None } // take Foo# as filter on apply - // class is either something replish or available to loader - // $line.$read$$etc$Foo#member - ((intp flatMap (_ translatePath k) filter (loadable) map ((_, member, filter, true))) - // s = "f" and $line.$read$$etc$#f is what we're after, - // ignoring any #member (except take # as filter on #apply) - orElse (intp flatMap (_ translateEnclosingClass k) map ((_, Some(k), filter, true))) - getOrElse (k, member, filter, false)) - } - /** Find the classnames of anonfuns associated with k, - * where k may be an available class or a symbol in scope. - */ - def funsOf(k0: String): Seq[String] = { - // class is either something replish or available to loader - val (k, member, filter, isReplish) = translate(k0) - val splat = k split "\\." - val name = splat.last - val prefix = if (splat.length > 1) splat.init mkString "/" else "" - val pkg = if (splat.length > 1) splat.init mkString "." else "" - // reconstitute an anonfun with a package - // if filtered, add the hash back, e.g. pkg.Foo#bar, pkg.Foo$anon$1#apply - def packaged(s: String) = { - val p = if (pkg.isEmpty) s else s"$pkg.$s" - val pm = filter map (p + "#" + _) - pm getOrElse p - } - // is this translated path in (usually virtual) repl outdir? or loadable from filesystem? - val fs = if (isReplish) { - def outed(d: AbstractFile, p: Seq[String]): Option[AbstractFile] = { - if (p.isEmpty) Option(d) - else Option(d.lookupName(p.head, directory = true)) flatMap (f => outed(f, p.tail)) - } - outed(intp.get.replOutput.dir, splat.init) map { d => - listFunsInAbsFile(name, member, d) map packaged - } - } else { - loader locate k map { w => - if (w.isDirectory) listFunsInDir(prefix, name, member)(w.toDirectory) map packaged - else if (w.isJar) listFunsInJar(prefix, name, member)(w.toFile) map packaged - else Nil - } - } - fs match { - case Some(xs) => xs.to[Seq] // maybe empty - case None => Seq() // nothing found, e.g., junk input - } - } - def funs(ks: Seq[String]) = ks flatMap funsOf _ - } -} - -object Javap { - - def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = JavapClass(cl).JavapTool.isAvailable - - def apply(path: String): Unit = apply(Seq(path)) - def apply(args: Seq[String]): Unit = JavapClass() apply args foreach (_.show()) - - trait Showable { - def show(): Unit - } - - sealed trait JpResult { - type ResultType - def isError: Boolean - def value: ResultType - def show(): Unit - // todo - // def header(): String - // def fields(): List[String] - // def methods(): List[String] - // def signatures(): List[String] - } - object JpResult { - def apply(msg: String) = new JpError(msg) - def apply(res: Showable) = new JpSuccess(res) - } - class JpError(msg: String) extends JpResult { - type ResultType = String - def isError = true - def value = msg - def show() = println(msg) // makes sense for :javap, less for -Ygen-javap - } - class JpSuccess(val value: Showable) extends JpResult { - type ResultType = AnyRef - def isError = false - def show() = value.show() // output to tool's PrintWriter - } - implicit class Lastly[A](val t: Try[A]) extends AnyVal { - private def effect[X](last: =>Unit)(a: X): Try[A] = { last; t } - def lastly(last: =>Unit): Try[A] = t transform (effect(last) _, effect(last) _) - } -} diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/repl/scala/tools/nsc/Interpreter.scala index 434f19f21b..434f19f21b 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/repl/scala/tools/nsc/Interpreter.scala diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/repl/scala/tools/nsc/InterpreterLoop.scala index a0be3f4fdb..a0be3f4fdb 100644 --- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala +++ b/src/repl/scala/tools/nsc/InterpreterLoop.scala diff --git a/src/compiler/scala/tools/nsc/MainGenericRunner.scala b/src/repl/scala/tools/nsc/MainGenericRunner.scala index adb03ca374..7195424cf9 100644 --- a/src/compiler/scala/tools/nsc/MainGenericRunner.scala +++ b/src/repl/scala/tools/nsc/MainGenericRunner.scala @@ -8,7 +8,6 @@ package scala.tools.nsc import io.{ File } import util.{ ClassPath, ScalaClassLoader } import Properties.{ versionString, copyrightString } -import interpreter.{ ILoop } import GenericRunnerCommand._ object JarRunner extends CommonRunner { @@ -78,7 +77,7 @@ class MainGenericRunner { Right(false) case _ => // We start the repl when no arguments are given. - Right(new ILoop process settings) + Right(new interpreter.ILoop process settings) } /** If -e and -i were both given, we want to execute the -e code after the diff --git a/src/repl/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/repl/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala new file mode 100644 index 0000000000..712219533d --- /dev/null +++ b/src/repl/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala @@ -0,0 +1,7 @@ +package scala.tools.nsc +package interpreter + +import scala.reflect.io.AbstractFile + +@deprecated("Use `scala.tools.nsc.util.AbstractFileClassLoader`", "2.11.0") +class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) extends util.AbstractFileClassLoader(root, parent) diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala b/src/repl/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala index e66e4eff29..e66e4eff29 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala +++ b/src/repl/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala b/src/repl/scala/tools/nsc/interpreter/ByteCode.scala index 48890a21c6..e1e3678837 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala +++ b/src/repl/scala/tools/nsc/interpreter/ByteCode.scala @@ -28,15 +28,5 @@ object ByteCode { method.invoke(module, _: String).asInstanceOf[Option[Map[String, String]]] } - /** Scala sig bytes. - */ - def scalaSigBytesForPath(path: String) = - for { - module <- DECODER - method <- decoderMethod("scalaSigAnnotationBytes", classOf[String]) - names <- method.invoke(module, path).asInstanceOf[Option[Array[Byte]]] - } - yield names - def aliasesForPackage(pkg: String) = aliasMap flatMap (_(pkg)) } diff --git a/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala b/src/repl/scala/tools/nsc/interpreter/CommandLine.scala index 0ab92ab769..0ab92ab769 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala +++ b/src/repl/scala/tools/nsc/interpreter/CommandLine.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/repl/scala/tools/nsc/interpreter/Completion.scala index 84a5cb49ae..84a5cb49ae 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala +++ b/src/repl/scala/tools/nsc/interpreter/Completion.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala b/src/repl/scala/tools/nsc/interpreter/CompletionAware.scala index 3dd5d93390..3dd5d93390 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala +++ b/src/repl/scala/tools/nsc/interpreter/CompletionAware.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala b/src/repl/scala/tools/nsc/interpreter/CompletionOutput.scala index d24ad60974..d24ad60974 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala +++ b/src/repl/scala/tools/nsc/interpreter/CompletionOutput.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala b/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala index 48af261937..48af261937 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala +++ b/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Delimited.scala b/src/repl/scala/tools/nsc/interpreter/Delimited.scala index e88a044931..e88a044931 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Delimited.scala +++ b/src/repl/scala/tools/nsc/interpreter/Delimited.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala index 9edd54b939..9edd54b939 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Formatting.scala b/src/repl/scala/tools/nsc/interpreter/Formatting.scala index 43e653edfd..43e653edfd 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Formatting.scala +++ b/src/repl/scala/tools/nsc/interpreter/Formatting.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 2ea255319d..599a061984 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -11,7 +11,6 @@ import java.io.{ BufferedReader, FileReader } import session._ import scala.annotation.tailrec import scala.util.Properties.{ jdkHome, javaVersion, versionString, javaVmName } -import scala.tools.util.{ Javap } import util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream } import io.{ File, Directory } import util.ScalaClassLoader @@ -19,7 +18,7 @@ import ScalaClassLoader._ import scala.tools.util._ import scala.language.{implicitConversions, existentials} import scala.reflect.classTag -import scala.tools.reflect.StdRuntimeTags._ +import StdReplTags._ import scala.concurrent.{ ExecutionContext, Await, Future, future } import ExecutionContext.Implicits._ diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index c54b01dbb0..cb2e3c32b8 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -15,6 +15,7 @@ import scala.tools.nsc.io.AbstractFile import reporters._ import scala.tools.util.PathResolver import scala.tools.nsc.util.ScalaClassLoader +import scala.tools.nsc.typechecker.{ TypeStrings, StructuredTypeStrings } import ScalaClassLoader.URLClassLoader import scala.tools.nsc.util.Exceptional.unwrap import scala.collection.{ mutable, immutable } @@ -22,7 +23,7 @@ import IMain._ import java.util.concurrent.Future import scala.reflect.runtime.{ universe => ru } import scala.reflect.{ ClassTag, classTag } -import scala.tools.reflect.StdRuntimeTags._ +import StdReplTags._ /** An interpreter for Scala code. * @@ -80,8 +81,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends * use a lazy val to ensure that any attempt to use the compiler object waits * on the future. */ - private var _classLoader: AbstractFileClassLoader = null // active classloader - private val _compiler: Global = newCompiler(settings, reporter) // our private compiler + private var _classLoader: util.AbstractFileClassLoader = null // active classloader + private val _compiler: ReplGlobal = newCompiler(settings, reporter) // our private compiler def compilerClasspath: Seq[URL] = ( if (isInitializeComplete) global.classPath.asURLs @@ -271,7 +272,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends if (_classLoader == null) _classLoader = makeClassLoader() } - def classLoader: AbstractFileClassLoader = { + def classLoader: util.AbstractFileClassLoader = { ensureClassLoader() _classLoader } @@ -318,7 +319,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } } - private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(replOutput.dir, parent) { + private class TranslatingClassLoader(parent: ClassLoader) extends util.AbstractFileClassLoader(replOutput.dir, parent) { /** Overridden here to try translating a simple name to the generated * class name if the original attempt fails. This method is used by * getResourceAsStream as well as findClass. @@ -329,7 +330,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends case file => file } } - private def makeClassLoader(): AbstractFileClassLoader = + private def makeClassLoader(): util.AbstractFileClassLoader = new TranslatingClassLoader(parentClassLoader match { case null => ScalaClassLoader fromURLs compilerClasspath case p => new URLClassLoader(compilerClasspath, p) diff --git a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala b/src/repl/scala/tools/nsc/interpreter/ISettings.scala index 9541d08db1..9541d08db1 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala +++ b/src/repl/scala/tools/nsc/interpreter/ISettings.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Imports.scala b/src/repl/scala/tools/nsc/interpreter/Imports.scala index ff7bfd432c..ff7bfd432c 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Imports.scala +++ b/src/repl/scala/tools/nsc/interpreter/Imports.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala b/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala index 28ddf2939c..28ddf2939c 100644 --- a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala +++ b/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala index 19fa562234..19fa562234 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/repl/scala/tools/nsc/interpreter/JLineReader.scala index 5d41f1bbb4..5d41f1bbb4 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala +++ b/src/repl/scala/tools/nsc/interpreter/JLineReader.scala diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala new file mode 100644 index 0000000000..a895944c15 --- /dev/null +++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala @@ -0,0 +1,693 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package interpreter + +import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } +import scala.tools.nsc.util.ScalaClassLoader +import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, PrintWriter, Writer } +import java.util.{ Locale } +import java.util.regex.Pattern +import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, + ForwardingJavaFileManager, JavaFileManager, JavaFileObject, + SimpleJavaFileObject, StandardLocation } +import scala.reflect.io.{ AbstractFile, Directory, File, Path } +import scala.io.Source +import scala.util.{ Try, Success, Failure } +import scala.util.Properties.lineSeparator +import scala.collection.JavaConverters +import scala.collection.generic.Clearable +import java.net.URL +import scala.language.reflectiveCalls +import Javap._ + +class JavapClass( + val loader: ScalaClassLoader, + val printWriter: PrintWriter, + intp: Option[IMain] = None +) extends scala.tools.util.Javap { + import JavapTool.ToolArgs + import JavapClass._ + + lazy val tool = JavapTool() + + /** Run the tool. Option args start with "-". + * The default options are "-protected -verbose". + * Byte data for filename args is retrieved with findBytes. + */ + def apply(args: Seq[String]): List[JpResult] = { + val (options, claases) = args partition (s => (s startsWith "-") && s.length > 1) + val (flags, upgraded) = upgrade(options) + import flags.{ app, fun, help, raw } + val targets = if (fun && !help) FunFinder(loader, intp).funs(claases) else claases + if (help || claases.isEmpty) List(JpResult(JavapTool.helper(printWriter))) + else if (targets.isEmpty) List(JpResult("No anonfuns found.")) + else tool(raw, upgraded)(targets map (claas => claas -> bytesFor(claas, app))) + } + + /** Cull our tool options. */ + private def upgrade(options: Seq[String]): (ToolArgs, Seq[String]) = ToolArgs fromArgs options match { + case (t,s) if s.nonEmpty => (t,s) + case (t,s) => (t, JavapTool.DefaultOptions) + } + + /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). */ + private def bytesFor(path: String, app: Boolean) = Try { + def last = intp.get.mostRecentVar // fail if no intp + def req = if (path == "-") last else { + val s = path.splitHashMember + if (s._1.nonEmpty) s._1 + else s._2 getOrElse "#" + } + def asAppBody(s: String) = { + val (cls, fix) = s.splitSuffix + s"${cls}$$delayedInit$$body${fix}" + } + def todo = if (app) asAppBody(req) else req + val bytes = findBytes(todo) + if (bytes.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '${path}'") + else bytes + } + + def findBytes(path: String): Array[Byte] = tryFile(path) getOrElse tryClass(path) + + /** Assume the string is a path and try to find the classfile + * it represents. + */ + def tryFile(path: String): Option[Array[Byte]] = + (Try (File(path.asClassResource)) filter (_.exists) map (_.toByteArray())).toOption + + /** Assume the string is a fully qualified class name and try to + * find the class object it represents. + * There are other symbols of interest, too: + * - a definition that is wrapped in an enclosing class + * - a synthetic that is not in scope but its associated class is + */ + def tryClass(path: String): Array[Byte] = { + def load(name: String) = loader classBytes name + def loadable(name: String) = loader resourceable name + // if path has an interior dollar, take it as a synthetic + // if the prefix up to the dollar is a symbol in scope, + // result is the translated prefix + suffix + def desynthesize(s: String) = { + val i = s indexOf '$' + if (0 until s.length - 1 contains i) { + val name = s substring (0, i) + val sufx = s substring i + val tran = intp flatMap (_ translatePath name) + def loadableOrNone(strip: Boolean) = { + def suffix(strip: Boolean)(x: String) = + (if (strip && (x endsWith "$")) x.init else x) + sufx + val res = tran map (suffix(strip) _) + if (res.isDefined && loadable(res.get)) res else None + } + // try loading translated+suffix + val res = loadableOrNone(strip = false) + // some synthetics lack a dollar, (e.g., suffix = delayedInit$body) + // so as a hack, if prefix$$suffix fails, also try prefix$suffix + if (res.isDefined) res else loadableOrNone(strip = true) + } else None + } + val p = path.asClassName // scrub any suffix + // if repl, translate the name to something replish + // (for translate, would be nicer to get the sym and ask .isClass, + // instead of translatePath and then asking did I get a class back) + val q = if (intp.isEmpty) p else ( + // only simple names get the scope treatment + Some(p) filter (_ contains '.') + // take path as a Name in scope + orElse (intp flatMap (_ translatePath p) filter loadable) + // take path as a Name in scope and find its enclosing class + orElse (intp flatMap (_ translateEnclosingClass p) filter loadable) + // take path as a synthetic derived from some Name in scope + orElse desynthesize(p) + // just try it plain + getOrElse p + ) + load(q) + } + + /** Base class for javap tool adapters for java 6 and 7. */ + abstract class JavapTool { + type ByteAry = Array[Byte] + type Input = Pair[String, Try[ByteAry]] + + /** Run the tool. */ + def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] + + // Since the tool is loaded by reflection, check for catastrophic failure. + protected def failed: Boolean + implicit protected class Failer[A](a: =>A) { + def orFailed[B >: A](b: =>B) = if (failed) b else a + } + protected def noToolError = new JpError(s"No javap tool available: ${getClass.getName} failed to initialize.") + + // output filtering support + val writer = new CharArrayWriter + def written = { + writer.flush() + val w = writer.toString + writer.reset() + w + } + + /** Create a Showable with output massage. + * @param raw show ugly repl names + * @param target attempt to filter output to show region of interest + * @param preamble other messages to output + */ + def showWithPreamble(raw: Boolean, target: String, preamble: String = ""): Showable = new Showable { + // ReplStrippingWriter clips and scrubs on write(String) + // circumvent it by write(mw, 0, mw.length) or wrap it in withoutUnwrapping + def show() = + if (raw && intp.isDefined) intp.get withoutUnwrapping { writeLines() } + else writeLines() + private def writeLines() { + // take Foo# as Foo#apply for purposes of filtering. Useful for -fun Foo#; + // if apply is added here, it's for other than -fun: javap Foo#, perhaps m#? + val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } + var filtering = false // true if in region matching filter + // true to output + def checkFilter(line: String) = if (filterOn.isEmpty) true else { + // cheap heuristic, todo maybe parse for the java sig. + // method sigs end in paren semi + def isAnyMethod = line.endsWith(");") + def isOurMethod = { + val lparen = line.lastIndexOf('(') + val blank = line.lastIndexOf(' ', lparen) + (blank >= 0 && line.substring(blank+1, lparen) == filterOn.get) + } + filtering = if (filtering) { + // next blank line terminates section + // for -public, next line is next method, more or less + line.trim.nonEmpty && !isAnyMethod + } else { + isAnyMethod && isOurMethod + } + filtering + } + for (line <- Source.fromString(preamble + written).getLines(); if checkFilter(line)) + printWriter write line+lineSeparator + printWriter.flush() + } + } + } + + class JavapTool6 extends JavapTool { + import JavapTool._ + val EnvClass = loader.tryToInitializeClass[FakeEnvironment](Env).orNull + val PrinterClass = loader.tryToInitializeClass[FakePrinter](Printer).orNull + override protected def failed = (EnvClass eq null) || (PrinterClass eq null) + + val PrinterCtr = PrinterClass.getConstructor(classOf[InputStream], classOf[PrintWriter], EnvClass) orFailed null + val printWrapper = new PrintWriter(writer) + def newPrinter(in: InputStream, env: FakeEnvironment): FakePrinter = + PrinterCtr.newInstance(in, printWrapper, env) orFailed null + def showable(raw: Boolean, target: String, fp: FakePrinter): Showable = { + fp.asInstanceOf[{ def print(): Unit }].print() // run tool and flush to buffer + printWrapper.flush() // just in case + showWithPreamble(raw, target) + } + + lazy val parser = new JpOptions + def newEnv(opts: Seq[String]): FakeEnvironment = { + def result = { + val env: FakeEnvironment = EnvClass.newInstance() + parser(opts) foreach { case (name, value) => + val field = EnvClass getDeclaredField name + field setAccessible true + field.set(env, value.asInstanceOf[AnyRef]) + } + env + } + result orFailed null + } + + override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = + (inputs map { + case (claas, Success(ba)) => JpResult(showable(raw, claas, newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) + case (_, Failure(e)) => JpResult(e.toString) + }).toList orFailed List(noToolError) + } + + class JavapTool7 extends JavapTool { + + import JavapTool._ + type Task = { + def call(): Boolean // true = ok + //def run(args: Array[String]): Int // all args + //def handleOptions(args: Array[String]): Unit // options, then run() or call() + } + // result of Task.run + //object TaskResult extends Enumeration { + // val Ok, Error, CmdErr, SysErr, Abnormal = Value + //} + val TaskClaas = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull + override protected def failed = TaskClaas eq null + + val TaskCtor = TaskClaas.getConstructor( + classOf[Writer], + classOf[JavaFileManager], + classOf[DiagnosticListener[_]], + classOf[JIterable[String]], + classOf[JIterable[String]] + ) orFailed null + + class JavaReporter extends DiagnosticListener[JavaFileObject] with Clearable { + import scala.collection.mutable.{ ArrayBuffer, SynchronizedBuffer } + type D = Diagnostic[_ <: JavaFileObject] + val diagnostics = new ArrayBuffer[D] with SynchronizedBuffer[D] + override def report(d: Diagnostic[_ <: JavaFileObject]) { + diagnostics += d + } + override def clear() = diagnostics.clear() + /** All diagnostic messages. + * @param locale Locale for diagnostic messages, null by default. + */ + def messages(implicit locale: Locale = null) = (diagnostics map (_ getMessage locale)).toList + + def reportable(raw: Boolean): String = { + // don't filter this message if raw, since the names are likely to differ + val container = "Binary file .* contains .*".r + val m = if (raw) messages + else messages filter (_ match { case container() => false case _ => true }) + clear() + if (m.nonEmpty) m mkString ("", lineSeparator, lineSeparator) + else "" + } + } + val reporter = new JavaReporter + + // DisassemblerTool.getStandardFileManager(reporter,locale,charset) + val defaultFileManager: JavaFileManager = + (loader.tryToLoadClass[JavaFileManager]("com.sun.tools.javap.JavapFileManager").get getMethod ( + "create", + classOf[DiagnosticListener[_]], + classOf[PrintWriter] + ) invoke (null, reporter, new PrintWriter(System.err, true))).asInstanceOf[JavaFileManager] orFailed null + + // manages named arrays of bytes, which might have failed to load + class JavapFileManager(val managed: Seq[Input])(delegate: JavaFileManager = defaultFileManager) + extends ForwardingJavaFileManager[JavaFileManager](delegate) { + import JavaFileObject.Kind + import Kind._ + import StandardLocation._ + import JavaFileManager.Location + import java.net.URI + def uri(name: String): URI = new URI(name) // new URI("jfo:" + name) + + def inputNamed(name: String): Try[ByteAry] = (managed find (_._1 == name)).get._2 + def managedFile(name: String, kind: Kind) = kind match { + case CLASS => fileObjectForInput(name, inputNamed(name), kind) + case _ => null + } + // todo: just wrap it as scala abstractfile and adapt it uniformly + def fileObjectForInput(name: String, bytes: Try[ByteAry], kind: Kind): JavaFileObject = + new SimpleJavaFileObject(uri(name), kind) { + override def openInputStream(): InputStream = new ByteArrayInputStream(bytes.get) + // if non-null, ClassWriter wrongly requires scheme non-null + override def toUri: URI = null + override def getName: String = name + // suppress + override def getLastModified: Long = -1L + } + override def getJavaFileForInput(location: Location, className: String, kind: Kind): JavaFileObject = + location match { + case CLASS_PATH => managedFile(className, kind) + case _ => null + } + override def hasLocation(location: Location): Boolean = + location match { + case CLASS_PATH => true + case _ => false + } + } + def fileManager(inputs: Seq[Input]) = new JavapFileManager(inputs)() + + // show tool messages and tool output, with output massage + def showable(raw: Boolean, target: String): Showable = showWithPreamble(raw, target, reporter.reportable(raw)) + + // eventually, use the tool interface + def task(options: Seq[String], claases: Seq[String], inputs: Seq[Input]): Task = { + //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). + //getTask(writer, fileManager, reporter, options.asJava, claases.asJava) + import JavaConverters.asJavaIterableConverter + TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, claases.asJava) + .orFailed (throw new IllegalStateException) + } + // a result per input + private def applyOne(raw: Boolean, options: Seq[String], claas: String, inputs: Seq[Input]): Try[JpResult] = + Try { + task(options, Seq(claas), inputs).call() + } map { + case true => JpResult(showable(raw, claas)) + case _ => JpResult(reporter.reportable(raw)) + } recoverWith { + case e: java.lang.reflect.InvocationTargetException => e.getCause match { + case t: IllegalArgumentException => Success(JpResult(t.getMessage)) // bad option + case x => Failure(x) + } + } lastly { + reporter.clear() + } + override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { + case (claas, Success(_)) => applyOne(raw, options, claas, inputs).get + case (_, Failure(e)) => JpResult(e.toString) + }).toList orFailed List(noToolError) + } + + object JavapTool { + // >= 1.7 + val Tool = "com.sun.tools.javap.JavapTask" + + // < 1.7 + val Env = "sun.tools.javap.JavapEnvironment" + val Printer = "sun.tools.javap.JavapPrinter" + // "documentation" + type FakeEnvironment = AnyRef + type FakePrinter = AnyRef + + // support JavapEnvironment + class JpOptions { + private object Access { + final val PRIVATE = 0 + final val PROTECTED = 1 + final val PACKAGE = 2 + final val PUBLIC = 3 + } + private val envActionMap: Map[String, (String, Any)] = { + val map = Map( + "-l" -> (("showLineAndLocal", true)), + "-c" -> (("showDisassembled", true)), + "-s" -> (("showInternalSigs", true)), + "-verbose" -> (("showVerbose", true)), + "-private" -> (("showAccess", Access.PRIVATE)), + "-package" -> (("showAccess", Access.PACKAGE)), + "-protected" -> (("showAccess", Access.PROTECTED)), + "-public" -> (("showAccess", Access.PUBLIC)), + "-all" -> (("showallAttr", true)) + ) + map ++ List( + "-v" -> map("-verbose"), + "-p" -> map("-private") + ) + } + def apply(opts: Seq[String]): Seq[(String, Any)] = { + opts flatMap { opt => + envActionMap get opt match { + case Some(pair) => List(pair) + case _ => + val charOpts = opt.tail.toSeq map ("-" + _) + if (charOpts forall (envActionMap contains _)) + charOpts map envActionMap + else Nil + } + } + } + } + + case class ToolArgs(raw: Boolean = false, help: Boolean = false, app: Boolean = false, fun: Boolean = false) + + object ToolArgs { + def fromArgs(args: Seq[String]): (ToolArgs, Seq[String]) = ((ToolArgs(), Seq[String]()) /: (args flatMap massage)) { + case ((t,others), s) => s match { + case "-fun" => (t copy (fun=true), others) + case "-app" => (t copy (app=true), others) + case "-help" => (t copy (help=true), others) + case "-raw" => (t copy (raw=true), others) + case _ => (t, others :+ s) + } + } + } + + val helps = List( + "usage" -> ":javap [opts] [path or class or -]...", + "-help" -> "Prints this help message", + "-raw" -> "Don't unmangle REPL names", + "-app" -> "Show the DelayedInit body of Apps", + "-fun" -> "Show anonfuns for class or Class#method", + "-verbose/-v" -> "Stack size, number of locals, method args", + "-private/-p" -> "Private classes and members", + "-package" -> "Package-private classes and members", + "-protected" -> "Protected classes and members", + "-public" -> "Public classes and members", + "-l" -> "Line and local variable tables", + "-c" -> "Disassembled code", + "-s" -> "Internal type signatures", + "-sysinfo" -> "System info of class", + "-constants" -> "Static final constants" + ) + + // match prefixes and unpack opts, or -help on failure + def massage(arg: String): Seq[String] = { + require(arg startsWith "-") + // arg matches opt "-foo/-f" if prefix of -foo or exactly -f + val r = """(-[^/]*)(/(-.))?""".r + def maybe(opt: String, s: String): Option[String] = opt match { + // disambiguate by preferring short form + case r(lf,_,sf) if s == sf => Some(sf) + case r(lf,_,sf) if lf startsWith s => Some(lf) + case _ => None + } + def candidates(s: String) = (helps map (h => maybe(h._1, s))).flatten + // one candidate or one single-char candidate + def uniqueOf(maybes: Seq[String]) = { + def single(s: String) = s.length == 2 + if (maybes.length == 1) maybes + else if ((maybes count single) == 1) maybes filter single + else Nil + } + // each optchar must decode to exactly one option + def unpacked(s: String): Try[Seq[String]] = { + val ones = (s drop 1) map { c => + val maybes = uniqueOf(candidates(s"-$c")) + if (maybes.length == 1) Some(maybes.head) else None + } + Try(ones) filter (_ forall (_.isDefined)) map (_.flatten) + } + val res = uniqueOf(candidates(arg)) + if (res.nonEmpty) res + else (unpacked(arg) + getOrElse (Seq("-help"))) // or else someone needs help + } + + def helper(pw: PrintWriter) = new Showable { + def show() = helps foreach (p => pw write "%-12.12s%s%n".format(p._1,p._2)) + } + + val DefaultOptions = List("-protected", "-verbose") + + def isAvailable = Seq(Env, Tool) exists (cn => hasClass(loader, cn)) + + private def hasClass(cl: ScalaClassLoader, cn: String) = cl.tryToInitializeClass[AnyRef](cn).isDefined + + private def isTaskable(cl: ScalaClassLoader) = hasClass(cl, Tool) + + def apply() = if (isTaskable(loader)) new JavapTool7 else new JavapTool6 + } +} + +object JavapClass { + def apply( + loader: ScalaClassLoader = ScalaClassLoader.appLoader, + printWriter: PrintWriter = new PrintWriter(System.out, true), + intp: Option[IMain] = None + ) = new JavapClass(loader, printWriter, intp) + + // We enjoy flexibility in specifying either a fully-qualified class name com.acme.Widget + // or a resource path com/acme/Widget.class; but not widget.out + implicit class MaybeClassLike(val s: String) extends AnyVal { + /* private[this] final val suffix = ".class" */ + private def suffix = ".class" + def asClassName = (s stripSuffix suffix).replace('/', '.') + def asClassResource = if (s endsWith suffix) s else s.replace('.', '/') + suffix + def splitSuffix: (String, String) = if (s endsWith suffix) (s dropRight suffix.length, suffix) else (s, "") + def strippingSuffix(f: String => String): String = + if (s endsWith suffix) f(s dropRight suffix.length) else s + // e.g. Foo#bar. Foo# yields zero-length member part. + def splitHashMember: (String, Option[String]) = { + val i = s lastIndexOf '#' + if (i < 0) (s, None) + //else if (i >= s.length - 1) (s.init, None) + else (s take i, Some(s drop i+1)) + } + } + implicit class ClassLoaderOps(val cl: ClassLoader) extends AnyVal { + private def parentsOf(x: ClassLoader): List[ClassLoader] = if (x == null) Nil else x :: parentsOf(x.getParent) + def parents: List[ClassLoader] = parentsOf(cl) + /* all file locations */ + def locations = { + def alldirs = parents flatMap (_ match { + case ucl: ScalaClassLoader.URLClassLoader => ucl.classPathURLs + case jcl: java.net.URLClassLoader => jcl.getURLs + case _ => Nil + }) + val dirs = for (d <- alldirs; if d.getProtocol == "file") yield Path(new JFile(d.toURI)) + dirs + } + /* only the file location from which the given class is loaded */ + def locate(k: String): Option[Path] = { + Try { + val claas = try cl loadClass k catch { + case _: NoClassDefFoundError => null // let it snow + } + // cf ScalaClassLoader.originOfClass + claas.getProtectionDomain.getCodeSource.getLocation + } match { + case Success(null) => None + case Success(loc) if loc.isFile => Some(Path(new JFile(loc.toURI))) + case _ => None + } + } + /* would classBytes succeed with a nonempty array */ + def resourceable(className: String): Boolean = cl.getResource(className.asClassResource) != null + } + implicit class PathOps(val p: Path) extends AnyVal { + import scala.tools.nsc.io.Jar + def isJar = Jar isJarOrZip p + } + implicit class URLOps(val url: URL) extends AnyVal { + def isFile: Boolean = url.getProtocol == "file" + } + object FunFinder { + def apply(loader: ScalaClassLoader, intp: Option[IMain]) = new FunFinder(loader, intp) + } + class FunFinder(loader: ScalaClassLoader, intp: Option[IMain]) { + + // class k, candidate f without prefix + def isFunOfClass(k: String, f: String) = { + val p = (s"${Pattern quote k}\\$$+anonfun").r + (p findPrefixOf f).nonEmpty + } + // class k, candidate f without prefix, method m + def isFunOfMethod(k: String, m: String, f: String) = { + val p = (s"${Pattern quote k}\\$$+anonfun\\$$${Pattern quote m}\\$$").r + (p findPrefixOf f).nonEmpty + } + def isFunOfTarget(k: String, m: Option[String], f: String) = + if (m.isEmpty) isFunOfClass(k, f) + else isFunOfMethod(k, m.get, f) + def listFunsInAbsFile(k: String, m: Option[String], d: AbstractFile) = { + for (f <- d; if !f.isDirectory && isFunOfTarget(k, m, f.name)) yield f.name + } + // path prefix p, class k, dir d + def listFunsInDir(p: String, k: String, m: Option[String])(d: Directory) = { + val subdir = Path(p) + for (f <- (d / subdir).toDirectory.list; if f.isFile && isFunOfTarget(k, m, f.name)) + yield f.name + } + // path prefix p, class k, jar file f + def listFunsInJar(p: String, k: String, m: Option[String])(f: File) = { + import java.util.jar.JarEntry + import scala.tools.nsc.io.Jar + def maybe(e: JarEntry) = { + val (path, name) = { + val parts = e.getName split "/" + if (parts.length < 2) ("", e.getName) + else (parts.init mkString "/", parts.last) + } + if (path == p && isFunOfTarget(k, m, name)) Some(name) else None + } + (new Jar(f) map maybe).flatten + } + def loadable(name: String) = loader resourceable name + // translated class, optional member, opt member to filter on, whether it is repl output + def translate(s: String): (String, Option[String], Option[String], Boolean) = { + val (k0, m0) = s.splitHashMember + val k = k0.asClassName + val member = m0 filter (_.nonEmpty) // take Foo# as no member, not "" + val filter = m0 flatMap { case "" => Some("apply") case _ => None } // take Foo# as filter on apply + // class is either something replish or available to loader + // $line.$read$$etc$Foo#member + ((intp flatMap (_ translatePath k) filter (loadable) map ((_, member, filter, true))) + // s = "f" and $line.$read$$etc$#f is what we're after, + // ignoring any #member (except take # as filter on #apply) + orElse (intp flatMap (_ translateEnclosingClass k) map ((_, Some(k), filter, true))) + getOrElse (k, member, filter, false)) + } + /** Find the classnames of anonfuns associated with k, + * where k may be an available class or a symbol in scope. + */ + def funsOf(k0: String): Seq[String] = { + // class is either something replish or available to loader + val (k, member, filter, isReplish) = translate(k0) + val splat = k split "\\." + val name = splat.last + val prefix = if (splat.length > 1) splat.init mkString "/" else "" + val pkg = if (splat.length > 1) splat.init mkString "." else "" + // reconstitute an anonfun with a package + // if filtered, add the hash back, e.g. pkg.Foo#bar, pkg.Foo$anon$1#apply + def packaged(s: String) = { + val p = if (pkg.isEmpty) s else s"$pkg.$s" + val pm = filter map (p + "#" + _) + pm getOrElse p + } + // is this translated path in (usually virtual) repl outdir? or loadable from filesystem? + val fs = if (isReplish) { + def outed(d: AbstractFile, p: Seq[String]): Option[AbstractFile] = { + if (p.isEmpty) Option(d) + else Option(d.lookupName(p.head, directory = true)) flatMap (f => outed(f, p.tail)) + } + outed(intp.get.replOutput.dir, splat.init) map { d => + listFunsInAbsFile(name, member, d) map packaged + } + } else { + loader locate k map { w => + if (w.isDirectory) listFunsInDir(prefix, name, member)(w.toDirectory) map packaged + else if (w.isJar) listFunsInJar(prefix, name, member)(w.toFile) map packaged + else Nil + } + } + fs match { + case Some(xs) => xs.to[Seq] // maybe empty + case None => Seq() // nothing found, e.g., junk input + } + } + def funs(ks: Seq[String]) = ks flatMap funsOf _ + } +} + +object Javap { + def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = JavapClass(cl).JavapTool.isAvailable + + def apply(path: String): Unit = apply(Seq(path)) + def apply(args: Seq[String]): Unit = JavapClass() apply args foreach (_.show()) + + trait Showable { + def show(): Unit + } + + sealed trait JpResult extends scala.tools.util.JpResult { + type ResultType + def isError: Boolean + def value: ResultType + def show(): Unit + // todo + // def header(): String + // def fields(): List[String] + // def methods(): List[String] + // def signatures(): List[String] + } + object JpResult { + def apply(msg: String) = new JpError(msg) + def apply(res: Showable) = new JpSuccess(res) + } + class JpError(msg: String) extends JpResult { + type ResultType = String + def isError = true + def value = msg + def show() = println(msg) // makes sense for :javap, less for -Ygen-javap + } + class JpSuccess(val value: Showable) extends JpResult { + type ResultType = AnyRef + def isError = false + def show() = value.show() // output to tool's PrintWriter + } + implicit class Lastly[A](val t: Try[A]) extends AnyVal { + private def effect[X](last: =>Unit)(a: X): Try[A] = { last; t } + def lastly(last: =>Unit): Try[A] = t transform (effect(last) _, effect(last) _) + } +} diff --git a/src/compiler/scala/tools/nsc/interpreter/Logger.scala b/src/repl/scala/tools/nsc/interpreter/Logger.scala index 7407daf8d0..7407daf8d0 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Logger.scala +++ b/src/repl/scala/tools/nsc/interpreter/Logger.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala b/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala index 4bba27b714..4bba27b714 100644 --- a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala +++ b/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala index 84a47311e2..84a47311e2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala b/src/repl/scala/tools/nsc/interpreter/NamedParam.scala index 627a881cae..a0af72940a 100644 --- a/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala +++ b/src/repl/scala/tools/nsc/interpreter/NamedParam.scala @@ -10,6 +10,7 @@ import NamedParam._ import scala.language.implicitConversions import scala.reflect.runtime.{universe => ru} import scala.reflect.{ClassTag, classTag} +import scala.tools.nsc.typechecker.{ TypeStrings } trait NamedParamCreator { protected def freshName: () => String diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/repl/scala/tools/nsc/interpreter/Naming.scala index 57f3675ada..57f3675ada 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Naming.scala +++ b/src/repl/scala/tools/nsc/interpreter/Naming.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala b/src/repl/scala/tools/nsc/interpreter/Parsed.scala index 672a6fd28f..672a6fd28f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala +++ b/src/repl/scala/tools/nsc/interpreter/Parsed.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Pasted.scala b/src/repl/scala/tools/nsc/interpreter/Pasted.scala index f5db3d9e3a..f5db3d9e3a 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Pasted.scala +++ b/src/repl/scala/tools/nsc/interpreter/Pasted.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Phased.scala b/src/repl/scala/tools/nsc/interpreter/Phased.scala index f625124e70..f625124e70 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Phased.scala +++ b/src/repl/scala/tools/nsc/interpreter/Phased.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala index e517a16b32..e517a16b32 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/repl/scala/tools/nsc/interpreter/Power.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala b/src/repl/scala/tools/nsc/interpreter/ReplConfig.scala index 3392ea0b5e..3392ea0b5e 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplConfig.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplDir.scala b/src/repl/scala/tools/nsc/interpreter/ReplDir.scala index 5d386b47b7..5d386b47b7 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplDir.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplDir.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala index 0eabd84234..51fab3082e 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -23,6 +23,14 @@ trait ReplGlobal extends Global { override lazy val analyzer = new { val global: ReplGlobal.this.type = ReplGlobal.this } with Analyzer { + + override protected def findMacroClassLoader(): ClassLoader = { + val loader = super.findMacroClassLoader + macroLogVerbose("macro classloader: initializing from a REPL classloader: %s".format(global.classPath.asURLs)) + val virtualDirectory = globalSettings.outputDirs.getSingleOutput.get + new util.AbstractFileClassLoader(virtualDirectory, loader) {} + } + override def newTyper(context: Context): Typer = new Typer(context) { override def typed(tree: Tree, mode: Mode, pt: Type): Tree = { val res = super.typed(tree, mode, pt) diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala b/src/repl/scala/tools/nsc/interpreter/ReplProps.scala index 2364918494..2364918494 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplProps.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala index b20166d070..b20166d070 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala b/src/repl/scala/tools/nsc/interpreter/ReplStrings.scala index 08472bbc64..08472bbc64 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplStrings.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/repl/scala/tools/nsc/interpreter/ReplVals.scala index ea100b25f2..ea100b25f2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplVals.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/Results.scala b/src/repl/scala/tools/nsc/interpreter/Results.scala index e400906a58..e400906a58 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Results.scala +++ b/src/repl/scala/tools/nsc/interpreter/Results.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala b/src/repl/scala/tools/nsc/interpreter/RichClass.scala index 36cdf65510..36cdf65510 100644 --- a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala +++ b/src/repl/scala/tools/nsc/interpreter/RichClass.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala b/src/repl/scala/tools/nsc/interpreter/SimpleReader.scala index 2d0917d91f..2d0917d91f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala +++ b/src/repl/scala/tools/nsc/interpreter/SimpleReader.scala diff --git a/src/repl/scala/tools/nsc/interpreter/StdReplTags.scala b/src/repl/scala/tools/nsc/interpreter/StdReplTags.scala new file mode 100644 index 0000000000..ebbb397a0c --- /dev/null +++ b/src/repl/scala/tools/nsc/interpreter/StdReplTags.scala @@ -0,0 +1,15 @@ +package scala.tools.nsc +package interpreter + +import scala.tools.reflect.StdTags +import scala.reflect.runtime.{ universe => ru } + +trait StdReplTags extends StdTags { + lazy val tagOfStdReplVals = tagOfStaticClass[StdReplVals] + lazy val tagOfIMain = tagOfStaticClass[IMain] +} + +object StdReplTags extends StdTags with StdReplTags { + val u: ru.type = ru + val m = u.runtimeMirror(getClass.getClassLoader) +} diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/repl/scala/tools/nsc/interpreter/package.scala index 52a085080b..52a085080b 100644 --- a/src/compiler/scala/tools/nsc/interpreter/package.scala +++ b/src/repl/scala/tools/nsc/interpreter/package.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala b/src/repl/scala/tools/nsc/interpreter/session/FileBackedHistory.scala index dddfb1b8f6..dddfb1b8f6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala +++ b/src/repl/scala/tools/nsc/interpreter/session/FileBackedHistory.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/session/History.scala b/src/repl/scala/tools/nsc/interpreter/session/History.scala index 794d41adc7..794d41adc7 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/History.scala +++ b/src/repl/scala/tools/nsc/interpreter/session/History.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/session/JLineHistory.scala b/src/repl/scala/tools/nsc/interpreter/session/JLineHistory.scala index 18e0ee7c85..18e0ee7c85 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/JLineHistory.scala +++ b/src/repl/scala/tools/nsc/interpreter/session/JLineHistory.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala b/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala index 89998e438a..89998e438a 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala +++ b/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/session/package.scala b/src/repl/scala/tools/nsc/interpreter/session/package.scala index c62cf21151..c62cf21151 100644 --- a/src/compiler/scala/tools/nsc/interpreter/session/package.scala +++ b/src/repl/scala/tools/nsc/interpreter/session/package.scala |