Load all resources in RuntimePackagePartProvider; optimize code
The issue was reproducible when the same package is present in different modules with the same -module-name (which is a popular case of src/test roots in simple IDEA projects). The problem was in the fact that several resource files containing package name mapping with the same name were present in the classpath, but RuntimePackagePartProvider only considered the first one. The fix is to use getResources instead of getResourceAsStream and handle each returned resource. Also, optimize internal representation to store the mapping in the form which is the most convenient for findPackageParts, which should be faster than registerModule because in theory, it's called more often. #KT-21973 Fixed #KT-24651
This commit is contained in:
@@ -16,17 +16,42 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class GeneratedClassLoader extends URLClassLoader {
|
||||
private static final URLStreamHandler FAKE_BASE64_URL_HANDLER = new URLStreamHandler() {
|
||||
@Override
|
||||
protected URLConnection openConnection(URL url) {
|
||||
return new URLConnection(url) {
|
||||
@Override
|
||||
public void connect() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return new ByteArrayInputStream(Base64.getDecoder().decode(url.getPath()));
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
private ClassFileFactory factory;
|
||||
|
||||
public GeneratedClassLoader(@NotNull ClassFileFactory factory, ClassLoader parentClassLoader, URL... urls) {
|
||||
@@ -43,6 +68,39 @@ public class GeneratedClassLoader extends URLClassLoader {
|
||||
return super.getResourceAsStream(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> findResources(String name) throws IOException {
|
||||
Enumeration<URL> fromParent = super.findResources(name);
|
||||
|
||||
URL url = createFakeURLForResource(name);
|
||||
if (url == null) return fromParent;
|
||||
|
||||
List<URL> fromMe = Collections.singletonList(url);
|
||||
List<URL> result = fromParent.hasMoreElements()
|
||||
? CollectionsKt.plus(fromMe, Collections.list(fromParent))
|
||||
: fromMe;
|
||||
return Collections.enumeration(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL findResource(String name) {
|
||||
URL url = createFakeURLForResource(name);
|
||||
return url != null ? url : super.findResource(name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private URL createFakeURLForResource(@NotNull String name) {
|
||||
try {
|
||||
OutputFile outputFile = factory.get(name);
|
||||
// Encode the byte array in the URL path to prevent creating unneeded temporary files
|
||||
return outputFile == null
|
||||
? null
|
||||
: new URL(null, "bytes:" + Base64.getEncoder().encodeToString(outputFile.asByteArray()), FAKE_BASE64_URL_HANDLER);
|
||||
} catch (IOException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<?> findClass(@NotNull String name) throws ClassNotFoundException {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
|
||||
// WITH_REFLECT
|
||||
// FILE: A.kt
|
||||
|
||||
fun test() {
|
||||
}
|
||||
|
||||
// FILE: B.kt
|
||||
|
||||
import kotlin.reflect.jvm.javaMethod
|
||||
import kotlin.reflect.jvm.kotlinFunction
|
||||
|
||||
fun box(): String {
|
||||
val r = Class.forName("AKt").methods.single { it.name == "test" }.kotlinFunction
|
||||
if (r?.toString() != "fun test(): kotlin.Unit")
|
||||
return "Fail: $r"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+5
@@ -233,6 +233,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
|
||||
runTest("compiler/testData/compileKotlinAgainstKotlin/recursiveGeneric.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("reflectTopLevelFunctionOtherFile.kt")
|
||||
public void testReflectTopLevelFunctionOtherFile() throws Exception {
|
||||
runTest("compiler/testData/compileKotlinAgainstKotlin/reflectTopLevelFunctionOtherFile.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("sealedClass.kt")
|
||||
public void testSealedClass() throws Exception {
|
||||
runTest("compiler/testData/compileKotlinAgainstKotlin/sealedClass.kt");
|
||||
|
||||
Reference in New Issue
Block a user