scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
230 stars 21 forks source link

Scala REPL (Shell) throws NoClassDefFoundError #12705

Closed sethiaarun closed 1 year ago

sethiaarun commented 1 year ago

Reproduction steps

Scala version: 2.12.10

case class Person(name:String)

Thread.currentThread.getContextClassLoader
Thread.currentThread.getContextClassLoader.getParent
classOf[Person].getClassLoader
classOf[Person].getClassLoader.loadClass("Person")

Problem

Running this code from Scala REPL (Shell) throws NoClassDefFoundError

java.lang.NoClassDefFoundError: Person (wrong name: Person)
  at java.base/java.lang.ClassLoader.defineClass1(Native Method)
  at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
  at scala.reflect.internal.util.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:74)
  at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
  at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
  ... 28 elided
SethTisue commented 1 year ago

This isn't a bug. Please use https://users.scala-lang.org for questions, or see https://scala-lang.org/community for other options.

som-snytt commented 1 year ago

dotty says

scala> classOf[Person].getClassLoader.loadClass("Person")
java.lang.ClassNotFoundException: Person
  at dotty.tools.repl.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:51)
  at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
  at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
  at dotty.tools.repl.AbstractFileClassLoader.loadClass(AbstractFileClassLoader.scala:57)
  ... 33 elided

java.lang.NoClassDefFoundError: Person (wrong name: Person) is interesting but not sure about the implication for java reflection.

SethTisue commented 1 year ago

discussion: https://users.scala-lang.org/t/scala-repl-shell-throws-noclassdeffounderror/9035

som-snytt commented 1 year ago

Actually "it should just work".

scala> case class Person(name: String)
class Person

scala> $intp.classLoader.getResource("Person.class")
val res0: java.net.URL = memory:(memory)/Person.class

scala> $intp.classLoader.classBytes("Person")
val res1: Array[Byte] = Array(-54, -2, -70, -66, 0, 0, 0, 52, 0, 111, 1, 0, 23, 36, 108, 105, 110, 101, 51, 47, 36, 114, 101, 97, 100, 36, 36, 105, 119, 36, 80, 101, 114, 115, 111, 110, 7, 0, 1, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 7, 0, 3, 1, 0, 13, 115, 99, 97, 108, 97, 47, 80, 114, 111, 100, 117, 99, 116, 7, 0, 5, 1, 0, 20, 106, 97, 118, 97, 47, 105, 111, 47, 83, 101, 114, 105, 97, 108, 105, 122, 97, 98, 108, 101, 7, 0, 7, 1, 0, 9, 60, 99, 111, 110, 115, 111, 108, 101, 62, 1, 0, 16, 36, 108, 105, 110, 101, 51, 47, 36, 114, 101, 97, 100, 36, 36, 105, 119, 7, 0, 10, 1, 0, 12, 36, 108, 105, 110, 101, 51, 47, 36, 114, 101, 97, 100, 7, 0, 12, 1, 0, 3, 36, 105, 119, 1, 0, 6, 80, 101, 114, 115, 111, 110, 1, 0, 4, 110, 97...

scala> $intp.classLoader.loadClass("Person")
java.lang.NoClassDefFoundError: Person (wrong name: Person)
  at java.base/java.lang.ClassLoader.defineClass1(Native Method)
  at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1013)
  at scala.reflect.internal.util.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:77)
  at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
  at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
  ... 30 elided

The problem is that it goes to defineClass and supplies the name, which doesn't match the "translated" name in the class file data.

SethTisue commented 1 year ago

note that this works as expected:

scala 2.13.10> case class Person(name:String)
class Person

scala 2.13.10> println(classOf[Person])
class $line7.$read$$iw$Person

scala 2.13.10> classOf[Person].getClassLoader.loadClass("$line3.$read$$iw$Person")
val res4: Class[_] = class Person

I don't understand why you think .loadClass("Person") should work, or how it could be made to work?

som-snytt commented 1 year ago

See the PR. It finds the class data, but the name doesn't match. (This is the point of the TranslatingClassLoader. I did not check whether the behavior changed at some point; there are other JDK version issues for AbstractFileClassLoader to address, too.)