From 09ce5c32f1db71ccaa5d4b22076d9ec606ad5ec9 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 15 Jan 2015 21:48:47 +1000 Subject: SI-6502 More robust REPL :require - handle missing files gracefully (rather than NPE) - read the class name with ASM, rather than with a dummy classloader. The dummy classloader is prone to throwing `LinkageError`s, as reported in the comments of SI-6502. Manual test of the original report: ``` % qscala Welcome to Scala version 2.11.5-20150115-183424-155dbf3fdf (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25). Type in expressions to have them evaluated. Type :help for more information. scala> :require does/not/exist Cannot read: does/not/exist scala> classOf[org.junit.Test] :8: error: object junit is not a member of package org classOf[org.junit.Test] ^ scala> :require /Users/jason/.m2/repository/junit/junit/4.11/junit-4.11.jar Added '/Users/jason/.m2/repository/junit/junit/4.11/junit-4.11.jar' to classpath. scala> classOf[org.junit.Test] res1: Class[org.junit.Test] = interface org.junit.Test ``` I have commited an automated test that is a minimization of this one. --- src/repl/scala/tools/nsc/interpreter/ILoop.scala | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'src/repl') diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 4fd5768b79..4d71e0e09e 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -12,6 +12,7 @@ import scala.annotation.tailrec import Predef.{ println => _, _ } import interpreter.session._ import StdReplTags._ +import scala.tools.asm.ClassReader import scala.util.Properties.{ jdkHome, javaVersion, versionString, javaVmName } import scala.tools.nsc.util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream } import scala.reflect.classTag @@ -633,28 +634,29 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) * the interpreter and replays all interpreter expressions. */ def require(arg: String): Unit = { - class InfoClassLoader extends java.lang.ClassLoader { - def classOf(arr: Array[Byte]): Class[_] = - super.defineClass(null, arr, 0, arr.length) - } - val f = File(arg).normalize - if (f.isDirectory) { - echo("Adding directories to the classpath is not supported. Add a jar instead.") + val jarFile = AbstractFile.getDirectory(new java.io.File(arg)) + if (jarFile == null) { + echo(s"Cannot load '$arg'") return } - val jarFile = AbstractFile.getDirectory(new java.io.File(arg)) - def flatten(f: AbstractFile): Iterator[AbstractFile] = if (f.isClassContainer) f.iterator.flatMap(flatten) else Iterator(f) val entries = flatten(jarFile) - val cloader = new InfoClassLoader - def classNameOf(classFile: AbstractFile): String = cloader.classOf(classFile.toByteArray).getName + def classNameOf(classFile: AbstractFile): String = { + val input = classFile.input + try { + val reader = new ClassReader(input) + reader.getClassName.replace('/', '.') + } finally { + input.close() + } + } def alreadyDefined(clsName: String) = intp.classLoader.tryToLoadClass(clsName).isDefined val exists = entries.filter(_.hasExtension("class")).map(classNameOf).exists(alreadyDefined) -- cgit v1.2.3 From a3c2b55b16ad404b51f4eea0628dd00badeb0fbe Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 16 Jan 2015 11:38:44 +1000 Subject: REPL: Tread EOF a "no" in the "yes"/"no" prompt. Before, we got in an endless loop if using ^D to try to end the session. When piping commands into the REPL, this was rather annoying! ``` scala-hash v2.11.5 Welcome to Scala version 2.11.5-20150101-184742-3fafbc204f (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25). Type in expressions to have them evaluated. Type :help for more information. scala> :require xxx java.lang.NullPointerException at scala.tools.nsc.interpreter.ILoop.scala$tools$nsc$interpreter$ILoop$$flatten$1(ILoop.scala:651) at scala.tools.nsc.interpreter.ILoop.require(ILoop.scala:654) That entry seems to have slain the compiler. Shall I replay your session? I can re-run each line except the last one. [y/n]^D You must enter y or n. That entry seems to have slain the compiler. Shall I replay your session? I can re-run each line except the last one. [y/n]^D ... ``` --- src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala | 1 + 1 file changed, 1 insertion(+) (limited to 'src/repl') diff --git a/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala b/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala index 28ddf2939c..ed69d449cb 100644 --- a/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala +++ b/src/repl/scala/tools/nsc/interpreter/InteractiveReader.scala @@ -23,6 +23,7 @@ trait InteractiveReader { def readYesOrNo(prompt: String, alt: => Boolean): Boolean = readOneKey(prompt) match { case 'y' => true case 'n' => false + case -1 => false // EOF case _ => alt } -- cgit v1.2.3