Implement support for "resX" result fields in REPL

This commit is contained in:
vitaly.khudobakhshov
2018-12-03 13:09:43 +01:00
committed by Ilya Chernikov
parent 79e577ae27
commit c901d6cebc
10 changed files with 128 additions and 32 deletions
@@ -48,13 +48,19 @@ class ScriptContext(
val resultFieldInfo: FieldInfo val resultFieldInfo: FieldInfo
get() { 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!! 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? val script = DescriptorToSourceUtils.getSourceFromDescriptor(scriptDescriptor) as KtScript?
?: error("Declaration should be present for script: $scriptDescriptor") ?: error("Declaration should be present for script: $scriptDescriptor")
init { init {
val lastDeclaration = script.declarations.lastOrNull() val lastDeclaration = script.declarations.lastOrNull()
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.* import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.*
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.KotlinType
import java.io.File import java.io.File
class GenerationState private constructor( class GenerationState private constructor(
@@ -214,6 +215,7 @@ class GenerationState private constructor(
var earlierScriptsForReplInterpreter: List<ScriptDescriptor>? = null var earlierScriptsForReplInterpreter: List<ScriptDescriptor>? = null
var scriptResultFieldName: String? = null var scriptResultFieldName: String? = null
val shouldGenerateScriptResultValue: Boolean get() = scriptResultFieldName != null val shouldGenerateScriptResultValue: Boolean get() = scriptResultFieldName != null
var resultType: KotlinType? = null
var hasResult: Boolean = false var hasResult: Boolean = false
} }
@@ -108,18 +108,14 @@ open class GenericReplEvaluator(
historyActor.addFinal(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper)) 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) 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() else ReplEvalResult.UnitResult()
} }
} }
companion object {
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
}
} }
private open class HistoryActionsForNoRepeat(val state: GenericReplEvaluatorState) { private open class HistoryActionsForNoRepeat(val state: GenericReplEvaluatorState) {
@@ -121,8 +121,11 @@ interface ReplEvalAction {
} }
sealed class ReplEvalResult : Serializable { sealed class ReplEvalResult : Serializable {
class ValueResult(val value: Any?, val type: String?) : ReplEvalResult() { class ValueResult(val name: String, val value: Any?, val type: String?) : ReplEvalResult() {
override fun toString(): String = "$value : $type" override fun toString(): String {
return "$name: $type = $value"
}
companion object { private val serialVersionUID: Long = 1L } companion object { private val serialVersionUID: Long = 1L }
} }
@@ -50,6 +50,8 @@ fun String.replNormalizeLineBreaks() = replace(END_LINE, "\n")
fun makeScriptBaseName(codeLine: ReplCodeLine) = fun makeScriptBaseName(codeLine: ReplCodeLine) =
"Line_${codeLine.no}${if (codeLine.generation > REPL_CODE_LINE_FIRST_GEN) "_gen_${codeLine.generation}" else ""}" "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 { fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String {
val newTrace = arrayListOf<StackTraceElement>() val newTrace = arrayListOf<StackTraceElement>()
var skip = true var skip = true
@@ -27,12 +27,8 @@ import org.jetbrains.kotlin.codegen.CompilationErrorHandler
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration 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.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor
import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDependenciesProvider import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import java.io.File import java.io.File
@@ -92,6 +88,8 @@ open class GenericReplCompiler(
else -> error("Unexpected result ${analysisResult::class.java}") else -> error("Unexpected result ${analysisResult::class.java}")
} }
val type = (scriptDescriptor as LazyScriptDescriptor).resultValue?.returnType
val generationState = GenerationState.Builder( val generationState = GenerationState.Builder(
psiFile.project, psiFile.project,
ClassBuilderFactories.BINARIES, ClassBuilderFactories.BINARIES,
@@ -100,7 +98,9 @@ open class GenericReplCompiler(
listOf(psiFile), listOf(psiFile),
compilerConfiguration compilerConfiguration
).build() ).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.replSpecific.earlierScriptsForReplInterpreter = compilerState.history.map { it.item }
generationState.beforeCompile() generationState.beforeCompile()
KotlinCodegenFacade.generatePackage( KotlinCodegenFacade.generatePackage(
@@ -113,23 +113,18 @@ open class GenericReplCompiler(
val generatedClassname = makeScriptBaseName(codeLine) val generatedClassname = makeScriptBaseName(codeLine)
compilerState.history.push(LineId(codeLine), scriptDescriptor) compilerState.history.push(LineId(codeLine), scriptDescriptor)
val expression = psiFile.getChildOfType<KtScript>()?.getChildOfType<KtBlockExpression>()?.getChildOfType<KtScriptInitializer>() val classes = generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }
?.getChildOfType<KtExpression>()
val type = expression?.let {
compilerState.analyzerEngine.trace.bindingContext.getType(it)
}?.let {
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it)
}
return ReplCompileResult.CompiledClasses( return ReplCompileResult.CompiledClasses(
LineId(codeLine), LineId(codeLine),
compilerState.history.map { it.id }, compilerState.history.map { it.id },
generatedClassname, generatedClassname,
generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }, classes,
generationState.replSpecific.hasResult, generationState.replSpecific.hasResult,
classpathAddendum ?: emptyList(), classpathAddendum ?: emptyList(),
type generationState.replSpecific.resultType?.let {
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it)
}
) )
} }
} }
@@ -117,7 +117,7 @@ class ReplFromTerminal(
is ReplEvalResult.ValueResult, is ReplEvalResult.UnitResult -> { is ReplEvalResult.ValueResult, is ReplEvalResult.UnitResult -> {
writer.notifyCommandSuccess() writer.notifyCommandSuccess()
if (evalResult is ReplEvalResult.ValueResult) { if (evalResult is ReplEvalResult.ValueResult) {
writer.outputCommandResult(evalResult.value.toString()) writer.outputCommandResult(evalResult.toString())
} }
} }
is ReplEvalResult.Error.Runtime -> writer.outputRuntimeError(evalResult.message) is ReplEvalResult.Error.Runtime -> writer.outputRuntimeError(evalResult.message)
@@ -53,9 +53,8 @@ class LazyScriptClassMemberScope(
) )
var paramsIndexBase = baseConstructorDescriptor.valueParameters.lastIndex + 1 var paramsIndexBase = baseConstructorDescriptor.valueParameters.lastIndex + 1
val syntheticParameters = val syntheticParameters =
(implicitReceiversParamTypes + environmentVarsParamTypes).mapNotNull { param: Pair<String, KotlinType> -> (implicitReceiversParamTypes + environmentVarsParamTypes).map { param: Pair<String, KotlinType> ->
if (param == null) null ValueParameterDescriptorImpl(
else ValueParameterDescriptorImpl(
constructorDescriptor, constructorDescriptor,
null, null,
paramsIndexBase++, paramsIndexBase++,
@@ -90,6 +89,15 @@ class LazyScriptClassMemberScope(
return constructor return constructor
} }
override fun getNonDeclaredProperties(name: Name, result: MutableSet<PropertyDescriptor>) {
super.getNonDeclaredProperties(name, result)
if (scriptDescriptor.resultFieldName() == name.asString()) {
scriptDescriptor.resultValue?.let {
result.add(it)
}
}
}
override fun createPropertiesFromPrimaryConstructorParameters(name: Name, result: MutableSet<PropertyDescriptor>) { override fun createPropertiesFromPrimaryConstructorParameters(name: Name, result: MutableSet<PropertyDescriptor>) {
} }
@@ -24,6 +24,11 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name 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.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.module 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.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.data.KtScriptInfo import org.jetbrains.kotlin.resolve.lazy.data.KtScriptInfo
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider 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.ScriptEnvironmentDescriptor
import org.jetbrains.kotlin.resolve.lazy.descriptors.script.classId import org.jetbrains.kotlin.resolve.lazy.descriptors.script.classId
import org.jetbrains.kotlin.resolve.scopes.LexicalScope 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.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptPriorities import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.types.TypeSubstitutor 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.KClass
import kotlin.reflect.KType import kotlin.reflect.KType
@@ -59,6 +67,42 @@ class LazyScriptDescriptor(
resolveSession.trace.record(BindingContext.SCRIPT, scriptInfo.script, this) resolveSession.trace.record(BindingContext.SCRIPT, scriptInfo.script, this)
} }
val resultValue: ReplResultPropertyDescriptor? by lazy { provideResultValue() }
private fun provideResultValue(): ReplResultPropertyDescriptor? {
val expression = scriptInfo.script
.getChildOfType<KtBlockExpression>()
?.getChildrenOfType<KtScriptInitializer>()?.lastOrNull()
?.getChildOfType<KtExpression>()
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() private val sourceElement = scriptInfo.script.toSourceElement()
override fun getSource() = sourceElement override fun getSource() = sourceElement
@@ -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
)
}
}