diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt index bb83f117748..b95514a5ff2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt @@ -48,13 +48,19 @@ class ScriptContext( val resultFieldInfo: FieldInfo get() { - assert(state.replSpecific.shouldGenerateScriptResultValue) { "Should not be called unless 'scriptResultFieldName' is set" } + assert(state.replSpecific.shouldGenerateScriptResultValue) { "Should not be called unless 'resultFieldName' is set" } val scriptResultFieldName = state.replSpecific.scriptResultFieldName!! - return FieldInfo.createForHiddenField(state.typeMapper.mapClass(scriptDescriptor), AsmTypes.OBJECT_TYPE, scriptResultFieldName) + val fieldType = state.replSpecific.resultType?.let { state.typeMapper.mapType(it) } ?: AsmTypes.OBJECT_TYPE + return FieldInfo.createForHiddenField( + state.typeMapper.mapClass(scriptDescriptor), + fieldType, + state.replSpecific.resultType, + scriptResultFieldName + ) } val script = DescriptorToSourceUtils.getSourceFromDescriptor(scriptDescriptor) as KtScript? - ?: error("Declaration should be present for script: $scriptDescriptor") + ?: error("Declaration should be present for script: $scriptDescriptor") init { val lastDeclaration = script.declarations.lastOrNull() diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 1e72f8eb1e5..f696c6e22f1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.* import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.types.KotlinType import java.io.File class GenerationState private constructor( @@ -214,6 +215,7 @@ class GenerationState private constructor( var earlierScriptsForReplInterpreter: List? = null var scriptResultFieldName: String? = null val shouldGenerateScriptResultValue: Boolean get() = scriptResultFieldName != null + var resultType: KotlinType? = null var hasResult: Boolean = false } diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplEvaluator.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplEvaluator.kt index 529c2b6370f..162799db033 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplEvaluator.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplEvaluator.kt @@ -108,18 +108,14 @@ open class GenericReplEvaluator( historyActor.addFinal(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper)) - val resultField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true } + val resultFieldName = scriptResultFieldName(compileResult.lineId.no) + val resultField = scriptClass.getDeclaredField(resultFieldName).apply { isAccessible = true } val resultValue: Any? = resultField.get(scriptInstance) - return if (compileResult.hasResult) ReplEvalResult.ValueResult(resultValue, compileResult.type) + return if (compileResult.hasResult) ReplEvalResult.ValueResult(resultFieldName, resultValue, compileResult.type) else ReplEvalResult.UnitResult() } } - - companion object { - private val SCRIPT_RESULT_FIELD_NAME = "\$\$result" - } - } private open class HistoryActionsForNoRepeat(val state: GenericReplEvaluatorState) { diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt index 1cdcf444495..569035fda60 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplApi.kt @@ -121,8 +121,11 @@ interface ReplEvalAction { } sealed class ReplEvalResult : Serializable { - class ValueResult(val value: Any?, val type: String?) : ReplEvalResult() { - override fun toString(): String = "$value : $type" + class ValueResult(val name: String, val value: Any?, val type: String?) : ReplEvalResult() { + override fun toString(): String { + return "$name: $type = $value" + } + companion object { private val serialVersionUID: Long = 1L } } diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt index 4212a153bea..4f29edc5c2d 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt @@ -50,6 +50,8 @@ fun String.replNormalizeLineBreaks() = replace(END_LINE, "\n") fun makeScriptBaseName(codeLine: ReplCodeLine) = "Line_${codeLine.no}${if (codeLine.generation > REPL_CODE_LINE_FIRST_GEN) "_gen_${codeLine.generation}" else ""}" +fun scriptResultFieldName(lineNo: Int) = "res$lineNo" + fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String { val newTrace = arrayListOf() var skip = true diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt index 49835f6c58d..ae98c44a53a 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt @@ -27,12 +27,8 @@ import org.jetbrains.kotlin.codegen.CompilationErrorHandler import org.jetbrains.kotlin.codegen.KotlinCodegenFacade import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtScript -import org.jetbrains.kotlin.psi.KtScriptInitializer -import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.ScriptDependenciesProvider import java.io.File @@ -92,6 +88,8 @@ open class GenericReplCompiler( else -> error("Unexpected result ${analysisResult::class.java}") } + val type = (scriptDescriptor as LazyScriptDescriptor).resultValue?.returnType + val generationState = GenerationState.Builder( psiFile.project, ClassBuilderFactories.BINARIES, @@ -100,7 +98,9 @@ open class GenericReplCompiler( listOf(psiFile), compilerConfiguration ).build() - generationState.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME + + generationState.replSpecific.resultType = type + generationState.replSpecific.scriptResultFieldName = scriptResultFieldName(codeLine.no) generationState.replSpecific.earlierScriptsForReplInterpreter = compilerState.history.map { it.item } generationState.beforeCompile() KotlinCodegenFacade.generatePackage( @@ -113,23 +113,18 @@ open class GenericReplCompiler( val generatedClassname = makeScriptBaseName(codeLine) compilerState.history.push(LineId(codeLine), scriptDescriptor) - val expression = psiFile.getChildOfType()?.getChildOfType()?.getChildOfType() - ?.getChildOfType() - - val type = expression?.let { - compilerState.analyzerEngine.trace.bindingContext.getType(it) - }?.let { - DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it) - } + val classes = generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) } return ReplCompileResult.CompiledClasses( LineId(codeLine), compilerState.history.map { it.id }, generatedClassname, - generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }, + classes, generationState.replSpecific.hasResult, classpathAddendum ?: emptyList(), - type + generationState.replSpecific.resultType?.let { + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it) + } ) } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplFromTerminal.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplFromTerminal.kt index da64bc42e3d..46cd6a2340d 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplFromTerminal.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplFromTerminal.kt @@ -117,7 +117,7 @@ class ReplFromTerminal( is ReplEvalResult.ValueResult, is ReplEvalResult.UnitResult -> { writer.notifyCommandSuccess() if (evalResult is ReplEvalResult.ValueResult) { - writer.outputCommandResult(evalResult.value.toString()) + writer.outputCommandResult(evalResult.toString()) } } is ReplEvalResult.Error.Runtime -> writer.outputRuntimeError(evalResult.message) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptClassMemberScope.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptClassMemberScope.kt index 34fc5687e6f..56fa12baed2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptClassMemberScope.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptClassMemberScope.kt @@ -53,9 +53,8 @@ class LazyScriptClassMemberScope( ) var paramsIndexBase = baseConstructorDescriptor.valueParameters.lastIndex + 1 val syntheticParameters = - (implicitReceiversParamTypes + environmentVarsParamTypes).mapNotNull { param: Pair -> - if (param == null) null - else ValueParameterDescriptorImpl( + (implicitReceiversParamTypes + environmentVarsParamTypes).map { param: Pair -> + ValueParameterDescriptorImpl( constructorDescriptor, null, paramsIndexBase++, @@ -90,6 +89,15 @@ class LazyScriptClassMemberScope( return constructor } + override fun getNonDeclaredProperties(name: Name, result: MutableSet) { + super.getNonDeclaredProperties(name, result) + if (scriptDescriptor.resultFieldName() == name.asString()) { + scriptDescriptor.resultValue?.let { + result.add(it) + } + } + } + override fun createPropertiesFromPrimaryConstructorParameters(name: Name, result: MutableSet) { } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptDescriptor.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptDescriptor.kt index f18ccf054bf..4ee91dc1666 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptDescriptor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptDescriptor.kt @@ -24,6 +24,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1 import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtScriptInitializer +import org.jetbrains.kotlin.psi.psiUtil.getChildOfType +import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.module @@ -31,6 +36,7 @@ import org.jetbrains.kotlin.resolve.lazy.LazyClassContext import org.jetbrains.kotlin.resolve.lazy.ResolveSession import org.jetbrains.kotlin.resolve.lazy.data.KtScriptInfo import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider +import org.jetbrains.kotlin.resolve.lazy.descriptors.script.ReplResultPropertyDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.script.ScriptEnvironmentDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.script.classId import org.jetbrains.kotlin.resolve.scopes.LexicalScope @@ -40,6 +46,8 @@ import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.ScriptPriorities import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.typeUtil.isNothing +import org.jetbrains.kotlin.types.typeUtil.isUnit import kotlin.reflect.KClass import kotlin.reflect.KType @@ -59,6 +67,42 @@ class LazyScriptDescriptor( resolveSession.trace.record(BindingContext.SCRIPT, scriptInfo.script, this) } + val resultValue: ReplResultPropertyDescriptor? by lazy { provideResultValue() } + + private fun provideResultValue(): ReplResultPropertyDescriptor? { + val expression = scriptInfo.script + .getChildOfType() + ?.getChildrenOfType()?.lastOrNull() + ?.getChildOfType() + + val type = expression?.let { + resolveSession.trace.bindingContext.getType(it) + } + + return if (type != null && !type.isUnit() && !type.isNothing()) { + resultFieldName()?.let { + ReplResultPropertyDescriptor( + Name.identifier(it), + type, + this.thisAsReceiverParameter, + this, + expression.toSourceElement() + ) + } + } else null + } + + fun resultFieldName(): String? { + val scriptPriority = scriptInfo.script.getUserData(ScriptPriorities.PRIORITY_KEY) + if (scriptPriority != null) { + return "res$scriptPriority" + } + val scriptName = name.asString() + return if (scriptName.startsWith("Line_")) { + "res${scriptName.split("_")[1]}" + } else "\$\$result" + } + private val sourceElement = scriptInfo.script.toSourceElement() override fun getSource() = sourceElement diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ReplResultPropertyDescriptor.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ReplResultPropertyDescriptor.kt new file mode 100644 index 00000000000..b258629d887 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ReplResultPropertyDescriptor.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.resolve.lazy.descriptors.script + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor +import org.jetbrains.kotlin.types.KotlinType + +class ReplResultPropertyDescriptor( + name: Name, + kotlinType: KotlinType, + receiver: ReceiverParameterDescriptor?, + script: LazyScriptDescriptor, + source: SourceElement +) : PropertyDescriptorImpl( + script, + null, + Annotations.EMPTY, + Modality.FINAL, + Visibilities.PUBLIC, + false, + name, + CallableMemberDescriptor.Kind.SYNTHESIZED, + source, + /* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false, + /* isDelegated = */ false +) { + init { + setType(kotlinType, emptyList(), receiver, null) + initialize( + null, null + ) + } +} \ No newline at end of file