Fix all small and medium-sized issues after review

This commit is contained in:
Ilya Chernikov
2016-10-06 18:46:46 +02:00
parent 9617fc7d6b
commit c2b5c11781
32 changed files with 195 additions and 312 deletions
@@ -152,10 +152,7 @@ public class ScriptCodegen extends MemberCodegen<KtScript> {
iv.invokespecial("java/lang/Object", "<init>", "()V", false); iv.invokespecial("java/lang/Object", "<init>", "()V", false);
} }
else { else {
// TODO: check if it is correct way to find a primary constructor ConstructorDescriptor ctorDesc = superclass.getUnsubstitutedPrimaryConstructor();
Collection<ClassConstructorDescriptor> ctorDescriptors = superclass.getConstructors();
assert ctorDescriptors.size() == 1;
ConstructorDescriptor ctorDesc = ctorDescriptors.iterator().next();
assert ctorDesc != null; assert ctorDesc != null;
iv.load(0, classType); iv.load(0, classType);
@@ -38,6 +38,6 @@ data class CompilerMessageLocation private constructor(
): CompilerMessageLocation = ): CompilerMessageLocation =
if (path == null) NO_LOCATION else CompilerMessageLocation(path, line, column, lineContent) if (path == null) NO_LOCATION else CompilerMessageLocation(path, line, column, lineContent)
private val serialVersionUID: Long = 8228357578L // just a random number, should be changed when serialized data is changed private val serialVersionUID: Long = 8228357578L
} }
} }
@@ -73,13 +73,13 @@ fun tryConstructScriptClass(scriptClass: Class<*>, scriptArgs: List<String>): An
} }
try { try {
return scriptClass.getConstructor(Array<String>::class.java).newInstance(*arrayOf<Any>(scriptArgs.toTypedArray())) return scriptClass.getConstructor(Array<String>::class.java).newInstance(scriptArgs.toTypedArray())
} }
catch (e: java.lang.NoSuchMethodException) { catch (e: NoSuchMethodException) {
for (ctor in scriptClass.kotlin.constructors) { for (ctor in scriptClass.kotlin.constructors) {
val (ctorArgs, scriptArgsLeft) = ctor.parameters.fold(Pair(emptyList<Any>(), scriptArgs), ::foldingFunc) val (ctorArgs, scriptArgsLeft) = ctor.parameters.fold(Pair(emptyList<Any>(), scriptArgs), ::foldingFunc)
if (ctorArgs.size <= ctor.parameters.size && (scriptArgsLeft == null || scriptArgsLeft.isEmpty())) { if (ctorArgs.size <= ctor.parameters.size && (scriptArgsLeft == null || scriptArgsLeft.isEmpty())) {
val argsMap = ctorArgs.zip(ctor.parameters) { a, p -> Pair(p, a) }.toMap() val argsMap = ctor.parameters.zip(ctorArgs).toMap()
try { try {
return ctor.callBy(argsMap) return ctor.callBy(argsMap)
} }
@@ -25,8 +25,7 @@ import kotlin.concurrent.write
open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClassloader: ClassLoader?, val scriptArgs: Array<Any?>? = null, val scriptArgsTypes: Array<Class<*>>? = null) : ReplCompiledEvaluator { open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClassloader: ClassLoader?, val scriptArgs: Array<Any?>? = null, val scriptArgsTypes: Array<Class<*>>? = null) : ReplCompiledEvaluator {
private var classLoader: org.jetbrains.kotlin.cli.common.repl.ReplClassLoader = private var classLoader: org.jetbrains.kotlin.cli.common.repl.ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
org.jetbrains.kotlin.cli.common.repl.ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
private val classLoaderLock = ReentrantReadWriteLock() private val classLoaderLock = ReentrantReadWriteLock()
private class ClassWithInstance(val klass: Class<*>, val instance: Any) private class ClassWithInstance(val klass: Class<*>, val instance: Any)
@@ -42,7 +41,7 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
classLoaderLock.read { classLoaderLock.read {
if (newClasspath.isNotEmpty()) { if (newClasspath.isNotEmpty()) {
classLoaderLock.write { classLoaderLock.write {
classLoader = org.jetbrains.kotlin.cli.common.repl.ReplClassLoader(URLClassLoader(newClasspath.map { it.toURI().toURL() }.toTypedArray(), classLoader)) classLoader = makeReplClassLoader(classLoader, newClasspath)
} }
} }
compiledClasses.filter { it.path.endsWith(".class") } compiledClasses.filter { it.path.endsWith(".class") }
@@ -55,14 +54,9 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
val constructorParams: Array<Class<*>> = val constructorParams: Array<Class<*>> =
(compiledLoadedClassesHistory.map { it.second.klass } + (compiledLoadedClassesHistory.map { it.second.klass } +
(scriptArgs?.asIterable() (scriptArgs?.mapIndexed { i, it -> scriptArgsTypes?.getOrNull(i) ?: it?.javaClass ?: Any::class.java } ?: emptyList())
?.mapIndexed { i, it ->
if (i < (scriptArgsTypes?.size ?: 0)) scriptArgsTypes!![i] else it?.javaClass ?: Any::class.java
}
?: emptyList()
)
).toTypedArray() ).toTypedArray()
val constructorArgs: Array<Any?> = (compiledLoadedClassesHistory.map { it.second.instance } + (scriptArgs?.asIterable() ?: emptyList())).toTypedArray() val constructorArgs: Array<Any?> = (compiledLoadedClassesHistory.map { it.second.instance } + scriptArgs.orEmpty()).toTypedArray()
val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams) val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams)
val scriptInstance = val scriptInstance =
@@ -85,4 +79,7 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
companion object { companion object {
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result" private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
} }
} }
private fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
@@ -23,7 +23,7 @@ import java.util.*
data class ReplCodeLine(val no: Int, val code: String) : Serializable { data class ReplCodeLine(val no: Int, val code: String) : Serializable {
companion object { companion object {
private val serialVersionUID: Long = 8228357578L // just a random number, should be changed when serialized data is changed private val serialVersionUID: Long = 8228357578L
} }
} }
@@ -31,7 +31,7 @@ data class CompiledClassData(val path: String, val bytes: ByteArray) : Serializa
override fun equals(other: Any?): Boolean = (other as? CompiledClassData)?.let { path == it.path && Arrays.equals(bytes, it.bytes) } ?: false override fun equals(other: Any?): Boolean = (other as? CompiledClassData)?.let { path == it.path && Arrays.equals(bytes, it.bytes) } ?: false
override fun hashCode(): Int = path.hashCode() + Arrays.hashCode(bytes) override fun hashCode(): Int = path.hashCode() + Arrays.hashCode(bytes)
companion object { companion object {
private val serialVersionUID: Long = 8228357578L // just a random number, should be changed when serialized data is changed private val serialVersionUID: Long = 8228357578L
} }
} }
@@ -42,7 +42,7 @@ sealed class ReplCheckResult : Serializable {
override fun toString(): String = "Error(message = \"$message\"" override fun toString(): String = "Error(message = \"$message\""
} }
companion object { companion object {
private val serialVersionUID: Long = 8228357578L // just a random number, should be changed when serialized data is changed private val serialVersionUID: Long = 8228357578L
} }
} }
@@ -54,7 +54,7 @@ sealed class ReplCompileResult : Serializable {
override fun toString(): String = "Error(message = \"$message\"" override fun toString(): String = "Error(message = \"$message\""
} }
companion object { companion object {
private val serialVersionUID: Long = 8228357578L // just a random number, should be changed when serialized data is changed private val serialVersionUID: Long = 8228357578L
} }
} }
@@ -68,10 +68,10 @@ sealed class ReplEvalResult : Serializable {
sealed class Error(val message: String) : ReplEvalResult() { sealed class Error(val message: String) : ReplEvalResult() {
class Runtime(message: String) : Error(message) class Runtime(message: String) : Error(message)
class CompileTime(message: String, val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : Error(message) class CompileTime(message: String, val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : Error(message)
override fun toString(): String = "${this.javaClass.kotlin.simpleName}Error(message = \"$message\"" override fun toString(): String = "${this::class.simpleName}Error(message = \"$message\""
} }
companion object { companion object {
private val serialVersionUID: Long = 8228357578L // just a random number, should be changed when serialized data is changed private val serialVersionUID: Long = 8228357578L
} }
} }
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.descriptors.ScriptDescriptor import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.KotlinScriptDefinition
@@ -74,28 +73,27 @@ open class GenericReplChecker(
fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder() fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
@Synchronized
override fun check(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplCheckResult { override fun check(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplCheckResult {
synchronized(this) { val virtualFile =
val virtualFile = LightVirtualFile("line${codeLine.no}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply {
LightVirtualFile("line${codeLine.no}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply { charset = CharsetToolkit.UTF8_CHARSET
charset = CharsetToolkit.UTF8_CHARSET }
} val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile? ?: error("Script file not analyzed at line ${codeLine.no}: ${codeLine.code}")
?: error("Script file not analyzed at line ${codeLine.no}: ${codeLine.code}")
val errorHolder = createDiagnosticHolder() val errorHolder = createDiagnosticHolder()
val syntaxErrorReport = AnalyzerWithCompilerReport.Companion.reportSyntaxErrors(psiFile, errorHolder) val syntaxErrorReport = AnalyzerWithCompilerReport.Companion.reportSyntaxErrors(psiFile, errorHolder)
if (!syntaxErrorReport.isHasErrors) { if (!syntaxErrorReport.isHasErrors) {
lineState = LineState(codeLine, psiFile, errorHolder) lineState = LineState(codeLine, psiFile, errorHolder)
} }
return when { return when {
syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete
syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderedDiagnostics) syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderedDiagnostics)
else -> ReplCheckResult.Ok else -> ReplCheckResult.Ok
}
} }
} }
} }
@@ -113,61 +111,59 @@ open class GenericReplCompiler(
private val descriptorsHistory = arrayListOf<Pair<ReplCodeLine, ScriptDescriptor>>() private val descriptorsHistory = arrayListOf<Pair<ReplCodeLine, ScriptDescriptor>>()
@Synchronized
override fun compile(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplCompileResult { override fun compile(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplCompileResult {
synchronized(this) { checkAndUpdateReplHistoryCollection(descriptorsHistory, history)?.let {
return@compile ReplCompileResult.HistoryMismatch(it)
checkAndUpdateReplHistoryCollection(descriptorsHistory, history)?.let {
return@compile ReplCompileResult.HistoryMismatch(it)
}
val (psiFile, errorHolder) = run {
if (lineState == null || lineState!!.codeLine != codeLine) {
val res = check(codeLine, history)
when (res) {
ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.message, res.location)
ReplCheckResult.Ok -> {} // continue
}
}
Pair(lineState!!.psiFile, lineState!!.errorHolder)
}
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, environment.project, lastDependencies)
if (lastDependencies != newDependencies) {
lastDependencies = newDependencies
}
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine.no)
AnalyzerWithCompilerReport.Companion.reportDiagnostics(analysisResult.diagnostics, errorHolder, false)
val scriptDescriptor = when (analysisResult) {
is CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(errorHolder.renderedDiagnostics)
is CliReplAnalyzerEngine.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
else -> error("Unexpected result ${analysisResult.javaClass}")
}
val state = GenerationState(
psiFile.project,
ClassBuilderFactories.binaries(false),
analyzerEngine.module,
analyzerEngine.trace.bindingContext,
listOf(psiFile),
compilerConfiguration
)
state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME
state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.map { it.second }
state.beforeCompile()
KotlinCodegenFacade.generatePackage(
state,
psiFile.script!!.getContainingKtFile().packageFqName,
setOf(psiFile.script!!.getContainingKtFile()),
org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
descriptorsHistory.add(codeLine to scriptDescriptor)
return ReplCompileResult.CompiledClasses(state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
state.replSpecific.hasResult,
newDependencies?.let { environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) } ?: emptyList())
} }
val (psiFile, errorHolder) = run {
if (lineState == null || lineState!!.codeLine != codeLine) {
val res = check(codeLine, history)
when (res) {
ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.message, res.location)
ReplCheckResult.Ok -> {} // continue
}
}
Pair(lineState!!.psiFile, lineState!!.errorHolder)
}
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, environment.project, lastDependencies)
if (lastDependencies != newDependencies) {
lastDependencies = newDependencies
}
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine.no)
AnalyzerWithCompilerReport.Companion.reportDiagnostics(analysisResult.diagnostics, errorHolder, false)
val scriptDescriptor = when (analysisResult) {
is CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(errorHolder.renderedDiagnostics)
is CliReplAnalyzerEngine.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
else -> error("Unexpected result ${analysisResult.javaClass}")
}
val state = GenerationState(
psiFile.project,
ClassBuilderFactories.binaries(false),
analyzerEngine.module,
analyzerEngine.trace.bindingContext,
listOf(psiFile),
compilerConfiguration
)
state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME
state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.map { it.second }
state.beforeCompile()
KotlinCodegenFacade.generatePackage(
state,
psiFile.script!!.getContainingKtFile().packageFqName,
setOf(psiFile.script!!.getContainingKtFile()),
org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
descriptorsHistory.add(codeLine to scriptDescriptor)
return ReplCompileResult.CompiledClasses(state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
state.replSpecific.hasResult,
newDependencies?.let { environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) } ?: emptyList())
} }
companion object { companion object {
@@ -188,9 +184,9 @@ open class GenericRepl(
private val compiledEvaluator = GenericReplCompiledEvaluator(compilerConfiguration.jvmClasspathRoots, baseClassloader, scriptArgs, scriptArgsTypes) private val compiledEvaluator = GenericReplCompiledEvaluator(compilerConfiguration.jvmClasspathRoots, baseClassloader, scriptArgs, scriptArgsTypes)
override fun eval(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplEvalResult = synchronized(this) { @Synchronized
return compileAndEval(this, compiledEvaluator, codeLine, history) override fun eval(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplEvalResult =
} compileAndEval(this, compiledEvaluator, codeLine, history)
} }
@@ -390,9 +390,10 @@ class CompileServiceImpl(
synchronized(state.sessions) { synchronized(state.sessions) {
// 2. check if any session hanged - clean // 2. check if any session hanged - clean
state.sessions.filterValues { !it.isAlive }.apply { anyDead = true }.forEach { state.sessions.filterValues { !it.isAlive }.forEach {
it.value.dispose() it.value.dispose()
state.sessions.remove(it.key) state.sessions.remove(it.key)
anyDead = true
} }
} }
@@ -403,13 +404,14 @@ class CompileServiceImpl(
} }
// 4. clean dead clients, then check if any left - conditional shutdown (with small delay) // 4. clean dead clients, then check if any left - conditional shutdown (with small delay)
synchronized(state.clientProxies) { synchronized(state.clientProxies) {
state.clientProxies.removeAll( state.clientProxies.removeAll(
state.clientProxies.filter { !it.isAlive }.apply { anyDead = true }.map { state.clientProxies.filter { !it.isAlive }.map {
it.dispose() it.dispose()
it anyDead = true
}) it
} })
}
if (state.clientProxies.isEmpty() && compilationsCounter.get() > 0 && !state.delayedShutdownQueued.get()) { if (state.clientProxies.isEmpty() && compilationsCounter.get() > 0 && !state.delayedShutdownQueued.get()) {
log.info("No more clients left, delayed shutdown in ${daemonOptions.shutdownDelayMilliseconds}ms") log.info("No more clients left, delayed shutdown in ${daemonOptions.shutdownDelayMilliseconds}ms")
shutdownWithDelay() shutdownWithDelay()
@@ -52,11 +52,11 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
defAnn != null -> defAnn != null ->
try { try {
defAnn.resolver.primaryConstructor?.call() ?: null.apply { defAnn.resolver.primaryConstructor?.call() ?: null.apply {
log.error("[kts] No default constructor found for ${defAnn.resolver.qualifiedName}") log.warn("[kts] No default constructor found for ${defAnn.resolver.qualifiedName}")
} }
} }
catch (ex: ClassCastException) { catch (ex: ClassCastException) {
log.error("[kts] Script def error ${ex.message}") log.warn("[kts] Script def error ${ex.message}")
null null
} }
else -> BasicScriptDependenciesResolver() else -> BasicScriptDependenciesResolver()
@@ -37,7 +37,7 @@ fun KotlinScriptDefinition.getScriptParameters(scriptDescriptor: ScriptDescripto
fun getKotlinType(scriptDescriptor: ScriptDescriptor, kClass: KClass<out Any>): KotlinType = fun getKotlinType(scriptDescriptor: ScriptDescriptor, kClass: KClass<out Any>): KotlinType =
getKotlinTypeByFqName(scriptDescriptor, getKotlinTypeByFqName(scriptDescriptor,
kClass.qualifiedName ?: throw java.lang.RuntimeException("Cannot get FQN from $kClass")) kClass.qualifiedName ?: throw RuntimeException("Cannot get FQN from $kClass"))
fun getKotlinTypeByFqName(scriptDescriptor: ScriptDescriptor, fqName: String): KotlinType = fun getKotlinTypeByFqName(scriptDescriptor: ScriptDescriptor, fqName: String): KotlinType =
scriptDescriptor.module.findNonGenericClassAcrossDependencies( scriptDescriptor.module.findNonGenericClassAcrossDependencies(
@@ -22,14 +22,14 @@ import junit.framework.TestCase
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.common.repl.* import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase
import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
import java.net.URLClassLoader import java.net.URLClassLoader
@@ -48,7 +48,7 @@ class GenericReplTest : TestCase() {
val res1 = repl.replCompiler?.check(ReplCodeLine(0, "val x ="), emptyList()) val res1 = repl.replCompiler?.check(ReplCodeLine(0, "val x ="), emptyList())
TestCase.assertTrue("Unexpected check results: $res1", res1 is ReplCheckResult.Incomplete) TestCase.assertTrue("Unexpected check results: $res1", res1 is ReplCheckResult.Incomplete)
val codeLine0 = ReplCodeLine(0, "1 + 2") val codeLine0 = ReplCodeLine(0, "val l1 = listOf(1 + 2)\nl1.first()")
val res2 = repl.replCompiler?.compile(codeLine0, emptyList()) val res2 = repl.replCompiler?.compile(codeLine0, emptyList())
val res2c = res2 as? ReplCompileResult.CompiledClasses val res2c = res2 as? ReplCompileResult.CompiledClasses
TestCase.assertNotNull("Unexpected compile result: $res2", res2c) TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
@@ -89,12 +89,10 @@ internal class TestRepl(
templateClasspath: List<File>, templateClasspath: List<File>,
templateClassName: String templateClassName: String
) { ) {
private val configuration = CompilerConfiguration().apply { private val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, *templateClasspath.toTypedArray()).apply {
addJvmClasspathRoots(PathUtil.getJdkClassesRoots())
addJvmClasspathRoots(PathUtil.getKotlinPathsForCompiler().let { listOf(it.runtimePath, it.reflectPath) })
addJvmClasspathRoots(templateClasspath)
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script") put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script")
} }
private fun makeScriptDefinition(templateClasspath: List<File>, templateClassName: String): KotlinScriptDefinition { private fun makeScriptDefinition(templateClasspath: List<File>, templateClassName: String): KotlinScriptDefinition {
val classloader = URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), this.javaClass.classLoader) val classloader = URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), this.javaClass.classLoader)
val cls = classloader.loadClass(templateClassName) val cls = classloader.loadClass(templateClassName)
@@ -510,7 +510,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
val res0 = repl.check(ReplCodeLine(0, "val x ="), emptyList()) val res0 = repl.check(ReplCodeLine(0, "val x ="), emptyList())
TestCase.assertTrue("Unexpected check results: $res0", res0 is ReplCheckResult.Incomplete) TestCase.assertTrue("Unexpected check results: $res0", res0 is ReplCheckResult.Incomplete)
val codeLine1 = ReplCodeLine(1, "val x = 5") val codeLine1 = ReplCodeLine(1, "val lst = listOf(1)\nval x = 5")
val res1 = repl.compile(codeLine1, emptyList()) val res1 = repl.compile(codeLine1, emptyList())
val res1c = res1 as? ReplCompileResult.CompiledClasses val res1c = res1 as? ReplCompileResult.CompiledClasses
TestCase.assertNotNull("Unexpected compile result: $res1", res1c) TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
@@ -181,29 +181,45 @@ class ScriptTemplateTest {
fun testScriptWithStandardTemplate() { fun testScriptWithStandardTemplate() {
val aClass = compileScript("fib_std.kts", StandardScriptTemplate::class, runIsolated = false) val aClass = compileScript("fib_std.kts", StandardScriptTemplate::class, runIsolated = false)
Assert.assertNotNull(aClass) Assert.assertNotNull(aClass)
aClass!!.getConstructor(Array<String>::class.java).newInstance(arrayOf("4", "other")) captureOut {
aClass!!.getConstructor(Array<String>::class.java).newInstance(arrayOf("4", "other"))
}.let {
assertEqualsTrimmed(NUM_4_LINE + " (other)" + FIB_SCRIPT_OUTPUT_TAIL, it)
}
} }
@Test @Test
fun testScriptWithPackage() { fun testScriptWithPackage() {
val aClass = compileScript("fib.pkg.kts", ScriptWithIntParam::class) val aClass = compileScript("fib.pkg.kts", ScriptWithIntParam::class)
Assert.assertNotNull(aClass) Assert.assertNotNull(aClass)
aClass!!.getConstructor(Integer.TYPE).newInstance(4) captureOut {
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
}.let {
assertEqualsTrimmed(NUM_4_LINE + FIB_SCRIPT_OUTPUT_TAIL, it)
}
} }
@Test @Test
fun testScriptWithScriptDefinition() { fun testScriptWithScriptDefinition() {
val aClass = compileScript("fib.kts", ScriptWithIntParam::class) val aClass = compileScript("fib.kts", ScriptWithIntParam::class)
Assert.assertNotNull(aClass) Assert.assertNotNull(aClass)
aClass!!.getConstructor(Integer.TYPE).newInstance(4) captureOut {
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
}.let {
assertEqualsTrimmed(NUM_4_LINE + FIB_SCRIPT_OUTPUT_TAIL, it)
}
} }
@Test @Test
fun testScriptWithParamConversion() { fun testScriptWithParamConversion() {
val aClass = compileScript("fib.kts", ScriptWithIntParam::class) val aClass = compileScript("fib.kts", ScriptWithIntParam::class)
Assert.assertNotNull(aClass) Assert.assertNotNull(aClass)
val anObj = tryConstructScriptClass(aClass!!, listOf("4")) captureOut {
Assert.assertNotNull(anObj) val anObj = tryConstructScriptClass(aClass!!, listOf("4"))
Assert.assertNotNull(anObj)
}.let {
assertEqualsTrimmed(NUM_4_LINE + FIB_SCRIPT_OUTPUT_TAIL, it)
}
} }
@Test @Test
@@ -275,8 +291,6 @@ class ScriptTemplateTest {
open class TestKotlinScriptDummyDependenciesResolver : ScriptDependenciesResolver { open class TestKotlinScriptDummyDependenciesResolver : ScriptDependenciesResolver {
private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() }
@AcceptedAnnotations(DependsOn::class, DependsOnTwo::class) @AcceptedAnnotations(DependsOn::class, DependsOnTwo::class)
override fun resolve(script: ScriptContents, override fun resolve(script: ScriptContents,
environment: Map<String, Any?>?, environment: Map<String, Any?>?,
@@ -39,9 +39,13 @@ internal fun captureOut(body: () -> Unit): String {
val outStream = ByteArrayOutputStream() val outStream = ByteArrayOutputStream()
val prevOut = System.out val prevOut = System.out
System.setOut(PrintStream(outStream)) System.setOut(PrintStream(outStream))
body() try {
System.out.flush() body()
System.setOut(prevOut) }
finally {
System.out.flush()
System.setOut(prevOut)
}
return outStream.toString() return outStream.toString()
} }
@@ -69,8 +69,9 @@ class KotlinJsr223JvmScriptEngine4Idea(
override fun eval(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplEvalResult { override fun eval(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplEvalResult {
fun ReplCompileResult.Error.locationString() = if (location == CompilerMessageLocation.NO_LOCATION) "" fun ReplCompileResult.Error.locationString() =
else " at ${location.line}:${location.column}:" if (location == CompilerMessageLocation.NO_LOCATION) ""
else " at ${location.line}:${location.column}"
val compileResult = replCompiler.compile(codeLine, history) val compileResult = replCompiler.compile(codeLine, history)
val compiled = when (compileResult) { val compiled = when (compileResult) {
@@ -36,6 +36,6 @@ class KotlinJsr223StandardScriptEngineFactory4Idea : KotlinJsr223JvmScriptEngine
arrayOf( arrayOf(
(bindings[ScriptEngine.ARGV] as? Array<*>) ?: emptyArray<String>(), (bindings[ScriptEngine.ARGV] as? Array<*>) ?: emptyArray<String>(),
bindings) }, bindings) },
arrayOf(Array<String>::class.java, java.util.Map::class.java) arrayOf(Array<String>::class.java, Map::class.java)
) )
} }
@@ -1,110 +0,0 @@
/*
* Copyright 2010-2016 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.
*/
package org.jetbrains.kotlin.jsr223
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.repl.GenericReplCompiledEvaluator
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
import org.jetbrains.kotlin.daemon.client.DaemonReportMessage
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient
import org.jetbrains.kotlin.daemon.client.KotlinRemoteReplCompiler
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.io.Reader
import javax.script.*
class KotlinJvmJsr223ScriptEngine4Idea(
disposable: Disposable,
private val factory: ScriptEngineFactory,
templateClasspath: List<File>,
templateClassName: String,
getScriptArgs: (ScriptContext) -> Array<Any?>?,
scriptArgsTypes: Array<Class<*>>?
) : AbstractScriptEngine(), ScriptEngine {
private val daemon by lazy {
val path = PathUtil.getKotlinPathsForIdeaPlugin().compilerPath
assert(path.exists())
val compilerId = CompilerId.makeCompilerId(path)
val daemonOptions = configureDaemonOptions()
val daemonJVMOptions = DaemonJVMOptions()
val daemonReportMessages = arrayListOf<DaemonReportMessage>()
KotlinCompilerClient.connectToCompileService(compilerId, daemonJVMOptions, daemonOptions, DaemonReportingTargets(null, daemonReportMessages), true, true)
?: throw ScriptException("Unable to connect to repl server:" + daemonReportMessages.joinToString("\n ", prefix = "\n ") { "${it.category.name} ${it.message}" })
}
private val replCompiler by lazy {
daemon.let {
KotlinRemoteReplCompiler(disposable,
it,
makeAutodeletingFlagFile("idea-jsr223-repl-session"),
CompileService.TargetPlatform.JVM,
templateClasspath,
templateClassName,
System.out)
}
}
// TODO: bindings passing works only once on the first eval, subsequent setContext/setBindings call have no effect. Consider making it dynamic, but take history into account
val localEvaluator by lazy { GenericReplCompiledEvaluator(templateClasspath, Thread.currentThread().contextClassLoader, getScriptArgs(getContext()), scriptArgsTypes) }
private var lineCount = 0
private val history = arrayListOf<ReplCodeLine>()
override fun eval(script: String, context: ScriptContext?): Any? {
fun ReplCompileResult.Error.locationString() = if (location == CompilerMessageLocation.NO_LOCATION) ""
else " at ${location.line}:${location.column}:"
lineCount += 1
// TODO bind to context
val codeLine = ReplCodeLine(lineCount, script)
val compileResult = replCompiler?.compile(codeLine, history)
val compiled = when (compileResult) {
is ReplCompileResult.Error -> throw ScriptException("Error${compileResult.locationString()}: ${compileResult.message}")
is ReplCompileResult.Incomplete -> throw ScriptException("error: incomplete code")
is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${compileResult.lineNo}")
is ReplCompileResult.CompiledClasses -> compileResult
}
val evalResult = localEvaluator.eval(codeLine, history, compiled.classes, compiled.hasResult, compiled.newClasspath)
val ret = when (evalResult) {
is ReplEvalResult.ValueResult -> evalResult.value
is ReplEvalResult.UnitResult -> null
is ReplEvalResult.Error -> throw ScriptException(evalResult.message)
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${evalResult.lineNo}")
}
history.add(codeLine)
// TODO update context
return ret
}
override fun eval(script: Reader, context: ScriptContext?): Any? = eval(script.readText(), context)
override fun createBindings(): Bindings = SimpleBindings()
override fun getFactory(): ScriptEngineFactory = factory
}
@@ -37,10 +37,10 @@ class IdeaJsr223Test : PlatformTestCase() {
val res0 = assertFails { engine.eval("val x =") } val res0 = assertFails { engine.eval("val x =") }
assertTrue("Unexpected check results: $res0", (res0 as? ScriptException)?.message?.contains("incomplete code") ?: false) assertTrue("Unexpected check results: $res0", (res0 as? ScriptException)?.message?.contains("incomplete code") ?: false)
val res1 = engine.eval("val x = 5") val res1 = engine.eval("val x = 5\nval y = listOf(x)")
assertNull("Unexpected eval result: $res1", res1) assertNull("Unexpected eval result: $res1", res1)
val res2 = engine.eval("x + 2") val res2 = engine.eval("y.first() + 2")
assertEquals(7, res2) assertEquals(7, res2)
} }
@@ -86,8 +86,8 @@
<version>2.6</version> <version>2.6</version>
<configuration> <configuration>
<systemPropertyVariables> <systemPropertyVariables>
<KOTLIN_COMPILER_JAR>${org.jetbrains.kotlin:kotlin-compiler:jar}</KOTLIN_COMPILER_JAR> <kotlin.compiler.jar>${org.jetbrains.kotlin:kotlin-compiler:jar}</kotlin.compiler.jar>
<KOTLIN_JAVA_RUNTIME_JAR>${org.jetbrains.kotlin:kotlin-runtime:jar}</KOTLIN_JAVA_RUNTIME_JAR> <kotlin.java.runtime.jar>${org.jetbrains.kotlin:kotlin-runtime:jar}</kotlin.java.runtime.jar>
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
<executions> <executions>
@@ -86,8 +86,8 @@
<version>2.6</version> <version>2.6</version>
<configuration> <configuration>
<systemPropertyVariables> <systemPropertyVariables>
<KOTLIN_COMPILER_JAR>${org.jetbrains.kotlin:kotlin-compiler:jar}</KOTLIN_COMPILER_JAR> <kotlin.compiler.jar>${org.jetbrains.kotlin:kotlin-compiler:jar}</kotlin.compiler.jar>
<KOTLIN_JAVA_RUNTIME_JAR>${org.jetbrains.kotlin:kotlin-runtime:jar}</KOTLIN_JAVA_RUNTIME_JAR> <kotlin.java.runtime.jar>${org.jetbrains.kotlin:kotlin-runtime:jar}</kotlin.java.runtime.jar>
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
<executions> <executions>
@@ -86,8 +86,8 @@
<version>2.6</version> <version>2.6</version>
<configuration> <configuration>
<systemPropertyVariables> <systemPropertyVariables>
<KOTLIN_COMPILER_JAR>${org.jetbrains.kotlin:kotlin-compiler:jar}</KOTLIN_COMPILER_JAR> <kotlin.compiler.jar>${org.jetbrains.kotlin:kotlin-compiler:jar}</kotlin.compiler.jar>
<KOTLIN_JAVA_RUNTIME_JAR>${org.jetbrains.kotlin:kotlin-runtime:jar}</KOTLIN_JAVA_RUNTIME_JAR> <kotlin.java.runtime.jar>${org.jetbrains.kotlin:kotlin-runtime:jar}</kotlin.java.runtime.jar>
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
<executions> <executions>
@@ -1 +1,2 @@
org.jetbrains.kotlin.script.jsr223.KotlinJsr232JvmLocalScriptEngineFactory org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory
@@ -51,7 +51,7 @@ class KotlinJsr223ScriptEngineIT {
val factory = ScriptEngineManager().getEngineByExtension("kts").factory val factory = ScriptEngineManager().getEngineByExtension("kts").factory
Assert.assertNotNull(factory) Assert.assertNotNull(factory)
val engine = factory!!.scriptEngine val engine = factory!!.scriptEngine
Assert.assertNotNull(engine as? KotlinJsr232JvmLocalScriptEngine) Assert.assertNotNull(engine as? KotlinJsr223JvmLocalScriptEngine)
Assert.assertSame(factory, engine!!.factory) Assert.assertSame(factory, engine!!.factory)
val bindings = engine.createBindings() val bindings = engine.createBindings()
Assert.assertTrue(bindings is SimpleBindings) Assert.assertTrue(bindings is SimpleBindings)
@@ -1,5 +1,9 @@
import java.lang.Exception
if (!args.isEmpty()) if (!args.isEmpty())
println("some args passed") println("some args passed")
if (this !is org.jetbrains.kotlin.script.util.StandardScript)
throw Exception("Unexpected script base class")
println("Hello from Kotlin script file!") println("Hello from Kotlin script file!")
+1 -1
View File
@@ -103,7 +103,7 @@
<configuration> <configuration>
<systemPropertyVariables> <systemPropertyVariables>
<jvm>${env.JDK_17}/bin/java</jvm> <jvm>${env.JDK_17}/bin/java</jvm>
<KOTLIN_JAVA_RUNTIME_JAR>${org.jetbrains.kotlin:kotlin-stdlib:jar}</KOTLIN_JAVA_RUNTIME_JAR> <kotlin.java.runtime.jar>${org.jetbrains.kotlin:kotlin-stdlib:jar}</kotlin.java.runtime.jar>
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
<executions> <executions>
@@ -37,7 +37,7 @@ import javax.script.ScriptContext
import javax.script.ScriptEngineFactory import javax.script.ScriptEngineFactory
import javax.script.ScriptException import javax.script.ScriptException
class KotlinJsr232JvmLocalScriptEngine( class KotlinJsr223JvmLocalScriptEngine(
disposable: Disposable, disposable: Disposable,
factory: ScriptEngineFactory, factory: ScriptEngineFactory,
val templateClasspath: List<File>, val templateClasspath: List<File>,
@@ -68,12 +68,11 @@ class KotlinJsr232JvmLocalScriptEngine(
fun resetAndThrowOnErrors() { fun resetAndThrowOnErrors() {
try { try {
if (hasErrors) { if (hasErrors) {
val msg = reports.joinToString("\n") { messageRenderer.render(it.severity, it.message, it.location) }
val firstErr = reports.firstOrNull { it.severity.isError } val firstErr = reports.firstOrNull { it.severity.isError }
if (firstErr != null) if (firstErr != null)
throw ScriptException(msg, firstErr.location.path, firstErr.location.line, firstErr.location.column) throw ScriptException(messageRenderer.render(firstErr.severity, firstErr.message, firstErr.location), firstErr.location.path, firstErr.location.line, firstErr.location.column)
else else
throw ScriptException(msg) throw ScriptException(reports.joinToString("\n") { messageRenderer.render(it.severity, it.message, it.location) })
} }
} }
finally { finally {
@@ -27,10 +27,10 @@ import javax.script.ScriptContext
import javax.script.ScriptEngine import javax.script.ScriptEngine
import kotlin.script.StandardScriptTemplate import kotlin.script.StandardScriptTemplate
class KotlinJsr232JvmLocalScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() { class KotlinJsr223JvmLocalScriptEngineFactory : KotlinJsr223JvmScriptEngineFactoryBase() {
override fun getScriptEngine(): ScriptEngine = override fun getScriptEngine(): ScriptEngine =
KotlinJsr232JvmLocalScriptEngine( KotlinJsr223JvmLocalScriptEngine(
Disposer.newDisposable(), Disposer.newDisposable(),
this, this,
listOf(kotlinRuntimeJar), listOf(kotlinRuntimeJar),
@@ -88,11 +88,11 @@ private fun makeSerializableArgumentsForTemplateWithArgsAndBindings(ctx: ScriptC
private fun File.existsOrNull(): File? = existsAndCheckOrNull { true } private fun File.existsOrNull(): File? = existsAndCheckOrNull { true }
private inline fun File.existsAndCheckOrNull(check: (File.() -> Boolean)): File? = if (exists() && check()) this else null private inline fun File.existsAndCheckOrNull(check: (File.() -> Boolean)): File? = if (exists() && check()) this else null
private val kotlinCompilerJar = System.getProperty("KOTLIN_COMPILER_JAR")?.let(::File)?.existsOrNull() private val kotlinCompilerJar = System.getProperty("kotlin.compiler.jar")?.let(::File)?.existsOrNull()
?: getPathUtilJar().existsAndCheckOrNull { name == KOTLIN_COMPILER_JAR } ?: getPathUtilJar().existsAndCheckOrNull { name == KOTLIN_COMPILER_JAR }
?: throw FileNotFoundException("Cannot find kotlin compiler jar, set KOTLIN_COMPILER_JAR property to proper location") ?: throw FileNotFoundException("Cannot find kotlin compiler jar, set kotlin.compiler.jar property to proper location")
private val kotlinRuntimeJar = System.getProperty("KOTLIN_JAVA_RUNTIME_JAR")?.let(::File)?.existsOrNull() private val kotlinRuntimeJar = System.getProperty("kotlin.java.runtime.jar")?.let(::File)?.existsOrNull()
?: kotlinCompilerJar.let { File(it.parentFile, KOTLIN_JAVA_RUNTIME_JAR) }.existsOrNull() ?: kotlinCompilerJar.let { File(it.parentFile, KOTLIN_JAVA_RUNTIME_JAR) }.existsOrNull()
?: getResourcePathForClass(StandardScriptTemplate::class.java).existsOrNull() ?: getResourcePathForClass(StandardScriptTemplate::class.java).existsOrNull()
?: throw FileNotFoundException("Cannot find kotlin runtime jar, set KOTLIN_JAVA_RUNTIME_JAR property to proper location") ?: throw FileNotFoundException("Cannot find kotlin runtime jar, set kotlin.java.runtime.jar property to proper location")
@@ -16,10 +16,14 @@
package org.jetbrains.kotlin.script.util package org.jetbrains.kotlin.script.util
// in case of flat or direct resolvers the value should be a direct path or file name of a jar respectively
// in case of maven resolver the maven coordinates string is accepted (resolved with com.jcabi.aether library)
@Target(AnnotationTarget.FILE, AnnotationTarget.EXPRESSION, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) @Target(AnnotationTarget.FILE, AnnotationTarget.EXPRESSION, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class DependsOn(val value: String) annotation class DependsOn(val value: String)
// only flat directory repositories are supported now, so value should be a path to a directory with jars
// TODO: support other types of repos
@Target(AnnotationTarget.FILE, AnnotationTarget.EXPRESSION, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) @Target(AnnotationTarget.FILE, AnnotationTarget.EXPRESSION, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class Repository(val value: String) annotation class Repository(val value: String)
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.script.util.resolvers.DirectResolver
import org.jetbrains.kotlin.script.util.resolvers.FlatLibDirectoryResolver import org.jetbrains.kotlin.script.util.resolvers.FlatLibDirectoryResolver
import org.jetbrains.kotlin.script.util.resolvers.MavenResolver import org.jetbrains.kotlin.script.util.resolvers.MavenResolver
import org.jetbrains.kotlin.script.util.resolvers.Resolver import org.jetbrains.kotlin.script.util.resolvers.Resolver
import org.jetbrains.kotlin.utils.addToStdlib.check
import java.io.File import java.io.File
import java.lang.Exception import java.lang.Exception
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
@@ -34,29 +35,28 @@ open class KotlinAnnotatedScriptDependenciesResolver(val baseClassPath: List<Fil
{ {
private val resolvers: MutableList<Resolver> = resolvers.toMutableList() private val resolvers: MutableList<Resolver> = resolvers.toMutableList()
inner class ResolvedDependencies(previousDependencies: KotlinScriptExternalDependencies?, depsFromAnnotations: List<File> ) : KotlinScriptExternalDependencies {
override val classpath = if (resolvers.isEmpty()) baseClassPath else baseClassPath + depsFromAnnotations
override val imports = if (previousDependencies != null) emptyList() else listOf(DependsOn::class.java.`package`.name + ".*")
}
@AcceptedAnnotations(DependsOn::class, Repository::class) @AcceptedAnnotations(DependsOn::class, Repository::class)
override fun resolve(script: ScriptContents, override fun resolve(script: ScriptContents,
environment: Map<String, Any?>?, environment: Map<String, Any?>?,
report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit, report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit,
previousDependencies: KotlinScriptExternalDependencies? previousDependencies: KotlinScriptExternalDependencies?
): Future<KotlinScriptExternalDependencies?> ): Future<KotlinScriptExternalDependencies?> {
= (if (previousDependencies != null && resolveFromAnnotations(script).isEmpty()) previousDependencies val depsFromAnnotations: List<File> = resolveFromAnnotations(script)
else return (if (previousDependencies != null && depsFromAnnotations.isEmpty()) previousDependencies
object : KotlinScriptExternalDependencies { else ResolvedDependencies(previousDependencies, depsFromAnnotations)
override val classpath: Iterable<File> = if (resolvers.isEmpty()) baseClassPath else baseClassPath + resolveFromAnnotations(script) ).asFuture()
override val imports: Iterable<String> = }
previousDependencies?.let { emptyList<String>() } ?: listOf(DependsOn::class.java.`package`.name + ".*")
}
).asFuture()
private fun resolveFromAnnotations(script: ScriptContents): List<File> { private fun resolveFromAnnotations(script: ScriptContents): List<File> {
script.annotations.forEach { script.annotations.forEach {
when (it) { when (it) {
is Repository -> is Repository -> File(it.value).check { it.exists() && it.isDirectory }?.let { resolvers.add(FlatLibDirectoryResolver(it)) }
when { ?: throw IllegalArgumentException("Illegal argument for Repository annotation: ${it.value}")
File(it.value).run { exists() && isDirectory } -> resolvers.add(FlatLibDirectoryResolver(File(it.value)))
else -> throw IllegalArgumentException("Illegal argument for Repository annotation: ${it.value}")
}
is DependsOn -> {} is DependsOn -> {}
is InvalidScriptResolverAnnotation -> throw Exception("Invalid annotation ${it.name}", it.error) is InvalidScriptResolverAnnotation -> throw Exception("Invalid annotation ${it.name}", it.error)
else -> throw Exception("Unknown annotation ${it.javaClass}") else -> throw Exception("Unknown annotation ${it.javaClass}")
@@ -83,7 +83,7 @@ private fun classpathFromClassloader(classLoader: ClassLoader): List<File>? =
private fun classpathFromClasspathProperty(): List<File>? = private fun classpathFromClasspathProperty(): List<File>? =
System.getProperty("java.class.path")?.let { System.getProperty("java.class.path")?.let {
it.split(String.format("\\%s", File.pathSeparatorChar).toRegex()).dropLastWhile(String::isEmpty).toTypedArray() it.split(String.format("\\%s", File.pathSeparatorChar).toRegex()).dropLastWhile(String::isEmpty)
.map(::File) .map(::File)
} }
@@ -104,14 +104,12 @@ done
scriptFileName: String, scriptFileName: String,
scriptTemplate: KClass<out Any>, scriptTemplate: KClass<out Any>,
environment: Map<String, Any?>? = null, environment: Map<String, Any?>? = null,
runIsolated: Boolean = true,
suppressOutput: Boolean = false): Class<*>? = suppressOutput: Boolean = false): Class<*>? =
compileScriptImpl("src/test/resources/scripts/" + scriptFileName, KotlinScriptDefinitionFromAnnotatedTemplate(scriptTemplate, null, null, environment), runIsolated, suppressOutput) compileScriptImpl("src/test/resources/scripts/" + scriptFileName, KotlinScriptDefinitionFromAnnotatedTemplate(scriptTemplate, null, null, environment), suppressOutput)
private fun compileScriptImpl( private fun compileScriptImpl(
scriptPath: String, scriptPath: String,
scriptDefinition: KotlinScriptDefinition, scriptDefinition: KotlinScriptDefinition,
runIsolated: Boolean,
suppressOutput: Boolean): Class<*>? suppressOutput: Boolean): Class<*>?
{ {
val paths = PathUtil.getKotlinPathsForDistDirectory() val paths = PathUtil.getKotlinPathsForDistDirectory()
@@ -123,7 +121,7 @@ done
try { try {
val configuration = CompilerConfiguration().apply { val configuration = CompilerConfiguration().apply {
addJvmClasspathRoots(PathUtil.getJdkClassesRoots()) addJvmClasspathRoots(PathUtil.getJdkClassesRoots())
val rtJar = System.getProperty("KOTLIN_JAVA_RUNTIME_JAR") val rtJar = System.getProperty("kotlin.java.runtime.jar")
Assert.assertNotNull(rtJar) Assert.assertNotNull(rtJar)
addJvmClasspathRoot(File(rtJar)) addJvmClasspathRoot(File(rtJar))
put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
@@ -141,14 +139,12 @@ done
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script-util-test") put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script-util-test")
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
put(JVMConfigurationKeys.INCLUDE_RUNTIME, true)
} }
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
try { try {
return if (runIsolated) KotlinToJVMBytecodeCompiler.compileScript(environment, paths) return KotlinToJVMBytecodeCompiler.compileScript(environment, paths)
else KotlinToJVMBytecodeCompiler.compileScript(environment, this.javaClass.classLoader)
} }
catch (e: CompilationException) { catch (e: CompilationException) {
messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e), messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e),
@@ -183,9 +179,13 @@ done
val outStream = ByteArrayOutputStream() val outStream = ByteArrayOutputStream()
val prevOut = System.out val prevOut = System.out
System.setOut(PrintStream(outStream)) System.setOut(PrintStream(outStream))
body() try {
System.out.flush() body()
System.setOut(prevOut) }
finally {
System.out.flush()
System.setOut(prevOut)
}
return outStream.toString() return outStream.toString()
} }
} }
@@ -1,8 +0,0 @@
println("Hello, world!")
if (bindings.isNotEmpty()) {
println(bindings.joinToString { "${it.key} = ${it.value}" })
}
println("done")
@@ -1,8 +0,0 @@
println("Hello, world!")
if (args.isNotEmpty()) {
println(args.joinToString())
}
println("done")
@@ -1,8 +0,0 @@
println("Hello, world!")
if (args.isNotEmpty()) {
println(args.joinToString())
}
println("done")