Load compiler dependencies in a separate class loader in IDE compiler runner

To be able to invoke the intermediate compiler built on the first step of
bootstrap: in contrast to the distributed compiler, it's incompatible with the
runtime produced by itself and should instead be run with the runtime in the
classpath which was used to compile it.

Drop the non-preloader mode of running compiler, because it would require using
a special parent-last class loader with exceptions specified by ClassCondition
This commit is contained in:
Alexander Udalov
2014-12-15 13:08:42 +03:00
parent 45c6d486fc
commit 7de9a3f029
2 changed files with 41 additions and 27 deletions
@@ -17,12 +17,15 @@
package org.jetbrains.jet.compiler.runner; package org.jetbrains.jet.compiler.runner;
import com.intellij.util.Function; import com.intellij.util.Function;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.cli.common.messages.MessageCollector; import org.jetbrains.jet.cli.common.messages.MessageCollector;
import org.jetbrains.jet.preloading.ClassCondition; import org.jetbrains.jet.preloading.ClassCondition;
import org.jetbrains.jet.preloading.ClassPreloadingUtils; import org.jetbrains.jet.preloading.ClassPreloadingUtils;
import org.jetbrains.jet.utils.KotlinPaths; import org.jetbrains.jet.utils.KotlinPaths;
import org.jetbrains.jet.utils.UtilsPackage;
import java.io.*; import java.io.*;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
@@ -30,8 +33,10 @@ import java.lang.reflect.Method;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.Arrays; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION; import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION;
import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERROR; import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERROR;
@@ -55,32 +60,45 @@ public class CompilerRunnerUtil {
} }
@NotNull @NotNull
private static List<File> compilerClasspath(@NotNull File libs) { private static File compilerJar(@NotNull File libPath) {
return Arrays.asList( return new File(libPath, "kotlin-compiler.jar");
new File(libs, "kotlin-compiler.jar"),
new File(libs, "kotlin-runtime.jar")
);
} }
@NotNull @NotNull
private static ClassLoader createPreloader( private static String loadCompilerClasspathSpaceSeparated(@NotNull File libPath) throws IOException {
@NotNull File libPath, JarFile jar = new JarFile(compilerJar(libPath));
try {
return (String) jar.getManifest().getMainAttributes().get(Attributes.Name.CLASS_PATH);
}
finally {
jar.close();
}
}
@NotNull
private static ClassLoader createClassLoader(
@NotNull final File libPath,
@Nullable ClassLoader parentClassLoader, @Nullable ClassLoader parentClassLoader,
@Nullable ClassCondition classToLoadByParent @Nullable ClassCondition classToLoadByParent
) throws IOException { ) throws IOException {
return ClassPreloadingUtils.preloadClasses( List<URL> classpath = KotlinPackage.map(loadCompilerClasspathSpaceSeparated(libPath).split(" "), new Function1<String, URL>() {
compilerClasspath(libPath), /* estimatedClassNumber = */ 4096, parentClassLoader, classToLoadByParent @Override
); public URL invoke(String dependency) {
} try {
return new File(libPath, dependency).toURI().toURL();
}
catch (MalformedURLException e) {
throw UtilsPackage.rethrow(e);
}
}
});
@NotNull return ClassPreloadingUtils.preloadClasses(
private static URLClassLoader createClassLoader(@NotNull File libPath) throws MalformedURLException { Collections.singletonList(compilerJar(libPath)),
List<File> jars = compilerClasspath(libPath); /* estimatedClassNumber = */ 4096,
URL[] urls = new URL[jars.size()]; new URLClassLoader(classpath.toArray(new URL[classpath.size()]), parentClassLoader),
for (int i = 0; i < urls.length; i++) { classToLoadByParent
urls[i] = jars.get(i).toURI().toURL(); );
}
return new URLClassLoader(urls, null);
} }
private static void handleProcessTermination(int exitCode, @NotNull MessageCollector messageCollector) { private static void handleProcessTermination(int exitCode, @NotNull MessageCollector messageCollector) {
@@ -107,17 +125,14 @@ public class CompilerRunnerUtil {
@NotNull String[] arguments, @NotNull String[] arguments,
@NotNull CompilerEnvironment environment, @NotNull CompilerEnvironment environment,
@NotNull MessageCollector messageCollector, @NotNull MessageCollector messageCollector,
@NotNull PrintStream out, @NotNull PrintStream out
boolean usePreloader
) throws Exception { ) throws Exception {
File libPath = getLibPath(environment.getKotlinPaths(), messageCollector); File libPath = getLibPath(environment.getKotlinPaths(), messageCollector);
if (libPath == null) return null; if (libPath == null) return null;
ClassLoader classLoader = ourClassLoaderRef.get(); ClassLoader classLoader = ourClassLoaderRef.get();
if (classLoader == null) { if (classLoader == null) {
classLoader = usePreloader classLoader = createClassLoader(libPath, environment.getParentClassLoader(), environment.getClassesToLoadByParent());
? createPreloader(libPath, environment.getParentClassLoader(), environment.getClassesToLoadByParent())
: createClassLoader(libPath);
ourClassLoaderRef = new SoftReference<ClassLoader>(classLoader); ourClassLoaderRef = new SoftReference<ClassLoader>(classLoader);
} }
@@ -108,8 +108,7 @@ public class KotlinCompilerRunner {
"Using kotlin-home = " + environment.getKotlinPaths().getHomePath(), "Using kotlin-home = " + environment.getKotlinPaths().getHomePath(),
CompilerMessageLocation.NO_LOCATION); CompilerMessageLocation.NO_LOCATION);
Object rc = CompilerRunnerUtil.invokeExecMethod(compilerClassName, arguments, environment, Object rc = CompilerRunnerUtil.invokeExecMethod(compilerClassName, arguments, environment, messageCollector, out);
messageCollector, out, /*usePreloader=*/true);
// exec() returns a K2JVMCompiler.ExitCode object, that class is not accessible here, // exec() returns a K2JVMCompiler.ExitCode object, that class is not accessible here,
// so we take it's contents through reflection // so we take it's contents through reflection
return CompilerRunnerUtil.getReturnCodeFromObject(rc); return CompilerRunnerUtil.getReturnCodeFromObject(rc);