Add new REPL API JVM implementation

This commit is contained in:
Ilya Muradyan
2020-02-28 19:45:20 +03:00
committed by Ilya Chernikov
parent 4c2c44b106
commit d2fec96f38
51 changed files with 2557 additions and 503 deletions
@@ -0,0 +1,50 @@
plugins {
kotlin("jvm")
}
jvmTarget = "1.8"
val allTestsRuntime by configurations.creating
val testCompile by configurations
testCompile.extendsFrom(allTestsRuntime)
val embeddableTestRuntime by configurations.creating {
extendsFrom(allTestsRuntime)
}
dependencies {
allTestsRuntime(commonDep("junit"))
testCompile(project(":kotlin-scripting-ide-services"))
testCompile(project(":kotlin-scripting-compiler"))
testCompile(project(":compiler:cli-common"))
testRuntimeOnly(project(":kotlin-compiler"))
testRuntimeOnly(commonDep("org.jetbrains.intellij.deps", "trove4j"))
testRuntimeOnly(project(":idea:ide-common")) { isTransitive = false }
embeddableTestRuntime(project(":kotlin-scripting-ide-services-embeddable"))
embeddableTestRuntime(project(":kotlin-compiler-embeddable"))
embeddableTestRuntime(testSourceSet.output)
}
sourceSets {
"main" {}
"test" { projectDefault() }
}
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
kotlinOptions.freeCompilerArgs += "-Xallow-kotlin-package"
}
projectTest(parallel = true) {
dependsOn(":dist")
workingDir = rootDir
}
// This doesn;t work now due to conflicts between embeddable compiler contents and intellij sdk modules
// To make it work, the dependencies to the intellij sdk should be eliminated
projectTest(taskName = "embeddableTest", parallel = true) {
workingDir = rootDir
dependsOn(embeddableTestRuntime)
classpath = embeddableTestRuntime
}
@@ -0,0 +1,426 @@
/*
* 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
import junit.framework.TestCase
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
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.util.LinkedSnippet
import kotlin.script.experimental.util.get
import kotlin.script.experimental.jvm.util.isError
import kotlin.script.experimental.jvm.util.isIncomplete
// Adapted form GenericReplTest
// Artificial split into several testsuites, to speed up parallel testing
class JvmIdeServicesTest : TestCase() {
fun testReplBasics() {
JvmTestRepl()
.use { repl ->
val res1 = repl.compile(
SourceCodeTestImpl(
0,
"val x ="
)
)
assertTrue("Unexpected check results: $res1", res1.isIncomplete())
assertEvalResult(
repl,
"val l1 = listOf(1 + 2)\nl1.first()",
3
)
assertEvalUnit(
repl,
"val x = 5"
)
assertEvalResult(
repl,
"x + 2",
7
)
}
}
fun testReplErrors() {
JvmTestRepl()
.use { repl ->
repl.compileAndEval(repl.nextCodeLine("val x = 10"))
val res = repl.compileAndEval(repl.nextCodeLine("java.util.fish"))
assertTrue("Expected compile error", res.first.isError())
val result = repl.compileAndEval(repl.nextCodeLine("x"))
assertEquals(res.second.toString(), 10, (result.second?.result as ResultValue.Value?)?.value)
}
}
fun testReplErrorsWithLocations() {
JvmTestRepl()
.use { repl ->
val (compileResult, evalResult) = repl.compileAndEval(
repl.nextCodeLine(
"""
val foobar = 78
val foobaz = "dsdsda"
val ddd = ppp
val ooo = foobar
""".trimIndent()
)
)
if (compileResult.isError() && evalResult == null) {
val errors = compileResult.getErrors()
val loc = errors.location
if (loc == null) {
fail("Location shouldn't be null")
} else {
assertEquals(3, loc.line)
assertEquals(11, loc.column)
assertEquals(3, loc.lineEnd)
assertEquals(14, loc.columnEnd)
}
} else {
fail("Result should be an error")
}
}
}
fun testReplErrorsAndWarningsWithLocations() {
JvmTestRepl()
.use { repl ->
val (compileResult, evalResult) = repl.compileAndEval(
repl.nextCodeLine(
"""
fun f() {
val x = 3
val y = ooo
return y
}
""".trimIndent()
)
)
if (compileResult.isError() && evalResult == null) {
val errors = compileResult.getErrors()
val loc = errors.location
if (loc == null) {
fail("Location shouldn't be null")
} else {
assertEquals(3, loc.line)
assertEquals(13, loc.column)
assertEquals(3, loc.lineEnd)
assertEquals(16, loc.columnEnd)
}
} else {
fail("Result should be an error")
}
}
}
fun testReplSyntaxErrorsChecked() {
JvmTestRepl()
.use { repl ->
val res = repl.compileAndEval(repl.nextCodeLine("data class Q(val x: Int, val: String)"))
assertTrue("Expected compile error", res.first.isError())
}
}
private fun checkContains(actual: Sequence<SourceCodeCompletionVariant>, expected: Set<String>) {
val variants = actual.map { it.displayText }.toHashSet()
for (displayText in expected) {
if (!variants.contains(displayText)) {
fail("Element $displayText should be in $variants")
}
}
}
private fun checkDoesntContain(actual: Sequence<SourceCodeCompletionVariant>, expected: Set<String>) {
val variants = actual.map { it.displayText }.toHashSet()
for (displayText in expected) {
if (variants.contains(displayText)) {
fail("Element $displayText should NOT be in $variants")
}
}
}
fun testCompletion() = JvmTestRepl().use { repl ->
repl.compileAndEval(
repl.nextCodeLine(
"""
class AClass(val prop_x: Int) {
fun filter(xxx: (AClass).(AClass) -> Boolean): AClass {
return if(this.xxx(this))
this
else
this
}
}
val AClass.prop_y: Int
get() = prop_x * prop_x
val df = AClass(10)
val pro = "some string"
""".trimIndent()
)
)
val codeLine1 = repl.nextCodeLine(
"""
df.filter{pr}
""".trimIndent()
)
val completionList1 = repl.complete(codeLine1, 12)
checkContains(completionList1.valueOrThrow(), setOf("prop_x", "prop_y", "pro", "println(Double)"))
}
fun testPackageCompletion() = JvmTestRepl().use { repl ->
val codeLine1 = repl.nextCodeLine(
"""
import java.
val xval = 3
""".trimIndent()
)
val completionList1 = repl.complete(codeLine1, 12)
checkContains(completionList1.valueOrThrow(), setOf("lang", "math"))
checkDoesntContain(completionList1.valueOrThrow(), setOf("xval"))
}
fun testFileCompletion() = JvmTestRepl().use { repl ->
val codeLine1 = repl.nextCodeLine(
"""
val fname = "
""".trimIndent()
)
val completionList1 = repl.complete(codeLine1, 13)
val files = File(".").listFiles()?.map { it.name }
assertFalse("There should be files in current dir", files.isNullOrEmpty())
checkContains(completionList1.valueOrThrow(), files!!.toSet())
}
fun testReplCodeFormat() {
JvmTestRepl()
.use { repl ->
val codeLine0 =
SourceCodeTestImpl(0, "val l1 = 1\r\nl1\r\n")
val res = repl.compile(codeLine0)
assertTrue("Unexpected compile result: $res", res is ResultWithDiagnostics.Success<*>)
}
}
fun testRepPackage() {
JvmTestRepl()
.use { repl ->
assertEvalResult(
repl,
"package mypackage\n\nval x = 1\nx+2",
3
)
assertEvalResult(
repl,
"x+4",
5
)
}
}
fun testReplResultFieldWithFunction() {
JvmTestRepl()
.use { repl ->
assertEvalResultIs<Function0<Int>>(
repl,
"{ 1 + 2 }"
)
assertEvalResultIs<Function0<Int>>(
repl,
"res0"
)
assertEvalResult(
repl,
"res0()",
3
)
}
}
fun testReplResultField() {
JvmTestRepl()
.use { repl ->
assertEvalResult(
repl,
"5 * 4",
20
)
assertEvalResult(
repl,
"res0 + 3",
23
)
}
}
}
// Artificial split into several testsuites, to speed up parallel testing
class LegacyReplTestLong1 : TestCase() {
fun test256Evals() {
JvmTestRepl()
.use { repl ->
repl.compileAndEval(
SourceCodeTestImpl(
0,
"val x0 = 0"
)
)
val evals = 256
for (i in 1..evals) {
repl.compileAndEval(
SourceCodeTestImpl(
i,
"val x$i = x${i - 1} + 1"
)
)
}
val (_, evaluated) = repl.compileAndEval(
SourceCodeTestImpl(
evals + 1,
"x$evals"
)
)
assertEquals(evaluated.toString(), evals, (evaluated?.result as ResultValue.Value?)?.value)
}
}
}
// Artificial split into several testsuites, to speed up parallel testing
class LegacyReplTestLong2 : TestCase() {
fun testReplSlowdownKt22740() {
JvmTestRepl()
.use { repl ->
repl.compileAndEval(
SourceCodeTestImpl(
0,
"class Test<T>(val x: T) { fun <R> map(f: (T) -> R): R = f(x) }".trimIndent()
)
)
// We expect that analysis time is not exponential
for (i in 1..60) {
repl.compileAndEval(
SourceCodeTestImpl(
i,
"fun <T> Test<T>.map(f: (T) -> Double): List<Double> = listOf(f(this.x))"
)
)
}
}
}
}
private fun JvmTestRepl.compileAndEval(codeLine: SourceCode): Pair<ResultWithDiagnostics<LinkedSnippet<out CompiledSnippet>>, EvaluatedSnippet?> {
val compRes = compile(codeLine)
val evalRes = compRes.valueOrNull()?.let {
eval(it)
}
return compRes to evalRes?.valueOrNull().get()
}
private fun assertEvalUnit(
repl: JvmTestRepl,
@Suppress("SameParameterValue")
line: String
) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Unit)
}
private fun <R> assertEvalResult(repl: JvmTestRepl, line: String, expectedResult: R) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Value)
TestCase.assertEquals(expectedResult, (valueResult.result as ResultValue.Value).value)
}
private inline fun <reified R> assertEvalResultIs(repl: JvmTestRepl, line: String) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Value)
TestCase.assertTrue((valueResult.result as ResultValue.Value).value is R)
}
private fun checkCompile(repl: JvmTestRepl, line: String): LinkedSnippet<KJvmCompiledScript>? {
val codeLine = repl.nextCodeLine(line)
val compileResult = repl.compile(codeLine)
return compileResult.valueOrNull()
}
private data class CompilationErrors(
val message: String,
val location: CompilerMessageLocation?
)
private fun <T> ResultWithDiagnostics<T>.getErrors(): CompilationErrors =
CompilationErrors(
reports.joinToString("\n") { report ->
report.location?.let { loc ->
CompilerMessageLocation.create(
report.sourcePath,
loc.start.line,
loc.start.col,
loc.end?.line,
loc.end?.col,
null
)?.toString()?.let {
"$it "
}
}.orEmpty() + report.message
},
reports.firstOrNull {
when (it.severity) {
ScriptDiagnostic.Severity.ERROR -> true
ScriptDiagnostic.Severity.FATAL -> true
else -> false
}
}?.let {
val loc = it.location ?: return@let null
CompilerMessageLocation.create(
it.sourcePath,
loc.start.line,
loc.start.col,
loc.end?.line,
loc.end?.col,
null
)
}
)
@@ -0,0 +1,379 @@
/*
* Copyright 2010-2019 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
import junit.framework.TestCase
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.scripting.ide_services.compiler.KJvmReplCompilerWithIdeServices
import org.jetbrains.kotlin.scripting.ide_services.test_util.SourceCodeTestImpl
import org.jetbrains.kotlin.scripting.ide_services.test_util.simpleScriptCompilationConfiguration
import org.jetbrains.kotlin.scripting.ide_services.test_util.toList
import org.junit.Assert
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.script.experimental.api.*
class ReplCompletionAndErrorsAnalysisTest : TestCase() {
@Test
fun testTrivial() = test {
run {
doCompile
code = """
data class AClass(val memx: Int, val memy: String)
data class BClass(val memz: String, val mema: AClass)
val foobar = 42
var foobaz = "string"
val v = BClass("KKK", AClass(5, "25"))
""".trimIndent()
}
run {
code = "foo"
cursor = 3
expect {
addCompletion("foobar", "foobar", "Int", "property")
addCompletion("foobaz", "foobaz", "String", "property")
}
}
run {
code = "v.mema."
cursor = 7
expect {
completionsMode(ComparisonType.INCLUDES)
addCompletion("memx", "memx", "Int", "property")
addCompletion("memy", "memy", "String", "property")
}
}
run {
code = "listO"
cursor = 5
expect {
addCompletion("listOf(", "listOf(T)", "List<T>", "method")
addCompletion("listOf()", "listOf()", "List<T>", "method")
addCompletion("listOf(", "listOf(vararg T)", "List<T>", "method")
addCompletion("listOfNotNull(", "listOfNotNull(T?)", "List<T>", "method")
addCompletion("listOfNotNull(", "listOfNotNull(vararg T?)", "List<T>", "method")
}
}
}
@Test
fun testPackagesImport() = test {
run {
cursor = 17
code = "import java.lang."
expect {
completionsMode(ComparisonType.INCLUDES)
addCompletion("Process", "Process", " (java.lang)", "class")
}
}
}
@Test
fun testExtensionMethods() = test {
run {
doCompile
code = """
class AClass(val c_prop_x: Int) {
fun filter(xxx: (AClass).() -> Boolean): AClass {
return this
}
}
val AClass.c_prop_y: Int
get() = c_prop_x * c_prop_x
fun AClass.c_meth_z(v: Int) = v * c_prop_y
val df = AClass(10)
val c_zzz = "some string"
""".trimIndent()
}
run {
code = "df.filter{ c_ }"
cursor = 13
expect {
addCompletion("c_prop_x", "c_prop_x", "Int", "property")
addCompletion("c_zzz", "c_zzz", "String", "property")
addCompletion("c_prop_y", "c_prop_y", "Int", "property")
addCompletion("c_meth_z(", "c_meth_z(Int)", "Int", "method")
}
}
run {
code = "df.fil"
cursor = 6
expect {
addCompletion("filter { ", "filter(Line_1_simplescript.AClass.() -> ...", "Line_1_simplescript.AClass", "method")
}
}
}
@Test
fun testBacktickedFields() = test {
run {
doCompile
code = """
class AClass(val `c_prop x y z`: Int)
val df = AClass(33)
""".trimIndent()
}
run {
code = "df.c_"
cursor = 5
expect {
addCompletion("`c_prop x y z`", "`c_prop x y z`", "Int", "property")
}
}
}
@Test
fun testListErrors() = test {
run {
doCompile
code = """
data class AClass(val memx: Int, val memy: String)
data class BClass(val memz: String, val mema: AClass)
val foobar = 42
var foobaz = "string"
val v = BClass("KKK", AClass(5, "25"))
""".trimIndent()
expect {
errorsMode(ComparisonType.EQUALS)
}
}
run {
code = """
val a = AClass("42", 3.14)
val b: Int = "str"
val c = foob
""".trimIndent()
expect {
addError(1, 16, 1, 20, "Type mismatch: inferred type is String but Int was expected", "ERROR")
addError(1, 22, 1, 26, "The floating-point literal does not conform to the expected type String", "ERROR")
addError(2, 14, 2, 19, "Type mismatch: inferred type is String but Int was expected", "ERROR")
addError(3, 9, 3, 13, "Unresolved reference: foob", "ERROR")
}
}
}
@Test
fun testCompletionDuplication() = test {
for (i in 1..6) {
run {
if (i == 5) doCompile
if (i % 2 == 1) doErrorCheck
val value = "a".repeat(i)
code = "val ddddd = \"$value\""
cursor = 13 + i
}
}
run {
code = "dd"
cursor = 2
expect {
addCompletion("ddddd", "ddddd", "String", "property")
}
}
}
private class TestConf {
private val runs = mutableListOf<Run>()
fun run(setup: (Run).() -> Unit) {
val r = Run()
r.setup()
runs.add(r)
}
fun collect() = runs.map { it.collect() }
class Run {
private var _doCompile = false
val doCompile: Unit
get() {
_doCompile = true
}
private var _doComplete = false
val doComplete: Unit
get() {
_doComplete = true
}
private var _doErrorCheck = false
val doErrorCheck: Unit
get() {
_doErrorCheck = true
}
var cursor: Int? = null
var code: String = ""
private var _expected: Expected = Expected(this)
fun expect(setup: (Expected).() -> Unit) {
_expected = Expected(this)
_expected.setup()
}
fun collect(): Pair<RunRequest, ExpectedResult> {
return RunRequest(cursor, code, _doCompile, _doComplete, _doErrorCheck) to _expected.collect()
}
class Expected(private val run: Run) {
private val variants = mutableListOf<SourceCodeCompletionVariant>()
private var _completionsMode: ComparisonType = ComparisonType.DONT_CHECK
fun completionsMode(mode: ComparisonType) {
_completionsMode = mode
}
fun addCompletion(text: String, displayText: String, tail: String, icon: String) {
if (_completionsMode == ComparisonType.DONT_CHECK)
_completionsMode = ComparisonType.EQUALS
run.doComplete
variants.add(SourceCodeCompletionVariant(text, displayText, tail, icon))
}
private val errors = mutableListOf<ScriptDiagnostic>()
private var _errorsMode: ComparisonType = ComparisonType.DONT_CHECK
fun errorsMode(mode: ComparisonType) {
_errorsMode = mode
}
fun addError(startLine: Int, startCol: Int, endLine: Int, endCol: Int, message: String, severity: String) {
if (_errorsMode == ComparisonType.DONT_CHECK)
_errorsMode = ComparisonType.EQUALS
run.doErrorCheck
errors.add(
ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError,
message,
ScriptDiagnostic.Severity.valueOf(severity),
location = SourceCode.Location(
SourceCode.Position(startLine, startCol),
SourceCode.Position(endLine, endCol)
)
)
)
}
fun collect(): ExpectedResult {
return ExpectedResult(variants, _completionsMode, errors, _errorsMode)
}
}
}
}
private fun test(setup: (TestConf).() -> Unit) {
val test = TestConf()
test.setup()
runBlocking { checkEvaluateInRepl(simpleScriptCompilationConfiguration, test.collect()) }
}
enum class ComparisonType {
INCLUDES, EQUALS, DONT_CHECK
}
data class RunRequest(
val cursor: Int?,
val code: String,
val doCompile: Boolean,
val doComplete: Boolean,
val doErrorCheck: Boolean
)
data class ExpectedResult(
val completions: List<SourceCodeCompletionVariant>, val completionsCompType: ComparisonType,
val errors: List<ScriptDiagnostic>, val errorsCompType: ComparisonType
)
private val currentLineCounter = AtomicInteger()
private fun nextCodeLine(code: String): SourceCode =
SourceCodeTestImpl(
currentLineCounter.getAndIncrement(),
code
)
private suspend fun evaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration,
snippets: List<RunRequest>
): List<ResultWithDiagnostics<Pair<List<SourceCodeCompletionVariant>, List<ScriptDiagnostic>>>> {
val compiler = KJvmReplCompilerWithIdeServices()
return snippets.map { (cursor, snippetText, doComplile, doComplete, doErrorCheck) ->
val pos = SourceCode.Position(0, 0, cursor)
val codeLine = nextCodeLine(snippetText)
val completionRes = if (doComplete && cursor != null) {
val res = compiler.complete(codeLine, pos, compilationConfiguration)
res.toList().filter { it.tail != "keyword" }
} else {
emptyList()
}
val errorsRes = if (doErrorCheck) {
val codeLineForErrorCheck = nextCodeLine(snippetText)
compiler.analyze(codeLineForErrorCheck, SourceCode.Position(0, 0), compilationConfiguration).toList()
} else {
emptyList()
}
if (doComplile) {
val codeLineForCompilation = nextCodeLine(snippetText)
compiler.compile(codeLineForCompilation, compilationConfiguration)
}
Pair(completionRes, errorsRes).asSuccess()
}
}
private fun <T> checkLists(index: Int, checkName: String, expected: List<T>, actual: List<T>, compType: ComparisonType) {
when (compType) {
ComparisonType.EQUALS -> Assert.assertEquals(
"#$index ($checkName): Expected $expected, got $actual",
expected,
actual
)
ComparisonType.INCLUDES -> Assert.assertTrue(
"#$index ($checkName): Expected $actual to include $expected",
actual.containsAll(expected)
)
ComparisonType.DONT_CHECK -> {
}
}
}
private suspend fun checkEvaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration,
testData: List<Pair<RunRequest, ExpectedResult>>
) {
val (snippets, expected) = testData.unzip()
val expectedIter = expected.iterator()
evaluateInRepl(compilationConfiguration, snippets).forEachIndexed { index, res ->
when (res) {
is ResultWithDiagnostics.Failure -> Assert.fail("#$index: Expected result, got $res")
is ResultWithDiagnostics.Success -> {
val (expectedCompletions, completionsCompType, expectedErrors, errorsCompType) = expectedIter.next()
val (completionsRes, errorsRes) = res.value
checkLists(index, "completions", expectedCompletions, completionsRes, completionsCompType)
val expectedErrorsWithPath = expectedErrors.map {
it.copy(sourcePath = errorsRes.firstOrNull()?.sourcePath)
}
checkLists(index, "errors", expectedErrorsWithPath, errorsRes, errorsCompType)
}
}
}
}
}
@@ -0,0 +1,76 @@
/*
* 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 kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.jvm.updateClasspath
import kotlin.script.experimental.jvm.util.classpathFromClass
@KotlinScript(fileExtension = "simplescript.kts")
abstract class SimpleScript
val simpleScriptCompilationConfiguration =
createJvmCompilationConfigurationFromTemplate<SimpleScript> {
updateClasspath(classpathFromClass<SimpleScript>())
}
val simpleScriptEvaluationConfiguration = ScriptEvaluationConfiguration()
@KotlinScript(fileExtension = "withboth.kts", compilationConfiguration = ReceiverAndPropertiesConfiguration::class)
abstract class ScriptWithBoth
@KotlinScript(fileExtension = "withproperties.kts", compilationConfiguration = ProvidedPropertiesConfiguration::class)
abstract class ScriptWithProvidedProperties
@KotlinScript(fileExtension = "withreceiver.kts", compilationConfiguration = ImplicitReceiverConfiguration::class)
abstract class ScriptWithImplicitReceiver
object ReceiverAndPropertiesConfiguration : ScriptCompilationConfiguration(
{
updateClasspath(classpathFromClass<ScriptWithBoth>())
providedProperties("providedString" to String::class)
implicitReceivers(ImplicitReceiverClass::class)
}
)
object ProvidedPropertiesConfiguration : ScriptCompilationConfiguration(
{
updateClasspath(classpathFromClass<ScriptWithProvidedProperties>())
providedProperties("providedString" to String::class)
}
)
object ImplicitReceiverConfiguration : ScriptCompilationConfiguration(
{
updateClasspath(classpathFromClass<ScriptWithImplicitReceiver>())
implicitReceivers(ImplicitReceiverClass::class)
}
)
class ImplicitReceiverClass(
@Suppress("unused")
val receiverString: String
)
inline fun <reified T : Any> createJvmCompilationConfigurationFromTemplate(
hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration,
noinline body: ScriptCompilationConfiguration.Builder.() -> Unit = {}
): ScriptCompilationConfiguration =
createCompilationConfigurationFromTemplate(
KotlinType(T::class),
hostConfiguration,
ScriptCompilationConfiguration::class,
body
)
@@ -0,0 +1,60 @@
/*
* 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.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.jvm.BasicJvmReplEvaluator
import kotlin.script.experimental.util.LinkedSnippet
import kotlin.script.experimental.jvm.util.toSourceCodePosition
internal class JvmTestRepl : Closeable {
private val currentLineCounter = AtomicInteger(0)
private val compileConfiguration = simpleScriptCompilationConfiguration
private val evalConfiguration = simpleScriptEvaluationConfiguration
fun nextCodeLine(code: String): SourceCode =
SourceCodeTestImpl(
currentLineCounter.getAndIncrement(),
code
)
private val replCompiler: KJvmReplCompilerWithIdeServices by lazy {
KJvmReplCompilerWithIdeServices()
}
private val compiledEvaluator: BasicJvmReplEvaluator by lazy {
BasicJvmReplEvaluator()
}
fun compile(code: SourceCode) = runBlocking { replCompiler.compile(code, compileConfiguration) }
fun complete(code: SourceCode, cursor: Int) = runBlocking { replCompiler.complete(code, cursor.toSourceCodePosition(code), compileConfiguration) }
fun eval(snippet: LinkedSnippet<out CompiledSnippet>) = runBlocking { compiledEvaluator.eval(snippet, evalConfiguration) }
override fun close() {
}
}
internal class SourceCodeTestImpl(number: Int, override val text: String) : SourceCode {
override val name: String? = "Line_$number"
override val locationId: String? = "location_$number"
}
@JvmName("iterableToList")
fun <T> ResultWithDiagnostics<Iterable<T>>.toList() = this.valueOrNull()?.toList().orEmpty()
@JvmName("sequenceToList")
fun <T> ResultWithDiagnostics<Sequence<T>>.toList() = this.valueOrNull()?.toList().orEmpty()