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:
Ilya Chernikov
2019-07-04 13:33:15 +02:00
parent dc4370ff08
commit 9ae0ff03fa
23 changed files with 421 additions and 127 deletions
@@ -7,7 +7,9 @@
package kotlin.script.experimental.api
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream
import java.io.File
import java.io.PrintStream
/**
* The single script diagnostic report
@@ -28,11 +30,28 @@ data class ScriptDiagnostic(
*/
enum class Severity { FATAL, ERROR, WARNING, INFO, DEBUG }
override fun toString(): String = buildString {
append(severity.name)
append(' ')
override fun toString(): String = render()
/**
* Render diagnostics message as a string in a form:
* "[SEVERITY ]message[ (file:line:column)][: exception message[\n exception stacktrace]]"
* @param withSeverity add severity prefix, true by default
* @param withLocation add error location in the compiled script, if present, true by default
* @param withException add exception message, if present, true by default
* @param withStackTrace add exception stacktrace, if exception is present and [withException] is true, false by default
*/
fun render(
withSeverity: Boolean = true,
withLocation: Boolean = true,
withException: Boolean = true,
withStackTrace: Boolean = false
): String = buildString {
if (withSeverity) {
append(severity.name)
append(' ')
}
append(message)
if (sourcePath != null || location != null) {
if (withLocation && (sourcePath != null || location != null)) {
append(" (")
sourcePath?.let { append(it.substringAfterLast(File.separatorChar)) }
location?.let {
@@ -43,9 +62,18 @@ data class ScriptDiagnostic(
}
append(')')
}
if (exception != null) {
if (withException && exception != null) {
append(": ")
append(exception)
if (withStackTrace) {
ByteOutputStream().use { os ->
val ps = PrintStream(os)
exception.printStackTrace(ps)
ps.flush()
append("\n")
append(os.toString())
}
}
}
}
}
@@ -6,6 +6,7 @@
package kotlin.script.experimental.api
import java.io.Serializable
import kotlin.script.experimental.util.PropertiesCollection
const val REPL_SNIPPET_FIRST_NO = 1
const val REPL_SNIPPET_FIRST_GEN = 1
@@ -29,3 +30,21 @@ data class ReplSnippetIdImpl(override val no: Int, override val generation: Int,
private val serialVersionUID: Long = 1L
}
}
interface ReplScriptCompilationConfigurationKeys
open class ReplScriptCompilationConfigurationBuilder : PropertiesCollection.Builder(),
ReplScriptCompilationConfigurationKeys {
companion object : ReplScriptCompilationConfigurationKeys
}
val ScriptCompilationConfigurationKeys.repl
get() = ReplScriptCompilationConfigurationBuilder()
/**
* The prefix of the name of the generated script class field to assign the snipped results to, empty means disabled
* see also ScriptCompilationConfigurationKeys.resultField
*/
val ReplScriptCompilationConfigurationKeys.resultFieldPrefix by PropertiesCollection.key<String>("res")
@@ -99,6 +99,12 @@ val ScriptCompilationConfigurationKeys.defaultImports by PropertiesCollection.ke
*/
val ScriptCompilationConfigurationKeys.importScripts by PropertiesCollection.key<List<SourceCode>>()
/**
* The name of the generated script class field to assign the script results to, empty means disabled
* see also ReplScriptCompilationConfigurationKeys.resultFieldPrefix
*/
val ScriptCompilationConfigurationKeys.resultField by PropertiesCollection.key<String>("\$\$result")
/**
* The list of script dependencies - platform specific
*/
@@ -121,17 +121,40 @@ data class RefineEvaluationConfigurationData(
/**
* The script evaluation result value
*/
sealed class ResultValue {
class Value(val name: String, val value: Any?, val type: String, val scriptInstance: Any) : ResultValue() {
sealed class ResultValue(val scriptInstance: Any? = null) {
/**
* The result value representing a script return value - the value of the last expression in the script
* @param name assigned name of the result field - used e.g. in REPL
* @param value actual result value
* @param type name of the result type
* @param scriptInstance instance of the script class
*/
class Value(val name: String, val value: Any?, val type: String, scriptInstance: Any) : ResultValue(scriptInstance) {
override fun toString(): String = "$name: $type = $value"
}
class UnitValue(val scriptInstance: Any) : ResultValue() {
/**
* The result value representing unit result, e.g. when the script ends with a statement
* @param scriptInstance instance of the script class
*/
class Unit(scriptInstance: Any) : ResultValue(scriptInstance) {
override fun toString(): String = "Unit"
}
// TODO: obsolete it, use differently named value in the saving evaluators
object Unit : ResultValue()
/**
* The result value representing an exception from script itself
* @param error the actual exception thrown on script evaluation
* @param wrappingException the wrapping exception e.g. InvocationTargetException, sometimes useful for calculating the relevant stacktrace
*/
class Error(val error: Throwable, val wrappingException: Throwable? = null) : ResultValue() {
override fun toString(): String = error.toString()
}
/**
* The result value used in non-evaluating "evaluators"
*/
object NotEvaluated : ResultValue()
}
/**