Preserve VM throw sites in stack traces

This commit is contained in:
sergeych committed 2026-04-14 01:17:04 +03:00
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