Preserve VM throw sites in stack traces
This commit is contained in:
1 parent
fada848907
commit
b42ceec686
11 files changed
+137
-14
No files matched your search
@@ -33,6 +33,17 @@ data class Pos(val source: Source, val line: Int, val column: Int) {
|
||||
if( end ) "EOF"
|
||||
else if( line >= 0 ) source.lines[line] else "<no line information>"
|
||||
|
||||
val currentLineTrimmedStart: String get() = currentLine.trimStart()
|
||||
|
||||
val currentLineIndentWidth: Int
|
||||
get() {
|
||||
val lineText = currentLine
|
||||
val firstNonWhitespace = lineText.indexOfFirst { !it.isWhitespace() }
|
||||
return if (firstNonWhitespace >= 0) firstNonWhitespace else 0
|
||||
}
|
||||
|
||||
val visualColumn: Int get() = (column - currentLineIndentWidth).coerceAtLeast(0)
|
||||
|
||||
val end: Boolean get() = line >= source.lines.size
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -25,14 +25,15 @@ open class ScriptError(val pos: Pos, val errorMessage: String, cause: Throwable?
|
||||
"""
|
||||
$pos: Error: $errorMessage
|
||||
|
||||
${pos.currentLine}
|
||||
${if( pos.column >= 0 ) "-".repeat(pos.column) + "^" else ""}
|
||||
${pos.currentLineTrimmedStart}
|
||||
${if( pos.column >= 0 ) "-".repeat(pos.visualColumn) + "^" else ""}
|
||||
""".trimIndent(),
|
||||
cause
|
||||
)
|
||||
|
||||
class ScriptFlowIsNoMoreCollected: Exception()
|
||||
|
||||
class ExecutionError(val errorObject: Obj, pos: Pos, message: String) : ScriptError(pos, message)
|
||||
class ExecutionError(val errorObject: Obj, pos: Pos, message: String, cause: Throwable? = null) :
|
||||
ScriptError(pos, message, cause)
|
||||
|
||||
class ImportException(pos: Pos, message: String) : ScriptError(pos, message)
|
||||
class ImportException(pos: Pos, message: String) : ScriptError(pos, message)
|
||||
@@ -3650,11 +3650,14 @@ class BytecodeCompiler(
|
||||
}
|
||||
|
||||
private fun compileIndexRef(ref: IndexRef): CompiledValue? {
|
||||
val indexPos = refPosOrCurrent(ref.targetRef)
|
||||
setPos(indexPos)
|
||||
val receiver = compileRefWithFallback(ref.targetRef, null, Pos.builtIn) ?: return null
|
||||
val elementSlotType = indexElementSlotType(receiver.slot, ref.targetRef)
|
||||
val dst = allocSlot()
|
||||
if (!ref.optionalRef) {
|
||||
val index = compileRefWithFallback(ref.indexRef, null, Pos.builtIn) ?: return null
|
||||
setPos(indexPos)
|
||||
if (elementSlotType == SlotType.INT && index.type == SlotType.INT) {
|
||||
builder.emit(Opcode.GET_INDEX_INT, receiver.slot, index.slot, dst)
|
||||
updateSlotType(dst, SlotType.INT)
|
||||
|
||||
@@ -43,9 +43,10 @@ class CmdVm {
|
||||
}
|
||||
break
|
||||
} catch (e: Throwable) {
|
||||
if (!frame.handleException(e)) {
|
||||
val throwable = frame.normalizeThrowable(e)
|
||||
if (!frame.handleException(throwable)) {
|
||||
frame.cancelIterators()
|
||||
throw e
|
||||
throw throwable
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4432,6 +4433,19 @@ class CmdFrame(
|
||||
return scope
|
||||
}
|
||||
|
||||
suspend fun normalizeThrowable(t: Throwable): Throwable {
|
||||
if (t is ExecutionError || t is ReturnException || t is LoopBreakContinueException) return t
|
||||
val parentScope = ensureScope()
|
||||
val pos = (t as? ScriptError)?.pos ?: currentErrorPos() ?: parentScope.pos
|
||||
val throwScope = parentScope.createChildScope(pos = pos)
|
||||
val message = when (t) {
|
||||
is ScriptError -> t.errorMessage
|
||||
else -> t.message ?: t.toString()
|
||||
}
|
||||
val errorObject = ObjUnknownException(throwScope, message).apply { getStackTrace() }
|
||||
return ExecutionError(errorObject, pos, message, t)
|
||||
}
|
||||
|
||||
suspend fun handleException(t: Throwable): Boolean {
|
||||
val handler = tryStack.lastOrNull() ?: return false
|
||||
vmIterDebug {
|
||||
|
||||
@@ -106,16 +106,18 @@ open class ObjException(
|
||||
val pos = s.pos
|
||||
if (pos != lastPos && !pos.currentLine.isEmpty()) {
|
||||
if (lastPos == null || (lastPos.source != pos.source || lastPos.line != pos.line)) {
|
||||
val sourceLine = pos.currentLineTrimmedStart
|
||||
val visualColumn = pos.visualColumn
|
||||
val fallback =
|
||||
ObjString("#${pos.source.objSourceName}:${pos.line+1}:${pos.column+1}: ${pos.currentLine}")
|
||||
ObjString("#${pos.source.objSourceName}:${pos.line+1}:${visualColumn+1}: $sourceLine")
|
||||
if (maybeCls != null) {
|
||||
try {
|
||||
result.list += maybeCls.callWithArgs(
|
||||
scope,
|
||||
pos.source.objSourceName,
|
||||
ObjInt(pos.line.toLong()),
|
||||
ObjInt(pos.column.toLong()),
|
||||
ObjString(pos.currentLine)
|
||||
ObjInt(visualColumn.toLong()),
|
||||
ObjString(sourceLine)
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
// Fallback textual entry if StackTraceEntry fails to instantiate
|
||||
|
||||
Reference in new issue
Block a user