Improve script and REPL result handling:
- implement error result - refactor other result classes - implement handling in the script evaluation extension - also restores previous script error reporting functionality - add possibility to customize result fileds in script and REPL - refactor result calculation in the backend: cleanup, rename (since it is not only about REPL now)
This commit is contained in:
@@ -364,23 +364,27 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
private void putStackValue(@Nullable KtElement expr, @NotNull Type type, @Nullable KotlinType kotlinType, @NotNull StackValue value) {
|
||||
// for repl store the result of the last line into special field
|
||||
if (value.type != Type.VOID_TYPE && state.getReplSpecific().getShouldGenerateScriptResultValue()) {
|
||||
if (value.type != Type.VOID_TYPE) {
|
||||
ScriptContext context = getScriptContext();
|
||||
if (expr == context.getLastStatement()) {
|
||||
StackValue.Field resultValue = StackValue.field(context.getResultFieldInfo(), StackValue.LOCAL_0);
|
||||
resultValue.store(value, v);
|
||||
state.getReplSpecific().setHasResult(true);
|
||||
return;
|
||||
if (context != null && expr == context.getLastStatement()) {
|
||||
FieldInfo resultFieldInfo = context.getResultFieldInfo();
|
||||
if (resultFieldInfo != null) {
|
||||
StackValue.Field resultValue = StackValue.field(resultFieldInfo, StackValue.LOCAL_0);
|
||||
resultValue.store(value, v);
|
||||
state.getScriptSpecific().setResultType(resultFieldInfo.getFieldKotlinType());
|
||||
state.getScriptSpecific().setResultFieldName(resultFieldInfo.getFieldName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value.put(type, kotlinType, v);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nullable
|
||||
private ScriptContext getScriptContext() {
|
||||
CodegenContext context = getContext();
|
||||
while (!(context instanceof ScriptContext)) {
|
||||
while (context != null && !(context instanceof ScriptContext)) {
|
||||
context = context.getParentContext();
|
||||
}
|
||||
return (ScriptContext) context;
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
@@ -86,14 +85,14 @@ class ScriptCodegen private constructor(
|
||||
)
|
||||
val asmMethod = jvmSignature.asmMethod
|
||||
|
||||
if (state.replSpecific.shouldGenerateScriptResultValue) {
|
||||
val resultFieldInfo = scriptContext.resultFieldInfo
|
||||
scriptContext.resultFieldInfo?.let { resultFieldInfo ->
|
||||
classBuilder.newField(
|
||||
JvmDeclarationOrigin.NO_ORIGIN,
|
||||
ACC_PUBLIC or ACC_FINAL,
|
||||
resultFieldInfo.fieldName,
|
||||
resultFieldInfo.fieldType.descriptor,
|
||||
null, null)
|
||||
NO_ORIGIN,
|
||||
ACC_PUBLIC or ACC_FINAL,
|
||||
resultFieldInfo.fieldName,
|
||||
resultFieldInfo.fieldType.descriptor,
|
||||
null, null
|
||||
)
|
||||
}
|
||||
|
||||
val mv = classBuilder.newMethod(
|
||||
@@ -277,7 +276,7 @@ class ScriptCodegen private constructor(
|
||||
val builder = state.factory.newVisitor(
|
||||
OtherOrigin(declaration, scriptDescriptor), classType, declaration.containingFile)
|
||||
|
||||
val earlierScripts = state.replSpecific.earlierScriptsForReplInterpreter
|
||||
val earlierScripts = state.scriptSpecific.earlierScriptsForReplInterpreter
|
||||
|
||||
val scriptContext = parentContext.intoScript(
|
||||
scriptDescriptor,
|
||||
|
||||
@@ -42,15 +42,15 @@ class ScriptContext(
|
||||
) : ScriptLikeContext(typeMapper, contextDescriptor, parentContext) {
|
||||
val lastStatement: KtExpression?
|
||||
|
||||
val resultFieldInfo: FieldInfo
|
||||
val resultFieldInfo: FieldInfo?
|
||||
get() {
|
||||
assert(state.replSpecific.shouldGenerateScriptResultValue) { "Should not be called unless 'resultFieldName' is set" }
|
||||
val scriptResultFieldName = state.replSpecific.scriptResultFieldName!!
|
||||
val fieldType = state.replSpecific.resultType?.let { state.typeMapper.mapType(it) } ?: AsmTypes.OBJECT_TYPE
|
||||
val resultValue = scriptDescriptor.resultValue ?: return null
|
||||
val scriptResultFieldName = resultValue.name.identifier
|
||||
val fieldType = resultValue.returnType?.let { state.typeMapper.mapType(it) } ?: AsmTypes.OBJECT_TYPE
|
||||
return FieldInfo.createForHiddenField(
|
||||
state.typeMapper.mapClass(scriptDescriptor),
|
||||
fieldType,
|
||||
state.replSpecific.resultType,
|
||||
resultValue.returnType,
|
||||
scriptResultFieldName
|
||||
)
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
||||
|
||||
return when (descriptor) {
|
||||
is ScriptDescriptor -> {
|
||||
val earlierScripts = state.replSpecific.earlierScriptsForReplInterpreter
|
||||
val earlierScripts = state.scriptSpecific.earlierScriptsForReplInterpreter
|
||||
containerContext.intoScript(
|
||||
descriptor,
|
||||
earlierScripts ?: emptyList(),
|
||||
|
||||
@@ -214,15 +214,15 @@ class GenerationState private constructor(
|
||||
val factory: ClassFileFactory
|
||||
private lateinit var duplicateSignatureFactory: BuilderFactoryForDuplicateSignatureDiagnostics
|
||||
|
||||
val replSpecific = ForRepl()
|
||||
val scriptSpecific = ForScript()
|
||||
|
||||
//TODO: should be refactored out
|
||||
class ForRepl {
|
||||
// TODO: review usages and consider replace mutability with explicit passing of input and output
|
||||
class ForScript {
|
||||
// quite a mess, this one is an input from repl interpreter
|
||||
var earlierScriptsForReplInterpreter: List<ScriptDescriptor>? = null
|
||||
var scriptResultFieldName: String? = null
|
||||
val shouldGenerateScriptResultValue: Boolean get() = scriptResultFieldName != null
|
||||
// and the rest is an output from the codegen
|
||||
var resultFieldName: String? = null
|
||||
var resultType: KotlinType? = null
|
||||
var hasResult: Boolean = false
|
||||
}
|
||||
|
||||
val isCallAssertionsDisabled: Boolean = configuration.getBoolean(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS)
|
||||
|
||||
+9
-5
@@ -108,12 +108,16 @@ open class GenericReplEvaluator(
|
||||
|
||||
historyActor.addFinal(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper))
|
||||
|
||||
val resultFieldName = scriptResultFieldName(compileResult.lineId.no)
|
||||
val resultField = scriptClass.getDeclaredField(resultFieldName).apply { isAccessible = true }
|
||||
val resultValue: Any? = resultField.get(scriptInstance)
|
||||
return if (compileResult.hasResult) {
|
||||
val resultFieldName = scriptResultFieldName(compileResult.lineId.no)
|
||||
val resultField = scriptClass.declaredFields.find { it.name == resultFieldName }?.apply { isAccessible = true }
|
||||
assert(resultField != null) { "compileResult.hasResult == true but resultField is null" }
|
||||
val resultValue: Any? = resultField!!.get(scriptInstance)
|
||||
|
||||
return if (compileResult.hasResult) ReplEvalResult.ValueResult(resultFieldName, resultValue, compileResult.type)
|
||||
else ReplEvalResult.UnitResult()
|
||||
ReplEvalResult.ValueResult(resultFieldName, resultValue, compileResult.type)
|
||||
} else {
|
||||
ReplEvalResult.UnitResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user