diff --git a/compiler/testData/codegen/customScript/simpleEnvVars.kts b/compiler/testData/codegen/customScript/simpleEnvVars.kts index c1508062fc1..3bfd067c5b1 100644 --- a/compiler/testData/codegen/customScript/simpleEnvVars.kts +++ b/compiler/testData/codegen/customScript/simpleEnvVars.kts @@ -1,8 +1,8 @@ // KOTLIN_SCRIPT_DEFINITION: org.jetbrains.kotlin.codegen.TestScriptWithSimpleEnvVars -// envVar: stringVar1 = abracadabra +// envVar: stringVar1=abracadabra val res = stringVar1.drop(4) -// expected: res = cadabra +// expected: res=cadabra diff --git a/compiler/testData/codegen/customScript/stringReceiver.kts b/compiler/testData/codegen/customScript/stringReceiver.kts index bda94458d8b..bc807341f9b 100644 --- a/compiler/testData/codegen/customScript/stringReceiver.kts +++ b/compiler/testData/codegen/customScript/stringReceiver.kts @@ -5,4 +5,4 @@ val res = drop(4) -// expected: res = cadabra +// expected: res=cadabra diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCustomScriptCodegenTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCustomScriptCodegenTest.kt index 554255ecb13..921726558f5 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCustomScriptCodegenTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCustomScriptCodegenTest.kt @@ -6,16 +6,12 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace -import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.AnalyzingUtils +import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots +import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.script.util.scriptCompilationClasspathFromContextOrStlib import org.jetbrains.kotlin.scripting.compiler.plugin.configureScriptDefinitions import org.jetbrains.kotlin.test.ConfigurationKind -import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.TestJdkKind import org.junit.Assert import java.io.File @@ -26,94 +22,62 @@ import kotlin.script.experimental.api.ScriptCompileConfigurationProperties import kotlin.script.experimental.misc.invoke import kotlin.script.experimental.util.TypedKey -/* - Note by Ilya Chernikov: I gave up attempts to reuse the main compilation and testing logic from CodegenTestCase: it is too rigid - and ad-hoc. I ended up reimplementing (with some copy/paste) a subset relevant for scripting, but in (I assume) much more composable - and generic way. - I suggest that we will start building a parallel implementation of the CodegenTestCase taking composability much more seriously, - avoiding shared state and other rotten OOP misfeatures as much as possible. This implementation could be a starting point, unless - we'll find something better, of course. - */ - open class AbstractCustomScriptCodegenTest : CodegenTestCase() { + private lateinit var scriptDefinitions: List - // TODO: add types to receivers, envVars and params - class ScriptTestFile(val file: File) { + override fun setUp() { + super.setUp() - val content by lazy { file.readText() } - - val definitions: List by lazy { extractAllSimpleValues("KOTLIN_SCRIPT_DEFINITION") } - - val receivers: List by lazy { extractAllSimpleValues("receiver") } - - val environmentVars: Map by lazy { extractAllKeyValPairs("envVar") } - - val expected: Map by lazy { extractAllKeyValPairs("expected") } - - val scriptParams: List by lazy { extractAllSimpleValues("param") } - - private fun extractAllSimpleValues(directive: String) = - Regex("//\\s*$directive:\\s*([^\\s]+)").findAll(content).map { it.groupValues[1] }.toList() - - private fun extractAllKeyValPairs(directive: String) = - Regex("//\\s*$directive:\\s*([^\\s]+)\\s*=\\s*([^\\s]+)").findAll(content).map { it.groupValues[1] to it.groupValues[2] }.toMap() + configurationKind = ConfigurationKind.ALL } - fun createEnvironment( - kind: ConfigurationKind, - jdkKind: TestJdkKind, - scriptDefinitions: List - ): KotlinCoreEnvironment { - - val classpath = - if (scriptDefinitions.isNotEmpty()) { - scriptCompilationClasspathFromContextOrStlib("tests-common", "kotlin-stdlib").also { - additionalDependencies = it - } - } else { - emptyList() - } - - val configuration = KotlinTestUtils.newConfiguration(kind, jdkKind, classpath, emptyList()) - + override fun updateConfiguration(configuration: CompilerConfiguration) { if (scriptDefinitions.isNotEmpty()) { configureScriptDefinitions(scriptDefinitions, configuration, MessageCollector.NONE, emptyMap()) } - return KotlinCoreEnvironment.createForTests( - testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES - ).also { - myEnvironment = it - } + configuration.addJvmClasspathRoots(additionalDependencies.orEmpty()) } - override fun doTest(filename: String) { - val testFile = AbstractCustomScriptCodegenTest.ScriptTestFile(File(filename)) - val environment = createEnvironment(ConfigurationKind.ALL, TestJdkKind.FULL_JDK, testFile.definitions) - - val psiFile = KotlinTestUtils.createFile(testFile.file.name, testFile.content, environment.project).also { - val ranges = AnalyzingUtils.getSyntaxErrorRanges(it) - assert(ranges.isEmpty()) { "Syntax errors found in $filename: $ranges" } + override fun doMultiFileTest(wholeFile: File, files: MutableList, javaFilesDir: File?) { + if (files.size > 1) { + throw UnsupportedOperationException("Multiple files are not yet supported in this test") } - val classesFactory = generateClasses(listOf(psiFile)) - if (classesFactory == null) { - fail("No class file was generated for: $name") + val file = files.single() + val content = file.content + + scriptDefinitions = InTextDirectivesUtils.findListWithPrefixes(content, "KOTLIN_SCRIPT_DEFINITION:") + if (scriptDefinitions.isNotEmpty()) { + additionalDependencies = scriptCompilationClasspathFromContextOrStlib("tests-common", "kotlin-stdlib") } + createEnvironmentWithMockJdkAndIdeaAnnotations(configurationKind, files, TestJdkKind.FULL_JDK) + + myFiles = CodegenTestFiles.create(file.name, content, myEnvironment.project) + try { - val scriptClass = classesFactory!!.loadClass(psiFile.script!!.fqName.asString()) + val scriptClass = generateClass(myFiles.psiFile.script!!.fqName.asString()) - val scriptInstance = runScript(scriptClass, testFile.receivers, testFile.environmentVars, testFile.scriptParams) - - checkExpectedFields(testFile.expected, scriptClass, scriptInstance) + // TODO: add types to receivers, envVars and params + val receivers = InTextDirectivesUtils.findListWithPrefixes(content, "receiver:") + val environmentVars = extractAllKeyValPairs(content, "envVar:") + val scriptParams = InTextDirectivesUtils.findListWithPrefixes(content, "param:") + val scriptInstance = runScript(scriptClass, receivers, environmentVars, scriptParams) + val expectedFields = extractAllKeyValPairs(content, "expected:") + checkExpectedFields(expectedFields, scriptClass, scriptInstance) } catch (e: Throwable) { - println(classesFactory!!.createText()) + println(generateToText()) throw e } } + private fun extractAllKeyValPairs(content: String, directive: String): Map = + InTextDirectivesUtils.findListWithPrefixes(content, directive).associate { line -> + line.substringBefore('=') to line.substringAfter('=') + } + private fun runScript(scriptClass: Class<*>, receivers: List, environmentVars: Map, scriptParams: List): Any? { val ctorParams = arrayListOf() @@ -129,7 +93,7 @@ open class AbstractCustomScriptCodegenTest : CodegenTestCase() { return constructor.newInstance(*ctorParams.toTypedArray()) } - protected fun checkExpectedFields(expectedFields: Map, scriptClass: Class<*>, scriptInstance: Any?) { + private fun checkExpectedFields(expectedFields: Map, scriptClass: Class<*>, scriptInstance: Any?) { Assert.assertFalse("expecting at least one expectation", expectedFields.isEmpty()) for ((fieldName, expectedValue) in expectedFields) { @@ -149,56 +113,6 @@ open class AbstractCustomScriptCodegenTest : CodegenTestCase() { Assert.assertEquals("comparing field $fieldName", expectedValue, resultString) } } - - protected fun ClassFileFactory.makeClassloader(): ClassLoader = - GeneratedClassLoader( - this, - when { - configurationKind.withReflection -> ForTestCompileRuntime.runtimeAndReflectJarClassLoader() - configurationKind.withCoroutines -> ForTestCompileRuntime.runtimeAndCoroutinesJarClassLoader() - else -> ForTestCompileRuntime.runtimeJarClassLoader() - }, - *classPathURLs - ) - - protected fun ClassFileFactory.loadClass(name: String): Class<*> = - makeClassloader().also { - if (!verifyAllFilesWithAsm(this, it)) { - Assert.fail("Verification failed: see exceptions above") - } - }.loadClass(name) - - protected fun generateClasses(psiFiles: List): ClassFileFactory? { - try { - val generationState = GenerationUtils.compileFiles( - psiFiles, myEnvironment, classBuilderFactory, - NoScopeRecordCliBindingTrace() - ) - val classFileFactory = generationState.factory - - if (verifyWithDex() && DxChecker.RUN_DX_CHECKER) { - DxChecker.check(classFileFactory) - } - return classFileFactory - } catch (e: Throwable) { - e.printStackTrace() - System.err.println("Generating instructions as text...") - try { - if (classFileFactory == null) { - System.err.println("Cannot generate text: exception was thrown during generation") - } else { - System.err.println(classFileFactory.createText()) - } - } catch (e1: Throwable) { - System.err.println("Exception thrown while trying to generate text, the actual exception follows:") - e1.printStackTrace() - System.err.println("-----------------------------------------------------------------------------") - } - - Assert.fail("See exceptions above") - return null - } - } } object TestScriptWithReceiversConfiguration : ArrayList, Any?>>(