K2 Scripting: implement basic metadata serialization support

#KT-62305 fixed
NB: kotlin reflection do not see script class constructor after
this change, and it's ok, since the fact that the script is compiled
into a class is an implementation detail.
If needed, java reflection could be used to access the constructor.
This commit is contained in:
Ilya Chernikov
2023-10-04 13:50:38 +02:00
committed by Space Team
parent 122f16fc18
commit 31f9e9e7a8
17 changed files with 251 additions and 24 deletions
@@ -103,8 +103,7 @@ class ScriptingCompilerPluginTest : TestCase() {
)
}
// muted, see KT-61490
fun testLazyScriptDefinitionDiscovery() = expectTestToFailOnK2 {
fun testLazyScriptDefinitionDiscovery() {
withTempDir { tmpdir ->
withDisposable { disposable ->
@@ -10,10 +10,8 @@ import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.scripting.compiler.plugin.expectTestToFailOnK2
import org.jetbrains.kotlin.scripting.compiler.plugin.getBaseCompilerArgumentsFromProperty
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptJvmCompilerIsolated
import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.declaredMembers
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.toScriptSource
@@ -43,8 +41,8 @@ class ScriptCompilerTest : TestCase() {
""".trimIndent().toScriptSource()
)
val kclass = res.valueOrThrow()
val scriptInstance = kclass.createInstance()
val kclass = res.valueOrThrow().java
val scriptInstance = kclass.constructors.first().newInstance()
assertNotNull(scriptInstance)
}
@@ -64,8 +62,8 @@ class ScriptCompilerTest : TestCase() {
""".trimIndent().toScriptSource()
)
val kclass = res.valueOrThrow()
val scriptInstance = kclass.createInstance()
val kclass = res.valueOrThrow().java
val scriptInstance = kclass.constructors.first().newInstance()
assertNotNull(scriptInstance)
}
@@ -97,8 +95,8 @@ class ScriptCompilerTest : TestCase() {
)
val kClass = res.valueOrThrow()
val scriptInstance = kClass.createInstance()
val members = kClass.declaredMembers
val scriptInstance = kClass.java.constructors.first().newInstance()
val namesToMembers = members.associateBy { it.name }
fun prop(name: String) = namesToMembers[name]!! as KProperty<*>
@@ -112,6 +110,24 @@ class ScriptCompilerTest : TestCase() {
assertNull(namesToMembers["_"])
}
fun testSimpleReflection() {
val res = compileToClass(
"""
val c = 3
class A {
class B
}
val a = A()
""".trimIndent().toScriptSource()
)
val kClass = res.valueOrThrow()
val members = kClass.declaredMembers
assertEquals(2, members.size)
val nestedClasses = kClass.nestedClasses
assertEquals(1, nestedClasses.size)
}
fun compile(
script: SourceCode,
cfgBody: ScriptCompilationConfiguration.Builder.() -> Unit
@@ -13,12 +13,11 @@ import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.compiler.plugin.TestMessageCollector
import org.jetbrains.kotlin.scripting.compiler.plugin.assertHasMessage
import org.jetbrains.kotlin.scripting.compiler.plugin.expectTestToFailOnK2
import org.jetbrains.kotlin.scripting.compiler.plugin.updateWithBaseCompilerArguments
import org.jetbrains.kotlin.scripting.compiler.plugin.*
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.SCRIPT_BASE_COMPILER_ARGUMENTS_PROPERTY
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
@@ -120,8 +119,8 @@ class ScriptTemplateTest : TestCase() {
assertEqualsTrimmed(NUM_4_LINE + FIB_SCRIPT_OUTPUT_TAIL, out)
}
// Should fail on K2, see KT-60452
fun testScriptWithoutParams() {
// Fails on K2, see KT-60452
fun testScriptWithoutParams() = expectTestToFailOnK2 {
val messageCollector = TestMessageCollector()
val aClass = compileScript("without_params.kts", ScriptWithoutParams::class, null, messageCollector = messageCollector)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
@@ -235,7 +234,8 @@ class ScriptTemplateTest : TestCase() {
}
}
fun testScriptWithParamConversion() {
// Fails on K2, see KT-62413
fun testScriptWithParamConversion() = expectTestToFailOnK2 {
val messageCollector = TestMessageCollector()
val aClass = compileScript("fib.kts", ScriptWithIntParam::class, messageCollector = messageCollector)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
@@ -383,6 +383,13 @@ class ScriptTemplateTest : TestCase() {
configuration.put(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION, true)
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
val isK2 = System.getProperty(SCRIPT_BASE_COMPILER_ARGUMENTS_PROPERTY)?.contains("-language-version 1.9") != true &&
System.getProperty(SCRIPT_TEST_BASE_COMPILER_ARGUMENTS_PROPERTY)?.contains("-language-version 1.9") != true
if (isK2) {
configuration.put(CommonConfigurationKeys.USE_FIR, true)
}
loadScriptingPlugin(configuration)
val environment = KotlinCoreEnvironment.createForTests(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)