diff --git a/compiler/preloader/src/org/jetbrains/jet/preloading/ClassLoaderFactory.java b/compiler/preloader/src/org/jetbrains/jet/preloading/ClassLoaderFactory.java new file mode 100644 index 00000000000..6847b9828c3 --- /dev/null +++ b/compiler/preloader/src/org/jetbrains/jet/preloading/ClassLoaderFactory.java @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.preloading; + +public interface ClassLoaderFactory { + ClassLoader create(ClassLoader parameter); +} diff --git a/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java b/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java index 5384309afc2..887612f3f2e 100644 --- a/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java +++ b/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java @@ -48,31 +48,33 @@ public class ClassPreloadingUtils { * * @param jarFiles jars to load all classes from * @param classCountEstimation an estimated number of classes in a the jars - * @param parent (nullable) parent class loader + * @param parentFactory (nullable) factory for creating parent class loader, passing preloader's as parameter * @param handler handler to be notified on class definitions done by this class loader, or null * @return a class loader that reads classes from memory * @throws IOException on from reading the jar */ public static ClassLoader preloadClasses( - Collection jarFiles, int classCountEstimation, ClassLoader parent, ClassHandler handler + Collection jarFiles, int classCountEstimation, ClassLoaderFactory parentFactory, ClassHandler handler ) throws IOException { Map entries = loadAllClassesFromJars(jarFiles, classCountEstimation, handler); - return createMemoryBasedClassLoader(parent, entries, handler); + return createMemoryBasedClassLoader(parentFactory, entries, handler); } public static ClassLoader preloadClasses( - Collection jarFiles, int classCountEstimation, ClassLoader parent + Collection jarFiles, int classCountEstimation, ClassLoaderFactory parentFactory ) throws IOException { - return preloadClasses(jarFiles, classCountEstimation, parent, null); + return preloadClasses(jarFiles, classCountEstimation, parentFactory, null); } private static ClassLoader createMemoryBasedClassLoader( - final ClassLoader parent, + final ClassLoaderFactory parentFactory, final Map preloadedResources, final ClassHandler handler ) { return new ClassLoader(null) { + private final ClassLoader parent = parentFactory == null ? null : parentFactory.create(this); + @Override public Class loadClass(String name) throws ClassNotFoundException { // Look in this class loader and then in the parent one @@ -106,6 +108,15 @@ public class ClassPreloadingUtils { return definedClass; } + @Override + public URL getResource(String name) { + URL resource = super.getResource(name); + if (resource == null && parent != null) { + return parent.getResource(name); + } + return resource; + } + @Override protected URL findResource(String name) { ResourceData resourceData = preloadedResources.get(name); @@ -113,6 +124,15 @@ public class ClassPreloadingUtils { return resourceData.getURL(); } + @Override + public Enumeration getResources(String name) throws IOException { + Enumeration resources = super.getResources(name); + if (!resources.hasMoreElements() && parent != null) { + return parent.getResources(name); + } + return resources; + } + @Override protected Enumeration findResources(String name) throws IOException { URL resource = findResource(name); diff --git a/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java b/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java index ca9a546d3b4..fa15040b241 100644 --- a/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java +++ b/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java @@ -57,10 +57,15 @@ public class Preloader { ClassLoader parent = Preloader.class.getClassLoader(); - ClassLoader withInstrumenter = instrumentersClasspath.length > 0 ? new URLClassLoader(instrumentersClasspath, parent) : parent; + final ClassLoader withInstrumenter = instrumentersClasspath.length > 0 ? new URLClassLoader(instrumentersClasspath, parent) : parent; final Handler handler = getHandler(mode, withInstrumenter); - ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(files, classNumber, withInstrumenter, handler); + ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(files, classNumber, new ClassLoaderFactory() { + @Override + public ClassLoader create(ClassLoader parameter) { + return withInstrumenter; + } + }, handler); Class mainClass = preloaded.loadClass(mainClassCanonicalName); Method mainMethod = mainClass.getMethod("main", String[].class); diff --git a/ide-compiler-runner/ide-compiler-runner.iml b/ide-compiler-runner/ide-compiler-runner.iml index 2526e0c840b..8fbeffb3f24 100644 --- a/ide-compiler-runner/ide-compiler-runner.iml +++ b/ide-compiler-runner/ide-compiler-runner.iml @@ -10,7 +10,7 @@ - + diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java index a633c4f5f24..cab1efc79c9 100644 --- a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerEnvironment.java @@ -19,6 +19,7 @@ package org.jetbrains.jet.compiler.runner; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.preloading.ClassLoaderFactory; import org.jetbrains.jet.utils.KotlinPaths; import org.jetbrains.jet.utils.PathUtil; @@ -29,17 +30,25 @@ import static org.jetbrains.jet.cli.common.messages.CompilerMessageSeverity.ERRO public final class CompilerEnvironment { - public static CompilerEnvironment getEnvironmentFor(@NotNull KotlinPaths kotlinPaths, @Nullable File outputDir) { - return new CompilerEnvironment(kotlinPaths, outputDir); + public static CompilerEnvironment getEnvironmentFor( + @NotNull KotlinPaths kotlinPaths, + @Nullable File outputDir, + @Nullable ClassLoaderFactory parentFactory + ) { + return new CompilerEnvironment(kotlinPaths, outputDir, parentFactory); } + @NotNull private final KotlinPaths kotlinPaths; @Nullable private final File output; + @Nullable + private final ClassLoaderFactory parentFactory; - private CompilerEnvironment(@NotNull KotlinPaths kotlinPaths, @Nullable File output) { + private CompilerEnvironment(@NotNull KotlinPaths kotlinPaths, @Nullable File output, @Nullable ClassLoaderFactory parentFactory) { this.kotlinPaths = kotlinPaths; this.output = output; + this.parentFactory = parentFactory; } public boolean success() { @@ -57,6 +66,11 @@ public final class CompilerEnvironment { return output; } + @Nullable + public ClassLoaderFactory getParentFactory() { + return parentFactory; + } + public void reportErrorsTo(@NotNull MessageCollector messageCollector) { if (output == null) { messageCollector.report(ERROR, "[Internal Error] No output directory", NO_LOCATION); @@ -66,5 +80,4 @@ public final class CompilerEnvironment { "or specify " + PathUtil.JPS_KOTLIN_HOME_PROPERTY + " system property", NO_LOCATION); } } - } diff --git a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerRunnerUtil.java b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerRunnerUtil.java index cdb9b43e7cc..4244b45ab54 100644 --- a/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerRunnerUtil.java +++ b/ide-compiler-runner/src/org/jetbrains/jet/compiler/runner/CompilerRunnerUtil.java @@ -17,8 +17,11 @@ package org.jetbrains.jet.compiler.runner; import com.intellij.util.Function; +import kotlin.Function1; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.preloading.ClassLoaderFactory; import org.jetbrains.jet.preloading.ClassPreloadingUtils; import org.jetbrains.jet.utils.KotlinPaths; @@ -54,12 +57,17 @@ public class CompilerRunnerUtil { return answer; } - public static ClassLoader getOrCreatePreloader(KotlinPaths paths, MessageCollector messageCollector) { + @NotNull + public static ClassLoader getOrCreatePreloader( + @NotNull KotlinPaths paths, + @Nullable ClassLoaderFactory parentFactory, + @NotNull MessageCollector messageCollector + ) { ClassLoader answer = ourClassLoaderRef.get(); if (answer == null) { try { int estimatedClassNumber = 4096; - answer = ClassPreloadingUtils.preloadClasses(kompilerClasspath(paths, messageCollector), estimatedClassNumber, null); + answer = ClassPreloadingUtils.preloadClasses(kompilerClasspath(paths, messageCollector), estimatedClassNumber, parentFactory); } catch (IOException e) { throw new RuntimeException(e); @@ -112,7 +120,7 @@ public class CompilerRunnerUtil { MessageCollector messageCollector, PrintStream out, boolean usePreloader ) throws Exception { ClassLoader loader = usePreloader - ? getOrCreatePreloader(environment.getKotlinPaths(), messageCollector) + ? getOrCreatePreloader(environment.getKotlinPaths(), environment.getParentFactory(), messageCollector) : getOrCreateClassLoader(environment.getKotlinPaths(), messageCollector); Class kompiler = Class.forName(compilerClassName, true, loader); diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/TranslatingCompilerUtils.java b/idea/src/org/jetbrains/jet/plugin/compiler/TranslatingCompilerUtils.java index a29b55f389e..cc6a4abda7f 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/TranslatingCompilerUtils.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/TranslatingCompilerUtils.java @@ -46,7 +46,7 @@ public final class TranslatingCompilerUtils { VirtualFile outputDirectoryForTests = compileContext.getModuleOutputDirectoryForTests(module); File outputDir = tests ? toNullableIoFile(outputDirectoryForTests) : toNullableIoFile(mainOutput); KotlinPaths kotlinPaths = PathUtil.getKotlinPathsForIdeaPlugin(); - return CompilerEnvironment.getEnvironmentFor(kotlinPaths, outputDir); + return CompilerEnvironment.getEnvironmentFor(kotlinPaths, outputDir, null); } @Nullable diff --git a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java index da5842fa0f1..ef37d49ae46 100644 --- a/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java +++ b/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.jps.build; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -38,6 +39,7 @@ import org.jetbrains.jet.compiler.runner.SimpleOutputItem; import org.jetbrains.jet.config.IncrementalCompilation; import org.jetbrains.jet.jps.JpsKotlinCompilerSettings; import org.jetbrains.jet.jps.incremental.IncrementalCacheImpl; +import org.jetbrains.jet.preloading.ClassLoaderFactory; import org.jetbrains.jet.utils.PathUtil; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.builders.BuildTarget; @@ -53,8 +55,10 @@ import org.jetbrains.jps.model.module.JpsModule; import java.io.File; import java.io.FileFilter; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.net.URL; import java.util.*; import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION; @@ -114,7 +118,14 @@ public class KotlinBuilder extends ModuleLevelBuilder { File outputDir = representativeTarget.getOutputDir(); - CompilerEnvironment environment = CompilerEnvironment.getEnvironmentFor(PathUtil.getKotlinPathsForJpsPluginOrJpsTests(), outputDir); + CompilerEnvironment environment = CompilerEnvironment.getEnvironmentFor( + PathUtil.getKotlinPathsForJpsPluginOrJpsTests(), outputDir, new ClassLoaderFactory() { + @Override + public ClassLoader create(ClassLoader compilerClassLoader) { + return new MyClassLoader(compilerClassLoader); + } + } + ); if (!environment.success()) { if (!hasKotlinFiles(chunk)) { // Configuration is bad, but there's nothing to compile anyways @@ -374,4 +385,49 @@ public class KotlinBuilder extends ModuleLevelBuilder { public List getCompilableFileExtensions() { return COMPILABLE_FILE_EXTENSIONS; } + + private class MyClassLoader extends ClassLoader { + private final ClassLoader compilerClassLoader; + private final ClassLoader jpsPluginClassLoader = KotlinBuilder.this.getClass().getClassLoader(); + + private MyClassLoader(ClassLoader compilerClassLoader) { + this.compilerClassLoader = compilerClassLoader; + } + + @NotNull + @Override + public Enumeration getResources(String name) throws IOException { + return jpsPluginClassLoader.getResources(name); + } + + @Override + public Class loadClass(@NotNull String name) throws ClassNotFoundException { + if (name.startsWith("org.jetbrains.jet.jps.incremental.")) { + return loadClassFromBytes(name); + } + else if (name.startsWith("org.jetbrains.jet.lang.resolve.kotlin.incremental.")) { + return compilerClassLoader.loadClass(name); + } + else { + return jpsPluginClassLoader.loadClass(name); + } + } + + private Class loadClassFromBytes(String name) throws ClassNotFoundException { + String classResource = name.replace('.', '/') + ".class"; + InputStream resource = jpsPluginClassLoader.getResourceAsStream(classResource); + if (resource == null) { + return null; + } + byte[] bytes; + try { + bytes = StreamUtil.loadFromStream(resource); + } + catch (IOException e) { + throw new ClassNotFoundException("Couldn't load class " + name, e); + } + + return defineClass(name, bytes, 0, bytes.length); + } + } }