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);
+ }
+}