diff --git a/.idea/modules.xml b/.idea/modules.xml index 31cba86fb8f..687c7133d79 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -26,6 +26,7 @@ + diff --git a/build.xml b/build.xml index 0842b187274..4aa7016872d 100644 --- a/build.xml +++ b/build.xml @@ -56,6 +56,10 @@ + + + + @@ -236,15 +240,36 @@ + + + + + + + + + + + + + + + + + + + + + - + - + @@ -252,14 +277,14 @@ - + - + - + @@ -517,13 +542,13 @@ + depends="clean,init,prepareDist,compileGenerators,invokeGenerators,preloader,compiler,compilerSources,antTools,jdkAnnotations,annotationsExt,runtime,jslib,j2kConverter"/> + depends="clean,init,prepareDist,compileGenerators,invokeGenerators,preloader,compiler_quick,antTools,jdkAnnotations,annotationsExt,runtime,jslib,j2kConverter"/> + depends="init,prepareDist,preloader,compiler_quick"/> diff --git a/compiler/cli/bin/kotlinc-js b/compiler/cli/bin/kotlinc-js index c209a56a758..cc2129bf160 100755 --- a/compiler/cli/bin/kotlinc-js +++ b/compiler/cli/bin/kotlinc-js @@ -81,5 +81,5 @@ CPSELECT="-cp " "${JAVACMD:=java}" \ $JAVA_OPTS \ "${java_args[@]}" \ - ${CPSELECT}"${KOTLIN_HOME}/lib/kotlin-compiler.jar" \ - org.jetbrains.jet.cli.js.K2JSCompiler "$@" + ${CPSELECT}"${KOTLIN_HOME}/lib/kotlin-preloader.jar" \ + org.jetbrains.jet.preloading.Preloader "${KOTLIN_HOME}/lib/kotlin-compiler.jar" org.jetbrains.jet.cli.js.K2JSCompiler 4096 "$@" diff --git a/compiler/cli/bin/kotlinc-js.bat b/compiler/cli/bin/kotlinc-js.bat index 6532412b8ed..fef3bb6bcc2 100644 --- a/compiler/cli/bin/kotlinc-js.bat +++ b/compiler/cli/bin/kotlinc-js.bat @@ -21,7 +21,9 @@ if "%_JAVACMD%"=="" set _JAVACMD=java rem We use the value of the JAVA_OPTS environment variable if defined set _JAVA_OPTS=-Xmx256M -Xms32M -noverify -"%_JAVACMD%" %_JAVA_OPTS% -cp "%_KOTLIN_HOME%\lib\kotlin-compiler.jar" org.jetbrains.jet.cli.js.K2JSCompiler %* +"%_JAVACMD%" %_JAVA_OPTS% -cp "%_KOTLIN_HOME%\lib\kotlin-preloader.jar" ^ + org.jetbrains.jet.preloading.Preloader "${KOTLIN_HOME}/lib/kotlin-compiler.jar" ^ + org.jetbrains.jet.cli.jvm.K2JVMCompiler 4096 org.jetbrains.jet.cli.js.K2JSCompiler %* goto end rem ########################################################################## diff --git a/compiler/cli/bin/kotlinc-jvm b/compiler/cli/bin/kotlinc-jvm index e21fc09c9ae..c837ecb6cb5 100755 --- a/compiler/cli/bin/kotlinc-jvm +++ b/compiler/cli/bin/kotlinc-jvm @@ -81,5 +81,5 @@ CPSELECT="-cp " "${JAVACMD:=java}" \ $JAVA_OPTS \ "${java_args[@]}" \ - ${CPSELECT}"${KOTLIN_HOME}/lib/kotlin-compiler.jar" \ - org.jetbrains.jet.cli.jvm.K2JVMCompiler "$@" + ${CPSELECT}"${KOTLIN_HOME}/lib/kotlin-preloader.jar" \ + org.jetbrains.jet.preloading.Preloader "${KOTLIN_HOME}/lib/kotlin-compiler.jar" org.jetbrains.jet.cli.jvm.K2JVMCompiler 4096 "$@" diff --git a/compiler/cli/bin/kotlinc-jvm.bat b/compiler/cli/bin/kotlinc-jvm.bat index 87dab3a05ac..3949f6f5797 100644 --- a/compiler/cli/bin/kotlinc-jvm.bat +++ b/compiler/cli/bin/kotlinc-jvm.bat @@ -21,7 +21,9 @@ if "%_JAVACMD%"=="" set _JAVACMD=java rem We use the value of the JAVA_OPTS environment variable if defined set _JAVA_OPTS=-Xmx256M -Xms32M -noverify -"%_JAVACMD%" %_JAVA_OPTS% -cp "%_KOTLIN_HOME%\lib\kotlin-compiler.jar" org.jetbrains.jet.cli.jvm.K2JVMCompiler %* +"%_JAVACMD%" %_JAVA_OPTS% -cp "%_KOTLIN_HOME%\lib\kotlin-preloader.jar" ^ + org.jetbrains.jet.preloading.Preloader "${KOTLIN_HOME}/lib/kotlin-compiler.jar" ^ + org.jetbrains.jet.cli.jvm.K2JVMCompiler 4096 org.jetbrains.jet.cli.jvm.K2JVMCompiler %* goto end rem ########################################################################## diff --git a/compiler/preloader/preloader.iml b/compiler/preloader/preloader.iml new file mode 100644 index 00000000000..d5c07432750 --- /dev/null +++ b/compiler/preloader/preloader.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java b/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java new file mode 100644 index 00000000000..9323890f067 --- /dev/null +++ b/compiler/preloader/src/org/jetbrains/jet/preloading/ClassPreloadingUtils.java @@ -0,0 +1,153 @@ +package org.jetbrains.jet.preloading;/* + * Copyright 2010-2013 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. + */ + +import sun.misc.CompoundEnumeration; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class ClassPreloadingUtils { + + /** + * Creates a class loader that loads all classes from {@code jarFile} into memory to make loading faster (avoid skipping through zip archives). + * @param jarFile a jar to load all classes from + * @param classCountEstimation an estimated number of classes in a the jar + * @param parent (nullable) parent class loader + * @return a class loader that reads classes from memory + * @throws IOException on from reading the jar + */ + public static ClassLoader preloadClasses(File jarFile, int classCountEstimation, ClassLoader parent) throws IOException { + Map entries = loadAllClassesFromJar(jarFile, classCountEstimation); + + return createMemoryBasedClassLoader(parent, jarFile, entries); + } + + private static ClassLoader createMemoryBasedClassLoader( + final ClassLoader parent, + final File jarFile, + final Map preloadedResources + ) { + return new ClassLoader(null) { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + // Look in this class loader and then in the parent one + Class aClass = super.loadClass(name); + if (aClass == null) { + return parent.loadClass(name); + } + return aClass; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + String internalName = name.replace('.', '/').concat(".class"); + byte[] bytes = preloadedResources.get(internalName); + if (bytes == null) return null; + + return defineClass(name, bytes, 0, bytes.length); + } + + @Override + protected URL findResource(String name) { + final byte[] bytes = preloadedResources.get(name); + if (bytes == null) return null; + + try { + String path = "file:" + jarFile + "!" + name; + return new URL("jar", null, 0, path, new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + return new URLConnection(u) { + @Override + public void connect() throws IOException {} + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(bytes); + } + }; + } + }); + } + catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } + + @Override + protected Enumeration findResources(String name) throws IOException { + URL resource = findResource(name); + if (resource == null) { + return new CompoundEnumeration(new Enumeration[0]); + } + return Collections.enumeration(Collections.singletonList(resource)); + } + }; + } + + private static Map loadAllClassesFromJar(File jarFile, int classNumberEstimate) throws IOException { + Map classes = new HashMap(classNumberEstimate); + + FileInputStream fileInputStream = new FileInputStream(jarFile); + try { + byte[] buffer = new byte[10 * 1024]; + ZipInputStream stream = new ZipInputStream(new BufferedInputStream(fileInputStream)); + while (true) { + ZipEntry entry = stream.getNextEntry(); + if (entry == null) break; + + ByteArrayOutputStreamWithPublicArray bytes = new ByteArrayOutputStreamWithPublicArray((int) entry.getSize()); + int count; + while ((count = stream.read(buffer)) > 0) { + bytes.write(buffer, 0, count); + } + if (!entry.isDirectory()) { + classes.put(entry.getName(), bytes.getBytes()); + } + } + } + finally { + try { + fileInputStream.close(); + } + catch (IOException e) { + // Ignore + } + } + return classes; + } + + private static class ByteArrayOutputStreamWithPublicArray extends ByteArrayOutputStream { + public ByteArrayOutputStreamWithPublicArray(int size) { + super(size); + } + + // To avoid copying the array + public byte[] getBytes() { + return buf; + } + } +} diff --git a/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java b/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java new file mode 100644 index 00000000000..d2cb3a1ab9e --- /dev/null +++ b/compiler/preloader/src/org/jetbrains/jet/preloading/Preloader.java @@ -0,0 +1,46 @@ +package org.jetbrains.jet.preloading; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.Arrays; + +public class Preloader { + + public static void main(String[] args) throws Exception { + if (args.length < 3) { + printUsageAndExit(); + } + + File file = new File(args[0]); + if (!file.exists()) { + System.out.println("File does not exist: " + file); + printUsageAndExit(); + } + + String mainClassCanonicalName = args[1]; + + int classNumber; + try { + classNumber = Integer.parseInt(args[2]); + } + catch (NumberFormatException e) { + System.out.println(e.getMessage()); + printUsageAndExit(); + return; + } + + ClassLoader parent = Preloader.class.getClassLoader(); + + ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(file, classNumber, parent); + + Class mainClass = preloaded.loadClass(mainClassCanonicalName); + Method mainMethod = mainClass.getMethod("main", String[].class); + + mainMethod.invoke(0, new Object[] {Arrays.copyOfRange(args, 2, args.length)}); + } + + private static void printUsageAndExit() { + System.out.println("Usage: Preloader
"); + System.exit(1); + } +}