Do not use ClassLoader.getResourceAsStream in reflection

Use ClassLoader.getResource + openStream instead, to workaround an issue
in URLClassLoader.

Also set useCaches to false because kotlin-reflect only reads builtins
metadata once per class loader, and doesn't need it to be cached. Using
caches here might also lead to the problem of closed input streams when
protobuf is read in parallel. The test doesn't check exactly this,
though (it seems to succeed even if cached connections are used).

Note that BuiltInsResourceLoader has a JDK 9+ specialization at
libraries/reflect/api/src/java9, but that implementation does not need
any changes because it uses Module.getResourceAsStream which is not
affected by this issue in URLClassLoader.

 #KT-18277 Fixed
This commit is contained in:
Alexander Udalov
2021-06-02 15:47:22 +02:00
parent a0503aa2d7
commit c1021623e8
2 changed files with 53 additions and 2 deletions
@@ -9,7 +9,12 @@ import java.io.InputStream
class BuiltInsResourceLoader {
fun loadResource(path: String): InputStream? {
return this::class.java.classLoader?.getResourceAsStream(path)
?: ClassLoader.getSystemResourceAsStream(path)
val classLoader = this::class.java.classLoader ?: return ClassLoader.getSystemResourceAsStream(path)
// Do not use getResourceAsStream because URLClassLoader's implementation creates InputStream instances which refer to
// a globally cached JarFile instance, which is closed as soon as URLClassLoader is closed, which instantly invalidates all
// input streams referring to that JarFile and breaks kotlin-reflect in case it's used from different class loaders.
val resource = classLoader.getResource(path) ?: return null
return resource.openConnection().apply { useCaches = false }.getInputStream()
}
}