1- package scala . tools . repl
1+ package dotty . embedded
22
33import java .net .{URL , URLClassLoader }
44import java .io .InputStream
55
66/**
77 * A classloader that remaps shaded classes back to their original package names.
8- *
9- * This classloader intercepts class loading requests and remaps them from
10- * dotty.tools.repl.shaded.* back to their original package names, allowing the
11- * shaded classes to be loaded as if they were in their original packages.
12- *
13- * The scala.* packages are not shaded, so they pass through normally.
148 */
159class UnshadingClassLoader (parent : ClassLoader ) extends ClassLoader (parent) {
1610
17- private val SHADED_PREFIX = " dotty.isolated."
11+ // dotty.isolated classes are loaded only within the REPL impl classloader.
12+ // They exist in the enclosing classpath relocated within the dotty.isolated
13+ // package, but are relocated to their proper package when the REPL impl
14+ // classloader loads them
15+ private val ISOLATED_PREFIX = " dotty.isolated."
1816
1917 override def loadClass (name : String , resolve : Boolean ): Class [? ] = {
2018 val loaded = findLoadedClass(name)
2119 if (loaded != null ) return loaded
2220
23- val shadedPath = (SHADED_PREFIX + name).replace('.' , '/' ) + " .class"
21+ // dotty.shaded classes are loaded separately between the REPL line classloader
22+ // and the REPL impl classloader, but at the same path because the REPL line
23+ // classloader doesn't tolerate relocating classfiles
24+ val shadedPath = (if (name.startsWith(" dotty.shaded." )) name else ISOLATED_PREFIX + name)
25+ .replace('.' , '/' ) + " .class"
26+
2427 val is0 = scala.util.Try (Option (super .getResourceAsStream(shadedPath))).toOption.flatten
2528
2629 is0 match {
@@ -29,17 +32,22 @@ class UnshadingClassLoader(parent: ClassLoader) extends ClassLoader(parent) {
2932 val bytes = is.readAllBytes()
3033 val clazz = defineClass(name, bytes, 0 , bytes.length)
3134 if (resolve) resolveClass(clazz)
32- return clazz
35+ clazz
3336 } finally is.close()
34- case None => super .loadClass(name, resolve)
37+ case None =>
38+ // These classes are loaded shared between all classloaders, because
39+ // they misbehave if loaded multiple times in separate classloaders
40+ if (name.startsWith(" java." ) || name.startsWith(" org.jline." )) parent.loadClass(name)
41+ // Other classes loaded by the `UnshadingClassLoader` *must* be found in the
42+ // `dotty.isolated` package. If they're not there, throw an error rather than
43+ // trying to look for them at their normal package path, to ensure we're not
44+ // accidentally pulling stuff in from the enclosing classloader
45+ else throw new ClassNotFoundException (name)
3546 }
3647 }
3748
3849 override def getResourceAsStream (name : String ): InputStream | Null = {
39- val shadedPath = SHADED_PREFIX .replace('.' , '/' ) + name
40- val shadedStream = super .getResourceAsStream(shadedPath)
41- if (shadedStream != null ) return shadedStream
42- else super .getResourceAsStream(name)
50+ super .getResourceAsStream(ISOLATED_PREFIX .replace('.' , '/' ) + name)
4351 }
4452}
4553
0 commit comments