Implement support for "resX" result fields in REPL
This commit is contained in:
committed by
Ilya Chernikov
parent
79e577ae27
commit
c901d6cebc
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-7
@@ -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)
|
||||||
|
|||||||
+11
-3
@@ -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>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+44
@@ -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
|
||||||
|
|||||||
+40
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user