Scala REPL (Shell) throws NoClassDefFoundError

Scala version: 2.12.10


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

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```

This issue was referred from a ticket.

The REPLs generally perform two tricks to provide a “seamless” experience: your code is packaged for purposes of managing namespaces or just to put your definitions into a class; and when output is displayed, all evidence of packaging is stripped as mere noise or an implementation detail.

Welcome to Scala 2.13.10 (OpenJDK 64-Bit Server VM, Java 19).
Type in expressions for evaluation. Or try :help.

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

scala> classOf[Person].getName
val res0: String = Person

scala> classOf[Person].getName.toUpperCase
val res1: String = $LINE3.$READ$$IW$PERSON

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

similarly

Welcome to Scala 3.2.1 (19, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

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

scala> classOf[Person].getName
val res0: String = Person

scala> classOf[Person].getName.toUpperCase
val res1: String = RS$LINE$1$PERSON

scala> classOf[Person].getClassLoader.loadClass("rs$line$1$Person")
java.lang.LinkageError: loader dotty.tools.repl.AbstractFileClassLoader @2fa3be26 attempted duplicate class definition for rs$line$1$Person. (rs$line$1$Person is in unnamed module of loader dotty.tools.repl.AbstractFileClassLoader @2fa3be26, parent loader java.net.URLClassLoader @6d868997)
  at java.base/java.lang.ClassLoader.defineClass1(Native Method)
  at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1013)
  at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:875)
  at dotty.tools.repl.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:54)
  at dotty.tools.repl.AbstractFileClassLoader.loadClass(AbstractFileClassLoader.scala:57)
  ... 33 elided

That’s the result I would expect, I’m not sure what Scala 2 does differently.

I think the class loader could support “name translation” in the reflection API, as a convenience.

In general, I think the REPL should provide all the conveniences. For example, it used to make it easier to dump javap output for functions, including anonymous functions. Since the REPL knows the underlying compiler intimately, it can serve as a guide through its labyrinthine ramifications, much like Virgil for Dante.

In Scala 2, you can also use raw paste mode if having a human readable or predictable class name is important for some reason.

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

case class Person(name: String)

// Exiting paste mode, now interpreting.


scala> classOf[Person].getClassLoader.loadClass("Person")
res0: Class[_] = class Person

REPL should allow to create class and load them dynamically, I am trying with Scala 2.12.10 with JDK 11.0.16

Welcome to Scala 2.12.10 (OpenJDK 64-Bit Server VM, Java 11.0.16).
Type in expressions for evaluation. Or try :help.

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

scala> classOf[Person].getName
res0: String = Person

scala> classOf[Person].getName.toUpperCase
res1: String = $LINE3.$READ$$IW$$IW$PERSON

scala> classOf[Person].getClassLoader.loadClass("$LINE3.$READ$$IW$$IW$PERSON")
java.lang.ClassNotFoundException: $LINE3.$READ$$IW$$IW$PERSON
  at scala.reflect.internal.util.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:72)
  at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
  at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
  ... 28 elided

Class loading is case sensitive:

scala> classOf[Person].getClassLoader.loadClass(classOf[Person].getName)
res8: Class[_] = class Person

scala> println(res8)
class $line2.$read$$iw$$iw$Person

The reason that you see res8: Class[_] = class Person is that the REPL tries to hide those $line2.$read$$iw$$iw$ internals from the user.

Thanks

Actually it was an easy fix, REPL already uses a classloader that translates the funny name, but defineClass complains if the name in the class data doesn’t match the name you think it should be.

2 Likes