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:
Alexander Udalov
2018-06-29 17:36:08 +02:00
parent 8ccbbf71ec
commit 4f2fdd1c01
4 changed files with 119 additions and 14 deletions
@@ -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 {