From 964a197cd90e561d05c9d725cc13895f18b6a6d0 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 4 Oct 2014 12:33:11 -0700 Subject: SI-8843 AbsFileCL acts like a CL Let the AbstractFileClassLoader override just the usual suspects. Normal delegation behavior should ensue. That's instead of overriding `getResourceAsStream`, which was intended that "The repl classloader now works more like you'd expect a classloader to." (Workaround for "Don't know how to construct an URL for something which exists only in memory.") Also override `findResources` so that `getResources` does the obvious thing, namely, return one iff `getResource` does. The translating class loader for REPL only special-cases `foo.class`: as a fallback, take `foo` as `$line42.$read$something$foo` and try that class file. That's the use case for "works like you'd expect it to." There was a previous fix to ensure `getResource` doesn't take a class name. The convenience behavior, that `classBytes` takes either a class name or a resource path ending in ".class", has been promoted to `ScalaClassLoader`. --- test/files/run/t8843-repl-xlat.scala | 33 +++++ .../scala/reflect/internal/PrintersTest.scala | 6 +- .../util/AbstractFileClassLoaderTest.scala | 138 +++++++++++++++++++++ 3 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 test/files/run/t8843-repl-xlat.scala create mode 100644 test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala (limited to 'test') diff --git a/test/files/run/t8843-repl-xlat.scala b/test/files/run/t8843-repl-xlat.scala new file mode 100644 index 0000000000..6426dbe7d4 --- /dev/null +++ b/test/files/run/t8843-repl-xlat.scala @@ -0,0 +1,33 @@ + +import scala.tools.partest.SessionTest + +// Handy hamburger helper for repl resources +object Test extends SessionTest { + def session = +"""Type in expressions to have them evaluated. +Type :help for more information. + +scala> $intp.isettings.unwrapStrings = false +$intp.isettings.unwrapStrings: Boolean = false + +scala> class Bippy +defined class Bippy + +scala> $intp.classLoader getResource "Bippy.class" +res0: java.net.URL = memory:(memory)/$line4/$read$$iw$$iw$Bippy.class + +scala> ($intp.classLoader getResources "Bippy.class").nextElement +res1: java.net.URL = memory:(memory)/$line4/$read$$iw$$iw$Bippy.class + +scala> ($intp.classLoader classBytes "Bippy").nonEmpty +res2: Boolean = true + +scala> ($intp.classLoader classAsStream "Bippy") != null +res3: Boolean = true + +scala> $intp.classLoader getResource "Bippy" +res4: java.net.URL = null + +scala> :quit""" +} + diff --git a/test/junit/scala/reflect/internal/PrintersTest.scala b/test/junit/scala/reflect/internal/PrintersTest.scala index 1458b942dc..ca9b4671b2 100644 --- a/test/junit/scala/reflect/internal/PrintersTest.scala +++ b/test/junit/scala/reflect/internal/PrintersTest.scala @@ -24,10 +24,10 @@ object PrinterHelper { resultCode.lines mkString s"$LF" def assertResultCode(code: String)(parsedCode: String = "", typedCode: String = "", wrap: Boolean = false, printRoot: Boolean = false) = { - def toolboxTree(tree: => Tree) = try{ + def toolboxTree(tree: => Tree) = try { tree } catch { - case e:scala.tools.reflect.ToolBoxError => throw new Exception(e.getMessage + ": " + code) + case e:scala.tools.reflect.ToolBoxError => throw new Exception(e.getMessage + ": " + code, e) } def wrapCode(source: String) = { @@ -1186,4 +1186,4 @@ trait QuasiTreesPrintTests { | }; | () |}""") -} \ No newline at end of file +} diff --git a/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala b/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala new file mode 100644 index 0000000000..a2537ddab7 --- /dev/null +++ b/test/junit/scala/reflect/internal/util/AbstractFileClassLoaderTest.scala @@ -0,0 +1,138 @@ +package scala.reflect.internal.util + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class AbstractFileClassLoaderTest { + + import scala.reflect.io._ + import scala.io.Source + import scala.io.Codec.UTF8 + import scala.reflect.io.Streamable + import java.net.{ URLClassLoader, URL } + + implicit def `we love utf8` = UTF8 + implicit class `abs file ops`(f: AbstractFile) { + def writeContent(s: String): Unit = Streamable.closing(f.bufferedOutput)(os => os write s.getBytes(UTF8.charSet)) + } + implicit class `url slurp`(url: URL) { + def slurp(): String = Streamable.slurp(url) + } + + val NoClassLoader: ClassLoader = null + + def fuzzBuzzBooz: (AbstractFile, AbstractFile) = { + val fuzz = new VirtualDirectory("fuzz", None) + val buzz = fuzz subdirectoryNamed "buzz" + val booz = buzz fileNamed "booz.class" + (fuzz, booz) + } + + @Test + def afclGetsParent(): Unit = { + val p = new URLClassLoader(Array.empty[URL]) + val d = new VirtualDirectory("vd", None) + val x = new AbstractFileClassLoader(d, p) + assertSame(p, x.getParent) + } + + @Test + def afclGetsResource(): Unit = { + val (fuzz, booz) = fuzzBuzzBooz + booz writeContent "hello, world" + val x = new AbstractFileClassLoader(fuzz, NoClassLoader) + val r = x.getResource("buzz/booz.class") + assertNotNull(r) + assertEquals("hello, world", r.slurp()) + } + + @Test + def afclGetsResourceFromParent(): Unit = { + val (fuzz, booz) = fuzzBuzzBooz + val (fuzz_, booz_) = fuzzBuzzBooz + booz writeContent "hello, world" + booz_ writeContent "hello, world_" + val p = new AbstractFileClassLoader(fuzz, NoClassLoader) + val x = new AbstractFileClassLoader(fuzz_, p) + val r = x.getResource("buzz/booz.class") + assertNotNull(r) + assertEquals("hello, world", r.slurp()) + } + + @Test + def afclGetsResourceInDefaultPackage(): Unit = { + val fuzz = new VirtualDirectory("fuzz", None) + val booz = fuzz fileNamed "booz.class" + val bass = fuzz fileNamed "bass" + booz writeContent "hello, world" + bass writeContent "lo tone" + val x = new AbstractFileClassLoader(fuzz, NoClassLoader) + val r = x.getResource("booz.class") + assertNotNull(r) + assertEquals("hello, world", r.slurp()) + assertEquals("lo tone", (x getResource "bass").slurp()) + } + + // SI-8843 + @Test + def afclGetsResources(): Unit = { + val (fuzz, booz) = fuzzBuzzBooz + booz writeContent "hello, world" + val x = new AbstractFileClassLoader(fuzz, NoClassLoader) + val e = x.getResources("buzz/booz.class") + assertTrue(e.hasMoreElements) + assertEquals("hello, world", e.nextElement.slurp()) + assertFalse(e.hasMoreElements) + } + + @Test + def afclGetsResourcesFromParent(): Unit = { + val (fuzz, booz) = fuzzBuzzBooz + val (fuzz_, booz_) = fuzzBuzzBooz + booz writeContent "hello, world" + booz_ writeContent "hello, world_" + val p = new AbstractFileClassLoader(fuzz, NoClassLoader) + val x = new AbstractFileClassLoader(fuzz_, p) + val e = x.getResources("buzz/booz.class") + assertTrue(e.hasMoreElements) + assertEquals("hello, world", e.nextElement.slurp()) + assertTrue(e.hasMoreElements) + assertEquals("hello, world_", e.nextElement.slurp()) + assertFalse(e.hasMoreElements) + } + + @Test + def afclGetsResourceAsStream(): Unit = { + val (fuzz, booz) = fuzzBuzzBooz + booz writeContent "hello, world" + val x = new AbstractFileClassLoader(fuzz, NoClassLoader) + val r = x.getResourceAsStream("buzz/booz.class") + assertNotNull(r) + assertEquals("hello, world", Streamable.closing(r)(is => Source.fromInputStream(is).mkString)) + } + + @Test + def afclGetsClassBytes(): Unit = { + val (fuzz, booz) = fuzzBuzzBooz + booz writeContent "hello, world" + val x = new AbstractFileClassLoader(fuzz, NoClassLoader) + val b = x.classBytes("buzz/booz.class") + assertEquals("hello, world", new String(b, UTF8.charSet)) + } + + @Test + def afclGetsClassBytesFromParent(): Unit = { + val (fuzz, booz) = fuzzBuzzBooz + val (fuzz_, booz_) = fuzzBuzzBooz + booz writeContent "hello, world" + booz_ writeContent "hello, world_" + + val p = new AbstractFileClassLoader(fuzz, NoClassLoader) + val x = new AbstractFileClassLoader(fuzz_, p) + val b = x.classBytes("buzz/booz.class") + assertEquals("hello, world", new String(b, UTF8.charSet)) + } +} -- cgit v1.2.3