diff --git a/ant/src/org/jetbrains/kotlin/ant/KotlinAntTaskUtil.kt b/ant/src/org/jetbrains/kotlin/ant/KotlinAntTaskUtil.kt
index 57108c33a85..7a4d993f8b9 100644
--- a/ant/src/org/jetbrains/kotlin/ant/KotlinAntTaskUtil.kt
+++ b/ant/src/org/jetbrains/kotlin/ant/KotlinAntTaskUtil.kt
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.ant
import org.apache.tools.ant.AntClassLoader
import org.jetbrains.kotlin.preloading.ClassPreloadingUtils
+import org.jetbrains.kotlin.preloading.Preloader
import java.io.File
import java.lang.ref.SoftReference
import java.net.JarURLConnection
@@ -54,10 +55,10 @@ object KotlinAntTaskUtil {
val cached = classLoaderRef.get()
if (cached != null) return cached
- val myLoader = javaClass.getClassLoader()
+ val myLoader = javaClass.classLoader
if (myLoader !is AntClassLoader) return myLoader
- val classLoader = ClassPreloadingUtils.preloadClasses(listOf(compilerJar), 4096, myLoader, null)
+ val classLoader = ClassPreloadingUtils.preloadClasses(listOf(compilerJar), Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE, myLoader, null)
classLoaderRef = SoftReference(classLoader)
return classLoader
diff --git a/build.xml b/build.xml
index f3478978e65..2549381d9e6 100644
--- a/build.xml
+++ b/build.xml
@@ -278,9 +278,9 @@
+
-
@@ -415,6 +415,7 @@
+
@@ -646,9 +647,9 @@
+
-
diff --git a/compiler/cli/bin/kotlinc b/compiler/cli/bin/kotlinc
index fcc15150836..64a275e304a 100755
--- a/compiler/cli/bin/kotlinc
+++ b/compiler/cli/bin/kotlinc
@@ -71,5 +71,5 @@ fi
"${java_args[@]}" \
-cp "${KOTLIN_HOME}/lib/kotlin-preloader.jar" \
org.jetbrains.kotlin.preloading.Preloader \
- "${KOTLIN_HOME}/lib/kotlin-compiler.jar" \
- $KOTLIN_COMPILER 4096 notime "$@"
+ -cp "${KOTLIN_HOME}/lib/kotlin-compiler.jar" \
+ $KOTLIN_COMPILER "$@"
diff --git a/compiler/cli/bin/kotlinc.bat b/compiler/cli/bin/kotlinc.bat
index 820d634ace1..c1ae05d10c6 100644
--- a/compiler/cli/bin/kotlinc.bat
+++ b/compiler/cli/bin/kotlinc.bat
@@ -28,8 +28,8 @@ 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-preloader.jar" ^
- org.jetbrains.kotlin.preloading.Preloader "%_KOTLIN_HOME%\lib\kotlin-compiler.jar" ^
- %KOTLIN_COMPILER% 4096 notime %*
+ org.jetbrains.kotlin.preloading.Preloader -cp "%_KOTLIN_HOME%\lib\kotlin-compiler.jar" ^
+ %KOTLIN_COMPILER% %*
exit /b %ERRORLEVEL%
goto end
diff --git a/compiler/preloader/src/org/jetbrains/kotlin/preloading/Preloader.java b/compiler/preloader/src/org/jetbrains/kotlin/preloading/Preloader.java
index a1bf06a444c..d22fbb68be1 100644
--- a/compiler/preloader/src/org/jetbrains/kotlin/preloading/Preloader.java
+++ b/compiler/preloader/src/org/jetbrains/kotlin/preloading/Preloader.java
@@ -19,87 +19,150 @@ package org.jetbrains.kotlin.preloading;
import org.jetbrains.kotlin.preloading.instrumentation.Instrumenter;
import java.io.File;
+import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
+@SuppressWarnings({"CallToPrintStackTrace", "UseOfSystemOutOrSystemErr"})
public class Preloader {
-
- public static final int PRELOADER_ARG_COUNT = 4;
- private static final String INSTRUMENT_PREFIX = "instrument=";
+ public static final int DEFAULT_CLASS_NUMBER_ESTIMATE = 4096;
public static void main(String[] args) throws Exception {
- if (args.length < PRELOADER_ARG_COUNT) {
- printUsageAndExit();
- }
-
- List files = parseClassPath(args[0]);
-
- String mainClassCanonicalName = args[1];
-
- int classNumber;
try {
- classNumber = Integer.parseInt(args[2]);
+ run(args);
}
- catch (NumberFormatException e) {
- System.err.println("error: number expected: " + e.getMessage());
- printUsageAndExit();
- return;
+ catch (Throwable e) {
+ System.err.println("error: " + e.toString());
+ e.printStackTrace();
+ System.err.println();
+ printUsage(System.err);
+ System.exit(1);
}
+ }
- final Mode mode = parseMode(args[3]);
-
+ private static void run(String[] args) throws Exception {
final long startTime = System.nanoTime();
- ClassLoader parent = Preloader.class.getClassLoader();
+ final Options options = parseOptions(args);
- ClassLoader withInstrumenter =
- mode instanceof Mode.Instrument ? new URLClassLoader(((Mode.Instrument) mode).classpath, parent) : parent;
+ ClassLoader classLoader = createClassLoader(options);
- final Handler handler = getHandler(mode, withInstrumenter);
- ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(files, classNumber, withInstrumenter, null, handler);
+ final Handler handler = getHandler(options, classLoader);
+ ClassLoader preloaded = ClassPreloadingUtils.preloadClasses(options.classpath, options.estimate, classLoader, null, handler);
- Class> mainClass = preloaded.loadClass(mainClassCanonicalName);
+ Class> mainClass = preloaded.loadClass(options.mainClass);
Method mainMethod = mainClass.getMethod("main", String[].class);
Runtime.getRuntime().addShutdownHook(
new Thread(new Runnable() {
@Override
public void run() {
- if (mode != Mode.NO_TIME) {
+ if (options.measure) {
System.out.println();
System.out.println("=== Preloader's measurements: ");
- long dt = System.nanoTime() - startTime;
- System.out.format("Total time: %.3fs\n", dt / 1e9);
+ System.out.format("Total time: %.3fs\n", (System.nanoTime() - startTime) / 1e9);
}
handler.done();
}
})
);
- mainMethod.invoke(0, new Object[] {Arrays.copyOfRange(args, PRELOADER_ARG_COUNT, args.length)});
+ //noinspection SSBasedInspection
+ mainMethod.invoke(0, (Object) options.arguments.toArray(new String[options.arguments.size()]));
+ }
+
+ private static ClassLoader createClassLoader(Options options) throws MalformedURLException {
+ ClassLoader parent = Preloader.class.getClassLoader();
+
+ List instrumenters = options.instrumenters;
+ if (instrumenters.isEmpty()) return parent;
+
+ URL[] classpath = new URL[instrumenters.size()];
+ for (int i = 0; i < instrumenters.size(); i++) {
+ classpath[i] = instrumenters.get(i).toURI().toURL();
+ }
+
+ return new URLClassLoader(classpath, parent);
+ }
+
+ @SuppressWarnings({"AssignmentToForLoopParameter", "ConstantConditions"})
+ private static Options parseOptions(String[] args) throws Exception {
+ // TODO: remove this temporary workaround as soon as the new command-line interface is bootstrapped
+ if (args.length >= 3 && "4096".equals(args[2])) {
+ //noinspection deprecation
+ return oldOptions(args);
+ }
+
+ List classpath = Collections.emptyList();
+ boolean measure = false;
+ List instrumenters = Collections.emptyList();
+ int estimate = DEFAULT_CLASS_NUMBER_ESTIMATE;
+ String mainClass = null;
+ List arguments = new ArrayList();
+
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ boolean end = i == args.length - 1;
+
+ if ("-help".equals(arg) || "-h".equals(arg)) {
+ printUsage(System.out);
+ System.exit(0);
+ }
+ else if ("-cp".equals(arg) || "-classpath".equals(arg)) {
+ if (end) throw new RuntimeException("no argument provided to " + arg);
+ classpath = parseClassPath(args[++i]);
+ }
+ else if ("-estimate".equals(arg)) {
+ if (end) throw new RuntimeException("no argument provided to " + arg);
+ estimate = Integer.parseInt(args[++i]);
+ }
+ else if ("-instrument".equals(arg)) {
+ if (end) throw new RuntimeException("no argument provided to " + arg);
+ instrumenters = parseClassPath(args[++i]);
+ }
+ else if ("-measure".equals(arg)) {
+ measure = true;
+ }
+ else {
+ mainClass = arg;
+ arguments.addAll(Arrays.asList(args).subList(i + 1, args.length));
+ break;
+ }
+ }
+
+ if (mainClass == null) throw new RuntimeException("no main class name provided");
+
+ return new Options(classpath, measure, instrumenters, estimate, mainClass, arguments);
+ }
+
+ @Deprecated
+ private static Options oldOptions(String[] args) {
+ return new Options(
+ parseClassPath(args[0]), false, Collections.emptyList(), 4096, args[1],
+ Arrays.asList(Arrays.copyOfRange(args, 4, args.length))
+ );
}
private static List parseClassPath(String classpath) {
- String[] paths = classpath.split("\\" + File.pathSeparator);
+ String[] paths = classpath.split(File.pathSeparator);
List files = new ArrayList(paths.length);
for (String path : paths) {
File file = new File(path);
if (!file.exists()) {
- System.err.println("error: file does not exist: " + file);
- printUsageAndExit();
+ throw new RuntimeException("file does not exist: " + file);
}
files.add(file);
}
return files;
}
- private static Handler getHandler(Mode mode, ClassLoader withInstrumenter) {
- if (mode == Mode.NO_TIME) return new Handler();
+ private static Handler getHandler(Options options, ClassLoader withInstrumenter) {
+ if (!options.measure) return new Handler();
- final Instrumenter instrumenter = mode instanceof Mode.Instrument ? loadInstrumenter(withInstrumenter) : Instrumenter.DO_NOTHING;
+ final Instrumenter instrumenter = options.instrumenters.isEmpty() ? Instrumenter.DO_NOTHING : loadInstrumenter(withInstrumenter);
final int[] counter = new int[1];
final int[] size = new int[1];
@@ -143,47 +206,40 @@ public class Preloader {
}
}
- private interface Mode {
- Mode NO_TIME = new Mode() {};
- Mode TIME = new Mode() {};
-
- class Instrument implements Mode {
- public final URL[] classpath;
-
- Instrument(URL[] classpath) {
- this.classpath = classpath;
- }
- }
+ private static void printUsage(PrintStream out) {
+ out.println("usage: java -jar kotlin-preloader.jar [] []");
+ out.println("where possible options include:");
+ out.println(" -classpath (-cp) Paths where to find class files");
+ out.println(" -measure Record and output the total time taken by the program and number of loaded classes");
+ out.println(" -instrument Paths where the instrumenter will be looked up by java.util.ServiceLoader");
+ out.println(" (the class must implement " + Instrumenter.class.getCanonicalName() + " interface)");
+ out.println(" -estimate Class number estimate (" + DEFAULT_CLASS_NUMBER_ESTIMATE + " by default)");
+ out.println(" -help (-h) Output this help message");
}
- private static Mode parseMode(String arg) {
- if ("time".equals(arg)) return Mode.TIME;
- if ("notime".equals(arg)) return Mode.NO_TIME;
+ private static class Options {
+ public final List classpath;
+ public final boolean measure;
+ public final List instrumenters;
+ public final int estimate;
+ public final String mainClass;
+ public final List arguments;
- if (arg.startsWith(INSTRUMENT_PREFIX)) {
- List files = parseClassPath(arg.substring(INSTRUMENT_PREFIX.length()));
- URL[] classpath = new URL[files.size()];
- for (int i = 0; i < files.size(); i++) {
- File file = files.get(i);
- try {
- classpath[i] = file.toURI().toURL();
- }
- catch (MalformedURLException e) {
- System.err.println("error: malformed URL: " + e.getMessage());
- printUsageAndExit();
- }
- }
- return new Mode.Instrument(classpath);
+ private Options(
+ List classpath,
+ boolean measure,
+ List instrumenters,
+ int estimate,
+ String mainClass,
+ List arguments
+ ) {
+ this.classpath = classpath;
+ this.measure = measure;
+ this.instrumenters = instrumenters;
+ this.estimate = estimate;
+ this.mainClass = mainClass;
+ this.arguments = arguments;
}
-
- System.err.println("error: unrecognized argument: " + arg);
- printUsageAndExit();
- return Mode.NO_TIME;
- }
-
- private static void printUsageAndExit() {
- System.out.println("Usage: Preloader > ");
- System.exit(1);
}
private static class Handler extends ClassHandler {
diff --git a/compiler/tests/org/jetbrains/kotlin/test/MockLibraryUtil.java b/compiler/tests/org/jetbrains/kotlin/test/MockLibraryUtil.java
index f4904f60360..06b28eaa21a 100644
--- a/compiler/tests/org/jetbrains/kotlin/test/MockLibraryUtil.java
+++ b/compiler/tests/org/jetbrains/kotlin/test/MockLibraryUtil.java
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.cli.js.K2JSCompiler;
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
import org.jetbrains.kotlin.preloading.ClassPreloadingUtils;
+import org.jetbrains.kotlin.preloading.Preloader;
import org.jetbrains.kotlin.utils.PathUtil;
import org.jetbrains.kotlin.utils.UtilsPackage;
@@ -228,7 +229,9 @@ public class MockLibraryUtil {
private static synchronized ClassLoader createCompilerClassLoader() {
try {
File kotlinCompilerJar = new File(PathUtil.getKotlinPathsForDistDirectory().getLibPath(), "kotlin-compiler.jar");
- return ClassPreloadingUtils.preloadClasses(Collections.singletonList(kotlinCompilerJar), 4096, null, null, null);
+ return ClassPreloadingUtils.preloadClasses(
+ Collections.singletonList(kotlinCompilerJar), Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE, null, null, null
+ );
}
catch (Throwable e) {
throw UtilsPackage.rethrow(e);
diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java
index 75036a84842..47bee730d39 100644
--- a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java
+++ b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/CompilerRunnerUtil.java
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cli.common.messages.MessageCollector;
import org.jetbrains.kotlin.preloading.ClassPreloadingUtils;
+import org.jetbrains.kotlin.preloading.Preloader;
import org.jetbrains.kotlin.utils.KotlinPaths;
import java.io.File;
@@ -45,7 +46,7 @@ public class CompilerRunnerUtil {
if (classLoader == null) {
classLoader = ClassPreloadingUtils.preloadClasses(
Collections.singletonList(new File(libPath, "kotlin-compiler.jar")),
- /* estimatedClassNumber = */ 4096,
+ Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE,
CompilerRunnerUtil.class.getClassLoader(),
environment.getClassesToLoadByParent()
);