diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CompileEnvironmentUtil.java b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CompileEnvironmentUtil.java index ec109d3b274..d46ebc5a4db 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CompileEnvironmentUtil.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CompileEnvironmentUtil.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector; import org.jetbrains.kotlin.cli.common.modules.ModuleChunk; import org.jetbrains.kotlin.cli.common.modules.ModuleXmlParser; import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol; import org.jetbrains.kotlin.utils.ExceptionUtilsKt; import org.jetbrains.kotlin.utils.PathUtil; @@ -60,6 +61,7 @@ public class CompileEnvironmentUtil { OutputStream fos, @Nullable FqName mainClass, boolean includeRuntime, + boolean noReflect, boolean resetJarTimestamps ) { try { @@ -89,6 +91,9 @@ public class CompileEnvironmentUtil { } if (includeRuntime) { writeRuntimeToJar(stream, resetJarTimestamps); + if (!noReflect) { + writeReflectToJar(stream, resetJarTimestamps); + } } stream.finish(); } @@ -98,12 +103,12 @@ public class CompileEnvironmentUtil { } public static void writeToJar( - File jarPath, boolean jarRuntime, boolean resetJarTimestamps, FqName mainClass, OutputFileCollection outputFiles + File jarPath, boolean jarRuntime, boolean noReflect, boolean resetJarTimestamps, FqName mainClass, OutputFileCollection outputFiles ) { FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(jarPath); - doWriteToJar(outputFiles, outputStream, mainClass, jarRuntime, resetJarTimestamps); + doWriteToJar(outputFiles, outputStream, mainClass, jarRuntime, noReflect, resetJarTimestamps); outputStream.close(); } catch (FileNotFoundException e) { @@ -125,13 +130,22 @@ public class CompileEnvironmentUtil { copyJarImpl(stream, stdlibPath, resetJarTimestamps); } + private static void writeReflectToJar(JarOutputStream stream, boolean resetJarTimestamps) throws IOException { + File reflectPath = PathUtil.getKotlinPathsForCompiler().getReflectPath(); + if (!reflectPath.exists()) { + throw new CompileEnvironmentException("Couldn't find kotlin-reflect at " + reflectPath); + } + copyJarImpl(stream, reflectPath, resetJarTimestamps); + } + private static void copyJarImpl(JarOutputStream stream, File jarPath, boolean resetJarTimestamps) throws IOException { try (JarInputStream jis = new JarInputStream(new FileInputStream(jarPath))) { while (true) { JarEntry e = jis.getNextJarEntry(); if (e == null) break; - if (!FileUtilRt.extensionEquals(e.getName(), "class") || + if ((!FileUtilRt.extensionEquals(e.getName(), "class") && + !FileUtilRt.extensionEquals(e.getName(), BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION)) || StringsKt.substringAfterLast(e.getName(), "/", e.getName()).equals("module-info.class")) { continue; } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt index 625581cdb7a..867ab0a1bb9 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt @@ -86,8 +86,9 @@ object KotlinToJVMBytecodeCompiler { val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) if (jarPath != null) { val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false) + val noReflect = configuration.get(JVMConfigurationKeys.NO_REFLECT, false) val resetJarTimestamps = !configuration.get(JVMConfigurationKeys.NO_RESET_JAR_TIMESTAMPS, false) - CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, resetJarTimestamps, mainClassFqName, outputFiles) + CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, noReflect, resetJarTimestamps, mainClassFqName, outputFiles) if (reportOutputFiles) { val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath) messageCollector.report(OUTPUT, message) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt index 3cbe6cc8780..f48a1ed5811 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt @@ -21,6 +21,7 @@ import java.io.File fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArguments) { put(JVMConfigurationKeys.INCLUDE_RUNTIME, arguments.includeRuntime) + put(JVMConfigurationKeys.NO_REFLECT, arguments.noReflect) putIfNotNull(JVMConfigurationKeys.FRIEND_PATHS, arguments.friendPaths?.asList()) diff --git a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java index b1c11d8f696..97e60e10235 100644 --- a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java +++ b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java @@ -146,4 +146,7 @@ public class JVMConfigurationKeys { public static final CompilerConfigurationKey ENABLE_JVM_PREVIEW = CompilerConfigurationKey.create("Enable Java language preview features"); + + public static final CompilerConfigurationKey NO_REFLECT = + CompilerConfigurationKey.create("Don't automatically include kotlin-reflect.jar into the output if the output is a jar"); } diff --git a/compiler/testData/integration/smoke/noReflect/noReflect.compile.expected b/compiler/testData/integration/smoke/noReflect/noReflect.compile.expected new file mode 100644 index 00000000000..43f7acbc168 --- /dev/null +++ b/compiler/testData/integration/smoke/noReflect/noReflect.compile.expected @@ -0,0 +1,6 @@ +ERR: +noReflect.kt:5:23: warning: call uses reflection API which is not found in compilation classpath. Make sure you have kotlin-reflect.jar in the classpath + String::class.annotations + ^ + +Return code: 0 diff --git a/compiler/testData/integration/smoke/noReflect/noReflect.kt b/compiler/testData/integration/smoke/noReflect/noReflect.kt new file mode 100644 index 00000000000..b1cdc504e49 --- /dev/null +++ b/compiler/testData/integration/smoke/noReflect/noReflect.kt @@ -0,0 +1,9 @@ +package noReflect + +fun main() { + try { + String::class.annotations + } catch (e: KotlinReflectionNotSupportedError) { + println("KotlinReflectionNotSupportedError has been caught") + } +} \ No newline at end of file diff --git a/compiler/testData/integration/smoke/noReflect/noReflect.run.expected b/compiler/testData/integration/smoke/noReflect/noReflect.run.expected new file mode 100644 index 00000000000..f2c45d486ed --- /dev/null +++ b/compiler/testData/integration/smoke/noReflect/noReflect.run.expected @@ -0,0 +1,4 @@ +OUT: +KotlinReflectionNotSupportedError has been caught + +Return code: 0 diff --git a/compiler/testData/integration/smoke/reflect/reflect.compile.expected b/compiler/testData/integration/smoke/reflect/reflect.compile.expected new file mode 100644 index 00000000000..a14ac74940f --- /dev/null +++ b/compiler/testData/integration/smoke/reflect/reflect.compile.expected @@ -0,0 +1 @@ +Return code: 0 diff --git a/compiler/testData/integration/smoke/reflect/reflect.kt b/compiler/testData/integration/smoke/reflect/reflect.kt new file mode 100644 index 00000000000..4fb5c0b80cf --- /dev/null +++ b/compiler/testData/integration/smoke/reflect/reflect.kt @@ -0,0 +1,5 @@ +package reflect + +fun main() { + String::class.annotations +} \ No newline at end of file diff --git a/compiler/testData/integration/smoke/reflect/reflect.run.expected b/compiler/testData/integration/smoke/reflect/reflect.run.expected new file mode 100644 index 00000000000..a14ac74940f --- /dev/null +++ b/compiler/testData/integration/smoke/reflect/reflect.run.expected @@ -0,0 +1 @@ +Return code: 0 diff --git a/compiler/tests/org/jetbrains/kotlin/integration/CompilerSmokeTest.java b/compiler/tests/org/jetbrains/kotlin/integration/CompilerSmokeTest.java index 4f4f3dd337e..1fb05ee2b88 100644 --- a/compiler/tests/org/jetbrains/kotlin/integration/CompilerSmokeTest.java +++ b/compiler/tests/org/jetbrains/kotlin/integration/CompilerSmokeTest.java @@ -187,4 +187,18 @@ public class CompilerSmokeTest extends CompilerSmokeTestBase { ); run("buildFile.run", "-cp", tmpdir.getAbsolutePath(), "MainKt"); } + + public void testReflect() throws Exception { + String jar = tmpdir.getAbsolutePath() + File.separator + "reflect.jar"; + assertEquals("compilation failed", 0, + runCompiler("reflect.compile", "-include-runtime", "reflect.kt", "-d", jar)); + run("reflect.run", "-cp", jar, "reflect.ReflectKt"); + } + + public void testNoReflect() throws Exception { + String jar = tmpdir.getAbsolutePath() + File.separator + "noReflect.jar"; + assertEquals("compilation failed", 0, + runCompiler("noReflect.compile", "-include-runtime", "-no-reflect", "noReflect.kt", "-d", jar)); + run("noReflect.run", "-cp", jar, "noReflect.NoReflectKt"); + } }