REPL: call 'toString()' for returned values explicitly and correctly report exceptions

#KT-12389 Fixed
This commit is contained in:
Pavel V. Talanov
2016-05-31 18:33:53 +03:00
parent 8ac40d7401
commit 5d31d6d98d
8 changed files with 47 additions and 22 deletions
@@ -100,7 +100,7 @@ class ReplFromTerminal(
is ValueResult, UnitResult -> {
writer.notifyCommandSuccess()
if (lineResult is ValueResult) {
writer.outputCommandResult(lineResult.value)
writer.outputCommandResult(lineResult.valueAsString)
}
}
Incomplete -> writer.notifyIncomplete()
@@ -132,25 +132,28 @@ class ReplInterpreter(
val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams)
val scriptInstance = try {
onUserCodeExecuting(true)
scriptInstanceConstructor.newInstance(*constructorArgs)
executeUserCode { scriptInstanceConstructor.newInstance(*constructorArgs) }
}
catch (e: Throwable) {
return LineResult.Error.Runtime(renderStackTrace(e.cause!!))
}
finally {
onUserCodeExecuting(false)
// ignore everything in the stack trace until this constructor call
return LineResult.Error.Runtime(renderStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"))
}
val rvField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true }
val rv = rvField.get(scriptInstance)
val rv: Any? = rvField.get(scriptInstance)
earlierLines.add(EarlierLine(line, scriptDescriptor, scriptClass, scriptInstance))
if (!state.replSpecific.hasResult) {
return LineResult.UnitResult
}
return LineResult.ValueResult(rv)
val valueAsString: String = try {
executeUserCode { rv.toString() }
}
catch (e: Throwable) {
return LineResult.Error.Runtime(renderStackTrace(e, startFromMethodName = "java.lang.String.valueOf"))
}
return LineResult.ValueResult(valueAsString)
}
catch (e: Throwable) {
val writer = PrintWriter(System.err)
@@ -160,6 +163,16 @@ class ReplInterpreter(
}
}
private fun <T> executeUserCode(body: () -> T): T {
try {
onUserCodeExecuting(true)
return body()
}
finally {
onUserCodeExecuting(false)
}
}
fun dumpClasses(out: PrintWriter) {
classLoader.dumpClasses(out)
}
@@ -176,13 +189,11 @@ class ReplInterpreter(
override fun getScriptName(script: KtScript): Name = StandardScriptDefinition.getScriptName(script)
}
private fun renderStackTrace(cause: Throwable): String {
private fun renderStackTrace(cause: Throwable, startFromMethodName: String): String {
val newTrace = arrayListOf<StackTraceElement>()
var skip = true
for ((i, element) in cause.stackTrace.withIndex().reversed()) {
// All our code happens in the script constructor, and no reflection/native code happens in constructors.
// So we ignore everything in the stack trace until the first constructor
if (element.methodName == "<init>") {
if ("${element.className}.${element.methodName}" == startFromMethodName) {
skip = false
}
if (!skip) {
@@ -190,7 +201,6 @@ class ReplInterpreter(
}
}
// throw away last element which contains Line1.kts<init>(Unknown source)
val resultingTrace = newTrace.reversed().dropLast(1)
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UsePropertyAccessSyntax")
@@ -220,7 +230,7 @@ class ReplInterpreter(
}
sealed class LineResult {
class ValueResult(val value: Any?): LineResult()
class ValueResult(val valueAsString: String): LineResult()
object UnitResult: LineResult()
object Incomplete : LineResult()
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ class ReplConsoleWriter : ReplWriter {
override fun printlnWelcomeMessage(x: String) = println(x)
override fun printlnHelpMessage(x: String) = println(x)
override fun outputCompileError(x: String) = println(x)
override fun outputCommandResult(x: Any?) = println(x)
override fun outputCommandResult(x: String) = println(x)
override fun outputRuntimeError(x: String) = println(x)
override fun notifyReadLineStart() {}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,7 +60,7 @@ class ReplSystemOutWrapperForIde(standardOut: PrintStream) : PrintStream(standar
override fun printlnWelcomeMessage(x: String) = printlnWithEscaping(x, EscapeType.INITIAL_PROMPT)
override fun printlnHelpMessage(x: String) = printlnWithEscaping(x, EscapeType.HELP_PROMPT)
override fun outputCommandResult(x: Any?) = printlnWithEscaping(x.toString(), EscapeType.REPL_RESULT)
override fun outputCommandResult(x: String) = printlnWithEscaping(x, EscapeType.REPL_RESULT)
override fun notifyReadLineStart() = printlnWithEscaping("", EscapeType.READLINE_START)
override fun notifyReadLineEnd() = printlnWithEscaping("", EscapeType.READLINE_END)
override fun notifyCommandSuccess() = printlnWithEscaping("", EscapeType.SUCCESS)
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.cli.jvm.repl.messages
interface ReplWriter {
fun printlnWelcomeMessage(x: String)
fun printlnHelpMessage(x: String)
fun outputCommandResult(x: Any?)
fun outputCommandResult(x: String)
fun notifyReadLineStart()
fun notifyReadLineEnd()
fun notifyIncomplete()
+9
View File
@@ -0,0 +1,9 @@
>>> class B { override fun toString(): String { return foo() } ; fun foo(): String { return error("message") } }
>>> B().toString()
java.lang.IllegalStateException: message
at Line1$B.foo(line1.kts:1)
at Line1$B.toString(line1.kts:1)
>>> B()
java.lang.IllegalStateException: message
at Line1$B.foo(line1.kts:1)
at Line1$B.toString(line1.kts:1)
@@ -93,7 +93,7 @@ abstract class AbstractReplInterpreterTest : KtUsefulTestCase() {
}
val actual = when (lineResult) {
is LineResult.ValueResult -> "${lineResult.value}"
is LineResult.ValueResult -> lineResult.valueAsString
is LineResult.Error -> lineResult.errorText
LineResult.Incomplete -> INCOMPLETE_LINE_MESSAGE
LineResult.UnitResult -> ""
@@ -59,6 +59,12 @@ public class ReplInterpreterTestGenerated extends AbstractReplInterpreterTest {
doTest(fileName);
}
@TestMetadata("exceptionInValueToString.repl")
public void testExceptionInValueToString() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/repl/exceptionInValueToString.repl");
doTest(fileName);
}
@TestMetadata("function.repl")
public void testFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/repl/function.repl");