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);
}
else {
// TODO: check if it is correct way to find a primary constructor
Collection<ClassConstructorDescriptor> ctorDescriptors = superclass.getConstructors();
assert ctorDescriptors.size() == 1;
ConstructorDescriptor ctorDesc = ctorDescriptors.iterator().next();
ConstructorDescriptor ctorDesc = superclass.getUnsubstitutedPrimaryConstructor();
assert ctorDesc != null;
iv.load(0, classType);
@@ -38,6 +38,6 @@ data class CompilerMessageLocation private constructor(
): CompilerMessageLocation =
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 {
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) {
val (ctorArgs, scriptArgsLeft) = ctor.parameters.fold(Pair(emptyList<Any>(), scriptArgs), ::foldingFunc)
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 {
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 {
private var classLoader: org.jetbrains.kotlin.cli.common.repl.ReplClassLoader =
org.jetbrains.kotlin.cli.common.repl.ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
private var classLoader: org.jetbrains.kotlin.cli.common.repl.ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
private val classLoaderLock = ReentrantReadWriteLock()
private class ClassWithInstance(val klass: Class<*>, val instance: Any)
@@ -42,7 +41,7 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
classLoaderLock.read {
if (newClasspath.isNotEmpty()) {
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") }
@@ -55,14 +54,9 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
val constructorParams: Array<Class<*>> =
(compiledLoadedClassesHistory.map { it.second.klass } +
(scriptArgs?.asIterable()
?.mapIndexed { i, it ->
if (i < (scriptArgsTypes?.size ?: 0)) scriptArgsTypes!![i] else it?.javaClass ?: Any::class.java
}
?: emptyList()
)
(scriptArgs?.mapIndexed { i, it -> scriptArgsTypes?.getOrNull(i) ?: it?.javaClass ?: Any::class.java } ?: emptyList())
).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 scriptInstance =
@@ -85,4 +79,7 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
companion object {
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 {
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 hashCode(): Int = path.hashCode() + Arrays.hashCode(bytes)
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\""
}
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\""
}
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() {
class Runtime(message: String) : 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 {
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.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.KotlinScriptDefinition
@@ -74,28 +73,27 @@ open class GenericReplChecker(
fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
@Synchronized
override fun check(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplCheckResult {
synchronized(this) {
val virtualFile =
LightVirtualFile("line${codeLine.no}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply {
charset = CharsetToolkit.UTF8_CHARSET
}
val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
?: error("Script file not analyzed at line ${codeLine.no}: ${codeLine.code}")
val virtualFile =
LightVirtualFile("line${codeLine.no}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply {
charset = CharsetToolkit.UTF8_CHARSET
}
val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
?: 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) {
lineState = LineState(codeLine, psiFile, errorHolder)
}
if (!syntaxErrorReport.isHasErrors) {
lineState = LineState(codeLine, psiFile, errorHolder)
}
return when {
syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete
syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderedDiagnostics)
else -> ReplCheckResult.Ok
}
return when {
syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete
syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderedDiagnostics)
else -> ReplCheckResult.Ok
}
}
}
@@ -113,61 +111,59 @@ open class GenericReplCompiler(
private val descriptorsHistory = arrayListOf<Pair<ReplCodeLine, ScriptDescriptor>>()
@Synchronized
override fun compile(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplCompileResult {
synchronized(this) {
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())
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())
}
companion object {
@@ -188,9 +184,9 @@ open class GenericRepl(
private val compiledEvaluator = GenericReplCompiledEvaluator(compilerConfiguration.jvmClasspathRoots, baseClassloader, scriptArgs, scriptArgsTypes)
override fun eval(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplEvalResult = synchronized(this) {
return compileAndEval(this, compiledEvaluator, codeLine, history)
}
@Synchronized
override fun eval(codeLine: ReplCodeLine, history: Iterable<ReplCodeLine>): ReplEvalResult =
compileAndEval(this, compiledEvaluator, codeLine, history)
}
@@ -390,9 +390,10 @@ class CompileServiceImpl(
synchronized(state.sessions) {
// 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()
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)
synchronized(state.clientProxies) {
state.clientProxies.removeAll(
state.clientProxies.filter { !it.isAlive }.apply { anyDead = true }.map {
it.dispose()
it
})
}
synchronized(state.clientProxies) {
state.clientProxies.removeAll(
state.clientProxies.filter { !it.isAlive }.map {
it.dispose()
anyDead = true
it
})
}
if (state.clientProxies.isEmpty() && compilationsCounter.get() > 0 && !state.delayedShutdownQueued.get()) {
log.info("No more clients left, delayed shutdown in ${daemonOptions.shutdownDelayMilliseconds}ms")
shutdownWithDelay()
@@ -52,11 +52,11 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
defAnn != null ->
try {
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) {
log.error("[kts] Script def error ${ex.message}")
log.warn("[kts] Script def error ${ex.message}")
null
}
else -> BasicScriptDependenciesResolver()
@@ -37,7 +37,7 @@ fun KotlinScriptDefinition.getScriptParameters(scriptDescriptor: ScriptDescripto
fun getKotlinType(scriptDescriptor: ScriptDescriptor, kClass: KClass<out Any>): KotlinType =
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 =
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.PrintingMessageCollector
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.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase
import org.jetbrains.kotlin.script.KotlinScriptDefinition
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 java.io.File
import java.net.URLClassLoader
@@ -48,7 +48,7 @@ class GenericReplTest : TestCase() {
val res1 = repl.replCompiler?.check(ReplCodeLine(0, "val x ="), emptyList())
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 res2c = res2 as? ReplCompileResult.CompiledClasses
TestCase.assertNotNull("Unexpected compile result: $res2", res2c)
@@ -89,12 +89,10 @@ internal class TestRepl(
templateClasspath: List<File>,
templateClassName: String
) {
private val configuration = CompilerConfiguration().apply {
addJvmClasspathRoots(PathUtil.getJdkClassesRoots())
addJvmClasspathRoots(PathUtil.getKotlinPathsForCompiler().let { listOf(it.runtimePath, it.reflectPath) })
addJvmClasspathRoots(templateClasspath)
private val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.MOCK_JDK, *templateClasspath.toTypedArray()).apply {
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script")
}
private fun makeScriptDefinition(templateClasspath: List<File>, templateClassName: String): KotlinScriptDefinition {
val classloader = URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), this.javaClass.classLoader)
val cls = classloader.loadClass(templateClassName)
@@ -510,7 +510,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
val res0 = repl.check(ReplCodeLine(0, "val x ="), emptyList())
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 res1c = res1 as? ReplCompileResult.CompiledClasses
TestCase.assertNotNull("Unexpected compile result: $res1", res1c)
@@ -181,29 +181,45 @@ class ScriptTemplateTest {
fun testScriptWithStandardTemplate() {
val aClass = compileScript("fib_std.kts", StandardScriptTemplate::class, runIsolated = false)
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
fun testScriptWithPackage() {
val aClass = compileScript("fib.pkg.kts", ScriptWithIntParam::class)
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
fun testScriptWithScriptDefinition() {
val aClass = compileScript("fib.kts", ScriptWithIntParam::class)
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
fun testScriptWithParamConversion() {
val aClass = compileScript("fib.kts", ScriptWithIntParam::class)
Assert.assertNotNull(aClass)
val anObj = tryConstructScriptClass(aClass!!, listOf("4"))
Assert.assertNotNull(anObj)
captureOut {
val anObj = tryConstructScriptClass(aClass!!, listOf("4"))
Assert.assertNotNull(anObj)
}.let {
assertEqualsTrimmed(NUM_4_LINE + FIB_SCRIPT_OUTPUT_TAIL, it)
}
}
@Test
@@ -275,8 +291,6 @@ class ScriptTemplateTest {
open class TestKotlinScriptDummyDependenciesResolver : ScriptDependenciesResolver {
private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() }
@AcceptedAnnotations(DependsOn::class, DependsOnTwo::class)
override fun resolve(script: ScriptContents,
environment: Map<String, Any?>?,
@@ -39,9 +39,13 @@ internal fun captureOut(body: () -> Unit): String {
val outStream = ByteArrayOutputStream()
val prevOut = System.out
System.setOut(PrintStream(outStream))
body()
System.out.flush()
System.setOut(prevOut)
try {
body()
}
finally {
System.out.flush()
System.setOut(prevOut)
}
return outStream.toString()
}