Don't create default importing scopes for REPL snippets
Default scopes should be created only once, for the first snippet. All following snippets should not create new default importing scopes. #KT-35651 fixed
This commit is contained in:
committed by
Ilya Chernikov
parent
c3cbfe34c4
commit
743abea690
@@ -20,6 +20,8 @@ dependencies {
|
||||
allTestsRuntime(commonDep("junit"))
|
||||
testCompile(project(":kotlin-scripting-ide-services-unshaded"))
|
||||
testCompile(project(":kotlin-scripting-compiler"))
|
||||
testCompile(project(":kotlin-scripting-dependencies"))
|
||||
testCompile(project(":kotlin-main-kts"))
|
||||
testCompile(project(":compiler:cli-common"))
|
||||
|
||||
testRuntimeOnly(project(":kotlin-compiler"))
|
||||
|
||||
+48
@@ -7,15 +7,19 @@ package org.jetbrains.kotlin.scripting.ide_services
|
||||
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocationWithRange
|
||||
import org.jetbrains.kotlin.scripting.ide_services.test_util.*
|
||||
import org.jetbrains.kotlin.scripting.ide_services.test_util.JvmTestRepl
|
||||
import org.jetbrains.kotlin.scripting.ide_services.test_util.SourceCodeTestImpl
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
import kotlin.script.experimental.jvm.updateClasspath
|
||||
import kotlin.script.experimental.util.LinkedSnippet
|
||||
import kotlin.script.experimental.util.get
|
||||
import kotlin.script.experimental.jvm.util.isError
|
||||
import kotlin.script.experimental.jvm.util.isIncomplete
|
||||
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContext
|
||||
|
||||
// Adapted form GenericReplTest
|
||||
|
||||
@@ -268,6 +272,50 @@ class JvmIdeServicesTest : TestCase() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun testDependency() {
|
||||
val resolver = ScriptDependenciesResolver()
|
||||
|
||||
val conf = ScriptCompilationConfiguration {
|
||||
jvm {
|
||||
updateClasspath(scriptCompilationClasspathFromContext("test", classLoader = DependsOn::class.java.classLoader))
|
||||
}
|
||||
defaultImports(DependsOn::class)
|
||||
refineConfiguration {
|
||||
onAnnotations(DependsOn::class, handler = { configureMavenDepsOnAnnotations(it, resolver) })
|
||||
}
|
||||
}
|
||||
|
||||
JvmTestRepl(conf)
|
||||
.use { repl ->
|
||||
/*
|
||||
The only source file in test.jar contains following code:
|
||||
|
||||
package example.dependency
|
||||
infix fun String.to(that: String) = this + that
|
||||
*/
|
||||
assertEvalUnit(
|
||||
repl, """
|
||||
@file:DependsOn("plugins/scripting/scripting-ide-services-test/testData/KT-35651-test.jar")
|
||||
import example.dependency.*
|
||||
|
||||
val x = listOf<String>()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// This snippet is needed to be evaluated to ensure that importing scopes were created
|
||||
// (but default ones were not)
|
||||
assertEvalUnit(
|
||||
repl, """
|
||||
import kotlin.math.*
|
||||
|
||||
val y = listOf<String>()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertEvalResult(repl, """ "a" to "a" """, "aa")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LegacyReplTestLong : TestCase() {
|
||||
|
||||
+21
@@ -332,6 +332,27 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
|
||||
run(setupDefaultImportsCompletionRun)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLongCompilationsWithImport() = test {
|
||||
// This test normally completes in about 5-10s
|
||||
// Log should show slow _linear_ compilation time growth
|
||||
|
||||
val compileWriter = System.out.writer()
|
||||
|
||||
for (i in 1..120) {
|
||||
run {
|
||||
code = """
|
||||
import kotlin.math.*
|
||||
val dataFrame = mapOf("x" to sin(3.0))
|
||||
val e = "str"
|
||||
""".trimIndent()
|
||||
doCompile
|
||||
|
||||
loggingInfo = CSVLoggingInfo(compile = CSVLoggingInfoItem(compileWriter, i, "compile;"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLongRunningCompilationWithReceiver() = test {
|
||||
// This test normally completes in about 8-13s
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.ide_services.test_util
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.kotlin.mainKts.impl.IvyResolver
|
||||
import java.io.File
|
||||
import kotlin.script.dependencies.ScriptContents
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.dependencies.*
|
||||
import kotlin.script.experimental.jvm.withUpdatedClasspath
|
||||
|
||||
// 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)
|
||||
@Repeatable
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class DependsOn(val value: String = "")
|
||||
|
||||
open class ScriptDependenciesResolver {
|
||||
|
||||
private val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), IvyResolver())
|
||||
private val addedClasspath = mutableListOf<File>()
|
||||
|
||||
fun resolveFromAnnotations(script: ScriptContents): ResultWithDiagnostics<List<File>> {
|
||||
val scriptDiagnostics = mutableListOf<ScriptDiagnostic>()
|
||||
val classpath = mutableListOf<File>()
|
||||
|
||||
script.annotations.forEach { annotation ->
|
||||
when (annotation) {
|
||||
is DependsOn -> {
|
||||
try {
|
||||
when (val result = runBlocking { resolver.resolve(annotation.value) }) {
|
||||
is ResultWithDiagnostics.Failure -> {
|
||||
val diagnostics = ScriptDiagnostic(
|
||||
ScriptDiagnostic.unspecifiedError,
|
||||
"Failed to resolve ${annotation.value}:\n" + result.reports.joinToString("\n") { it.message })
|
||||
scriptDiagnostics.add(diagnostics)
|
||||
}
|
||||
is ResultWithDiagnostics.Success -> {
|
||||
addedClasspath.addAll(result.value)
|
||||
classpath.addAll(result.value)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
val diagnostic =
|
||||
ScriptDiagnostic(ScriptDiagnostic.unspecifiedError, "Unhandled exception during resolve", exception = e)
|
||||
scriptDiagnostics.add(diagnostic)
|
||||
}
|
||||
}
|
||||
else -> throw Exception("Unknown annotation ${annotation.javaClass}")
|
||||
}
|
||||
}
|
||||
return if (scriptDiagnostics.isEmpty()) classpath.asSuccess()
|
||||
else makeFailureResult(scriptDiagnostics)
|
||||
}
|
||||
}
|
||||
|
||||
fun configureMavenDepsOnAnnotations(
|
||||
context: ScriptConfigurationRefinementContext,
|
||||
resolver: ScriptDependenciesResolver
|
||||
): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
||||
val annotations = context.collectedData?.get(ScriptCollectedData.foundAnnotations)?.takeIf { it.isNotEmpty() }
|
||||
?: return context.compilationConfiguration.asSuccess()
|
||||
val scriptContents = object : ScriptContents {
|
||||
override val annotations: Iterable<Annotation> = annotations
|
||||
override val file: File? = null
|
||||
override val text: CharSequence? = null
|
||||
}
|
||||
return try {
|
||||
resolver.resolveFromAnnotations(scriptContents)
|
||||
.onSuccess { classpath ->
|
||||
context.compilationConfiguration
|
||||
.let { if (classpath.isEmpty()) it else it.withUpdatedClasspath(classpath) }
|
||||
.asSuccess()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
ResultWithDiagnostics.Failure(e.asDiagnostics(path = context.script.locationId))
|
||||
}
|
||||
}
|
||||
+5
-8
@@ -9,20 +9,17 @@ import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.kotlin.scripting.ide_services.compiler.KJvmReplCompilerWithIdeServices
|
||||
import java.io.Closeable
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.script.experimental.api.CompiledSnippet
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.SourceCode
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.jvm.BasicJvmReplEvaluator
|
||||
import kotlin.script.experimental.util.LinkedSnippet
|
||||
import kotlin.script.experimental.jvm.util.toSourceCodePosition
|
||||
|
||||
internal class JvmTestRepl : Closeable {
|
||||
internal class JvmTestRepl (
|
||||
private val compileConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
|
||||
private val evalConfiguration: ScriptEvaluationConfiguration = simpleScriptEvaluationConfiguration,
|
||||
) : Closeable {
|
||||
private val currentLineCounter = AtomicInteger(0)
|
||||
|
||||
private val compileConfiguration = simpleScriptCompilationConfiguration
|
||||
private val evalConfiguration = simpleScriptEvaluationConfiguration
|
||||
|
||||
fun nextCodeLine(code: String): SourceCode =
|
||||
SourceCodeTestImpl(
|
||||
currentLineCounter.getAndIncrement(),
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user