Add support for script classfiles caching

This commit is contained in:
Ilya Chernikov
2016-06-15 12:54:51 +02:00
committed by Pavel V. Talanov
parent 3157421189
commit 8dac72f465
5 changed files with 39 additions and 15 deletions
@@ -166,6 +166,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
} }
else if (arguments.script) { else if (arguments.script) {
val scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size) val scriptArgs = arguments.freeArgs.subList(1, arguments.freeArgs.size)
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
val environment = createCoreEnvironment(rootDisposable, configuration) val environment = createCoreEnvironment(rootDisposable, configuration)
if (messageCollector.hasErrors()) return COMPILATION_ERROR if (messageCollector.hasErrors()) return COMPILATION_ERROR
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.messages.* import org.jetbrains.kotlin.cli.common.messages.*
@@ -51,23 +50,19 @@ import org.jetbrains.kotlin.name.isSubpackageOf
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.script.getScriptDefinition
import org.jetbrains.kotlin.util.PerformanceCounter import org.jetbrains.kotlin.util.PerformanceCounter
import org.jetbrains.kotlin.utils.KotlinPaths import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.lang.reflect.Constructor
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.net.URLClassLoader import java.net.URLClassLoader
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.jar.Attributes import java.util.jar.Attributes
import kotlin.reflect.KClass
import kotlin.reflect.KParameter import kotlin.reflect.KParameter
import kotlin.reflect.KType import kotlin.reflect.KType
import kotlin.reflect.defaultType import kotlin.reflect.defaultType
import kotlin.reflect.jvm.javaMethod
import kotlin.reflect.jvm.javaType import kotlin.reflect.jvm.javaType
object KotlinToJVMBytecodeCompiler { object KotlinToJVMBytecodeCompiler {
@@ -106,7 +101,9 @@ object KotlinToJVMBytecodeCompiler {
return GenerationStateEventCallback { state -> return GenerationStateEventCallback { state ->
val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput) val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput)
writeOutput(configuration, currentOutput, mainClass = null) writeOutput(configuration, currentOutput, mainClass = null)
state.factory.releaseGeneratedOutput() if (!configuration.get(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, false)) {
state.factory.releaseGeneratedOutput()
}
} }
} }
@@ -346,9 +343,8 @@ object KotlinToJVMBytecodeCompiler {
makeParentClassLoader: () -> ClassLoader): Class<*>? { makeParentClassLoader: () -> ClassLoader): Class<*>? {
val state = analyzeAndGenerate(environment) ?: return null val state = analyzeAndGenerate(environment) ?: return null
val classLoader: GeneratedClassLoader
try { try {
classLoader = GeneratedClassLoader(state.factory, makeParentClassLoader(), null) val classLoader = GeneratedClassLoader(state.factory, makeParentClassLoader(), null)
val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed") val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed")
return classLoader.loadClass(script.fqName.asString()) return classLoader.loadClass(script.fqName.asString())
@@ -41,6 +41,9 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<List<KotlinScriptDefinition>> SCRIPT_DEFINITIONS = public static final CompilerConfigurationKey<List<KotlinScriptDefinition>> SCRIPT_DEFINITIONS =
CompilerConfigurationKey.create("script definitions"); CompilerConfigurationKey.create("script definitions");
public static final CompilerConfigurationKey<Boolean> RETAIN_OUTPUT_IN_MEMORY =
CompilerConfigurationKey.create("retain compiled classes in memory for further use, e.g. when running scripts");
public static final CompilerConfigurationKey<Boolean> DISABLE_CALL_ASSERTIONS = public static final CompilerConfigurationKey<Boolean> DISABLE_CALL_ASSERTIONS =
CompilerConfigurationKey.create("disable not-null call assertions"); CompilerConfigurationKey.create("disable not-null call assertions");
public static final CompilerConfigurationKey<Boolean> DISABLE_PARAM_ASSERTIONS = public static final CompilerConfigurationKey<Boolean> DISABLE_PARAM_ASSERTIONS =
@@ -17,13 +17,13 @@
package org.jetbrains.kotlin.scripts package org.jetbrains.kotlin.scripts
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import junit.framework.TestCase
import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.* import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.addKotlinSourceRoot import org.jetbrains.kotlin.config.addKotlinSourceRoot
@@ -39,8 +39,9 @@ import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.net.URLClassLoader
class ScriptTest { class ScriptTest: TestCase() {
@Test @Test
fun testScriptWithParam() { fun testScriptWithParam() {
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam())) val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
@@ -64,6 +65,21 @@ class ScriptTest {
Assert.assertNotNull(anObj) Assert.assertNotNull(anObj)
} }
@Test
fun testStandardScriptWithSaving() {
val tmpdir = File(KotlinTestUtils.tmpDirForTest(this), "withSaving")
tmpdir.mkdirs()
val aClass = compileScript("fib_std.kts", StandardScriptDefinition, saveClassesDir = tmpdir)
Assert.assertNotNull(aClass)
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, emptyList())
Assert.assertNotNull(anObj)
val savedClassLoader = URLClassLoader(arrayOf(tmpdir.toURI().toURL()))
val aClassSaved = savedClassLoader.loadClass(aClass.name)
Assert.assertNotNull(aClassSaved)
val anObjSaved = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClassSaved!!, emptyList())
Assert.assertNotNull(anObjSaved)
}
@Test @Test
fun testScriptWithParamConversion() { fun testScriptWithParamConversion() {
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam())) val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
@@ -163,21 +179,24 @@ class ScriptTest {
scriptPath: String, scriptPath: String,
scriptDefinition: KotlinScriptDefinition, scriptDefinition: KotlinScriptDefinition,
runIsolated: Boolean = true, runIsolated: Boolean = true,
suppressOutput: Boolean = false): Class<*>? = suppressOutput: Boolean = false,
compileScriptImpl("compiler/testData/script/" + scriptPath, scriptDefinition, runIsolated, suppressOutput) saveClassesDir: File? = null): Class<*>? =
compileScriptImpl("compiler/testData/script/" + scriptPath, scriptDefinition, runIsolated, suppressOutput, saveClassesDir)
private fun compileSmokeTestScript( private fun compileSmokeTestScript(
scriptPath: String, scriptPath: String,
scriptDefinition: KotlinScriptDefinition, scriptDefinition: KotlinScriptDefinition,
runIsolated: Boolean = true, runIsolated: Boolean = true,
suppressOutput: Boolean = false): Class<*>? = suppressOutput: Boolean = false,
compileScriptImpl("compiler/testData/integration/smoke/" + scriptPath, scriptDefinition, runIsolated, suppressOutput) saveClassesDir: File? = null): Class<*>? =
compileScriptImpl("compiler/testData/integration/smoke/" + scriptPath, scriptDefinition, runIsolated, suppressOutput, saveClassesDir)
private fun compileScriptImpl( private fun compileScriptImpl(
scriptPath: String, scriptPath: String,
scriptDefinition: KotlinScriptDefinition, scriptDefinition: KotlinScriptDefinition,
runIsolated: Boolean, runIsolated: Boolean,
suppressOutput: Boolean): Class<*>? suppressOutput: Boolean,
saveClassesDir: File? = null): Class<*>?
{ {
val paths = PathUtil.getKotlinPathsForDistDirectory() val paths = PathUtil.getKotlinPathsForDistDirectory()
val messageCollector = val messageCollector =
@@ -190,6 +209,10 @@ class ScriptTest {
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
configuration.addKotlinSourceRoot(scriptPath) configuration.addKotlinSourceRoot(scriptPath)
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
if (saveClassesDir != null) {
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, saveClassesDir)
}
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
@@ -91,6 +91,7 @@ class ScriptTest2 {
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
configuration.addKotlinSourceRoot(scriptPath) configuration.addKotlinSourceRoot(scriptPath)
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)