diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt index c93f1048cd1..27c1abf6c79 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt @@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.codegen.context.CodegenContext import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.context.ScriptContext +import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.ClassDescriptor @@ -82,6 +83,8 @@ class ScriptCodegen private constructor( val jvmSignature = typeMapper.mapScriptSignature(scriptDescriptor) val asmMethod = jvmSignature.asmMethod + classBuilder.serializationBindings.put(METHOD_FOR_FUNCTION, scriptDescriptor.unsubstitutedPrimaryConstructor, asmMethod) + scriptContext.resultFieldInfo?.let { resultFieldInfo -> classBuilder.newField( NO_ORIGIN, diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt index bcde4252313..b85f5752dab 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt @@ -23,9 +23,10 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.toArrayOrPrimitiveArrayType -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.ir.util.indexOrMinusOne +import org.jetbrains.kotlin.ir.util.isCrossinline +import org.jetbrains.kotlin.ir.util.isNoinline +import org.jetbrains.kotlin.ir.util.varargElementType import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtScript @@ -155,6 +156,7 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG irScript.providedProperties.forEach { add(it.first) } } irConstructor.parent = irScript + irConstructor.metadata = DescriptorMetadataSource.Function(descriptor.unsubstitutedPrimaryConstructor) } for (d in ktScript.declarations) { diff --git a/libraries/scripting/jvm-host-test/build.gradle.kts b/libraries/scripting/jvm-host-test/build.gradle.kts index a7e6fc1bafb..69b1f8a039e 100644 --- a/libraries/scripting/jvm-host-test/build.gradle.kts +++ b/libraries/scripting/jvm-host-test/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { testImplementation(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) testRuntimeOnly(project(":kotlin-compiler")) - testRuntimeOnly(project(":kotlin-reflect")) + testImplementation(project(":kotlin-reflect")) testRuntimeOnly(commonDep("org.jetbrains.intellij.deps", "trove4j")) embeddableTestRuntime(project(":kotlin-scripting-jvm-host")) diff --git a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ReplTest.kt b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ReplTest.kt index c8ec05400d5..4676390e485 100644 --- a/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ReplTest.kt +++ b/libraries/scripting/jvm-host-test/test/kotlin/script/experimental/jvmhost/test/ReplTest.kt @@ -11,16 +11,47 @@ import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmReplCompilerBase import org.jetbrains.kotlin.scripting.compiler.plugin.repl.ReplCodeAnalyzerBase import org.junit.Assert import org.junit.Test +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.declaredMemberProperties import kotlin.script.experimental.api.* import kotlin.script.experimental.host.toScriptSource import kotlin.script.experimental.jvm.BasicJvmReplEvaluator +import kotlin.script.experimental.jvm.impl.KJvmCompiledModuleInMemory +import kotlin.script.experimental.jvm.impl.KJvmCompiledScript import kotlin.script.experimental.jvm.updateClasspath import kotlin.script.experimental.jvm.util.classpathFromClass import kotlin.script.experimental.jvmhost.createJvmScriptDefinitionFromTemplate +import kotlin.script.experimental.util.LinkedSnippet import kotlin.script.templates.standard.ScriptTemplateWithArgs class ReplTest : TestCase() { + @Test + fun testDecompiledReflection() { + checkEvaluateInRepl( + sequenceOf( + "val x = 1", + ), + sequenceOf(null), + compiledSnippetChecker = { linkedSnippet -> + val snippet = linkedSnippet.get() + val module = snippet.getCompiledModule() as KJvmCompiledModuleInMemory + + val classLoader = module.createClassLoader(ClassLoader.getSystemClassLoader()) + val kClass = classLoader.loadClass(snippet.scriptClassFQName).kotlin + val constructor = kClass.constructors.single() + val scriptInstance = constructor.call(emptyArray()) + + @Suppress("UNCHECKED_CAST") + val xProp = kClass.declaredMemberProperties.first { it.name == "x" } as KProperty1 + val xValue = xProp.get(scriptInstance) + + assertEquals(1, xValue) + + } + ) + } + @Test fun testCompileAndEval() { val out = captureOut { @@ -282,7 +313,8 @@ class ReplTest : TestCase() { snippets: Sequence, compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration, evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration, - limit: Int = 0 + limit: Int = 0, + compiledSnippetChecker: CompiledSnippetChecker = {}, ): Sequence> { val replCompiler = KJvmReplCompilerBase() val replEvaluator = BasicJvmReplEvaluator() @@ -293,6 +325,7 @@ class ReplTest : TestCase() { snippetText.toScriptSource("Line_$snippetNo.${compilationConfiguration[ScriptCompilationConfiguration.fileExtension]}") runBlocking { replCompiler.compile(snippetSource, compilationConfiguration) } .onSuccess { + compiledSnippetChecker(it) runBlocking { replEvaluator.eval(it, currentEvalConfig) } } .onSuccess { @@ -307,10 +340,17 @@ class ReplTest : TestCase() { compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration, evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration, limit: Int = 0, - ignoreDiagnostics: Boolean = false + ignoreDiagnostics: Boolean = false, + compiledSnippetChecker: CompiledSnippetChecker = {} ) { val expectedIter = (if (limit == 0) expected else expected.take(limit)).iterator() - evaluateInRepl(snippets, compilationConfiguration, evaluationConfiguration, limit).forEachIndexed { index, res -> + evaluateInRepl( + snippets, + compilationConfiguration, + evaluationConfiguration, + limit, + compiledSnippetChecker + ).forEachIndexed { index, res -> val expectedRes = expectedIter.next() when { res is ResultWithDiagnostics.Failure && expectedRes is ResultWithDiagnostics.Failure -> { @@ -375,9 +415,16 @@ class ReplTest : TestCase() { expected: Sequence, compilationConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration, evaluationConfiguration: ScriptEvaluationConfiguration? = simpleScriptEvaluationConfiguration, - limit: Int = 0 + limit: Int = 0, + compiledSnippetChecker: CompiledSnippetChecker = {} ) = checkEvaluateInReplDiags( - snippets, expected.map { ResultWithDiagnostics.Success(it) }, compilationConfiguration, evaluationConfiguration, limit, true + snippets, + expected.map { ResultWithDiagnostics.Success(it) }, + compilationConfiguration, + evaluationConfiguration, + limit, + true, + compiledSnippetChecker ) class TestReceiver( @@ -389,3 +436,5 @@ class ReplTest : TestCase() { @Target(AnnotationTarget.FILE) annotation class NewAnn + +typealias CompiledSnippetChecker = (LinkedSnippet) -> Unit