diff --git a/src/d_main.cpp b/src/d_main.cpp index dad2b86d9..bffb4136d 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1027,6 +1027,12 @@ void D_DoomLoop () } D_ErrorCleanup (); } + catch (CVMAbortException &error) + { + error.MaybePrintMessage(); + Printf("%s", error.stacktrace); + D_ErrorCleanup(); + } } } diff --git a/src/info.cpp b/src/info.cpp index a0117a71f..d3cb4e274 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -89,16 +89,35 @@ bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info, stateret = NULL; } } - if (stateret == NULL) + try { - GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, NULL, 0, NULL); + if (stateret == NULL) + { + GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, NULL, 0, NULL); + } + else + { + VMReturn ret; + ret.PointerAt((void **)stateret); + GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1, NULL); + } } - else + catch (CVMAbortException &err) { - VMReturn ret; - ret.PointerAt((void **)stateret); - GlobalVMStack.Call(ActionFunc, params, ActionFunc->ImplicitArgs, &ret, 1, NULL); + err.MaybePrintMessage(); + auto owner = FState::StaticFindStateOwner(this); + int offs = int(this - owner->OwnedStates); + const char *callinfo = ""; + if (info != nullptr && info->mStateType == STATE_Psprite) + { + if (stateowner->IsKindOf(RUNTIME_CLASS(AWeapon)) && stateowner != self) callinfo = "weapon "; + else callinfo = "overlay "; + } + err.stacktrace.AppendFormat("Called from %sstate %s.%d in %s\n", callinfo, owner->TypeName.GetChars(), offs, stateowner->GetClass()->TypeName.GetChars()); + throw; + throw; } + ActionCycles.Unclock(); return true; } diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 83844ffd7..12645561b 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -183,7 +183,19 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state) numret = 2; } } - GlobalVMStack.Call(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); + try + { + GlobalVMStack.Call(state->ActionFunc, params, state->ActionFunc->ImplicitArgs, wantret, numret); + } + catch (CVMAbortException &err) + { + err.MaybePrintMessage(); + auto owner = FState::StaticFindStateOwner(state); + int offs = int(state - owner->OwnedStates); + err.stacktrace.AppendFormat("Called from state %s.%d in inventory state chain in %s\n", owner->TypeName.GetChars(), offs, GetClass()->TypeName.GetChars()); + throw; + } + // As long as even one state succeeds, the whole chain succeeds unless aborted below. // A state that wants to jump does not count as "succeeded". if (nextstate == NULL) diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index aeb52dabb..1869c410b 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -194,7 +194,8 @@ class CVMAbortException : public CDoomError { public: static FString stacktrace; - CVMAbortException(EVMAbortException reason, const char *moreinfo, ...); + CVMAbortException(EVMAbortException reason, const char *moreinfo, va_list ap); + void MaybePrintMessage(); }; enum EVMOpMode @@ -808,6 +809,12 @@ union FVoidObj void *v; }; +struct FStatementInfo +{ + uint16_t InstructionIndex; + uint16_t LineNumber; +}; + class VMScriptFunction : public VMFunction { DECLARE_CLASS(VMScriptFunction, VMFunction); @@ -815,12 +822,13 @@ public: VMScriptFunction(FName name=NAME_None); ~VMScriptFunction(); size_t PropagateMark(); - void Alloc(int numops, int numkonstd, int numkonstf, int numkonsts, int numkonsta); + void Alloc(int numops, int numkonstd, int numkonstf, int numkonsts, int numkonsta, int numlinenumbers); VM_ATAG *KonstATags() { return (VM_UBYTE *)(KonstA + NumKonstA); } const VM_ATAG *KonstATags() const { return (VM_UBYTE *)(KonstA + NumKonstA); } VMOP *Code; + FStatementInfo *LineInfo; FString SourceFileName; int *KonstD; double *KonstF; @@ -828,6 +836,7 @@ public: FVoidObj *KonstA; int ExtraSpace; int CodeSize; // Size of code in instructions (not bytes) + unsigned LineInfoCount; VM_UBYTE NumRegD; VM_UBYTE NumRegF; VM_UBYTE NumRegS; @@ -843,6 +852,7 @@ public: void InitExtra(void *addr); void DestroyExtra(void *addr); int AllocExtraStack(PType *type); + int PCToLine(const VMOP *pc); }; class VMFrameStack diff --git a/src/scripting/vm/vmbuilder.cpp b/src/scripting/vm/vmbuilder.cpp index 65ec0bb26..3e833af79 100644 --- a/src/scripting/vm/vmbuilder.cpp +++ b/src/scripting/vm/vmbuilder.cpp @@ -88,7 +88,7 @@ void VMFunctionBuilder::BeginStatement(FxExpression *stmt) // only add a new entry if the line number differs. if (LineNumbers.Size() == 0 || stmt->ScriptPosition.ScriptLine != LineNumbers.Last().LineNumber) { - StatementInfo si = { (uint16_t)Code.Size(), (uint16_t)stmt->ScriptPosition.ScriptLine }; + FStatementInfo si = { (uint16_t)Code.Size(), (uint16_t)stmt->ScriptPosition.ScriptLine }; LineNumbers.Push(si); } StatementStack.Push(stmt); @@ -102,17 +102,18 @@ void VMFunctionBuilder::EndStatement() // Re-enter the previous statement. if (StatementStack.Size() > 0) { - StatementInfo si = { (uint16_t)Code.Size(), (uint16_t)StatementStack.Last()->ScriptPosition.ScriptLine }; + FStatementInfo si = { (uint16_t)Code.Size(), (uint16_t)StatementStack.Last()->ScriptPosition.ScriptLine }; LineNumbers.Push(si); } } void VMFunctionBuilder::MakeFunction(VMScriptFunction *func) { - func->Alloc(Code.Size(), IntConstantList.Size(), FloatConstantList.Size(), StringConstantList.Size(), AddressConstantList.Size()); + func->Alloc(Code.Size(), IntConstantList.Size(), FloatConstantList.Size(), StringConstantList.Size(), AddressConstantList.Size(), LineNumbers.Size()); // Copy code block. memcpy(func->Code, &Code[0], Code.Size() * sizeof(VMOP)); + memcpy(func->LineInfo, &LineNumbers[0], LineNumbers.Size() * sizeof(LineNumbers[0])); // Create constant tables. if (IntConstantList.Size() > 0) diff --git a/src/scripting/vm/vmbuilder.h b/src/scripting/vm/vmbuilder.h index d319b2280..7c086ac0c 100644 --- a/src/scripting/vm/vmbuilder.h +++ b/src/scripting/vm/vmbuilder.h @@ -100,13 +100,7 @@ private: VM_ATAG Tag; }; - struct StatementInfo - { - uint16_t InstructionIndex; - uint16_t LineNumber; - }; - - TArray<StatementInfo> LineNumbers; + TArray<FStatementInfo> LineNumbers; TArray<FxExpression *> StatementStack; TArray<int> IntConstantList; diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index 198410864..79d0b7fe6 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -40,6 +40,11 @@ #include "textures/textures.h" #include "math/cmath.h" +// This must be a separate function because the VC compiler would otherwise allocate memory on the stack for every separate instance of the exception object that may get thrown. +void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...); +// intentionally implemented in a different source file tp prevent inlining. +void ThrowVMException(VMException *x); + #define IMPLEMENT_VMEXEC #if !defined(COMPGOTO) && defined(__GNUC__) @@ -91,7 +96,7 @@ } #define GETADDR(a,o,x) \ - if (a == NULL) { throw CVMAbortException(x, nullptr); } \ + if (a == NULL) { ThrowAbortException(x, nullptr); } \ ptr = (VM_SBYTE *)a + o static const VM_UWORD ZapTable[16] = @@ -227,57 +232,4 @@ void VMFillParams(VMValue *params, VMFrame *callee, int numparam) } } -void NullParam(const char *varname) -{ - throw CVMAbortException(X_READ_NIL, "In function parameter %s", varname); -} - -FString CVMAbortException::stacktrace; - -CVMAbortException::CVMAbortException(EVMAbortException reason, const char *moreinfo, ...) -{ - SetMessage("VM execution aborted: "); - switch (reason) - { - case X_READ_NIL: - AppendMessage("tried to read from address zero."); - break; - - case X_WRITE_NIL: - AppendMessage("tried to write to address zero."); - break; - - case X_TOO_MANY_TRIES: - AppendMessage("too many try-catch blocks."); - break; - - case X_ARRAY_OUT_OF_BOUNDS: - AppendMessage("array access out of bounds."); - break; - - case X_DIVISION_BY_ZERO: - AppendMessage("division by zero."); - break; - - case X_BAD_SELF: - AppendMessage("invalid self pointer."); - break; - - default: - { - size_t len = strlen(m_Message); - mysnprintf(m_Message + len, MAX_ERRORTEXT - len, "Unknown reason %d", reason); - break; - } - } - if (moreinfo != nullptr) - { - AppendMessage(" "); - va_list ap; - va_start(ap, moreinfo); - size_t len = strlen(m_Message); - myvsnprintf(m_Message + len, MAX_ERRORTEXT - len, moreinfo, ap); - va_end(ap); - } -} diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 1e688365d..4152facc1 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -2,7 +2,6 @@ #error vmexec.h must not be #included outside vmexec.cpp. Use vm.h instead. #endif - static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret) { #if COMPGOTO @@ -596,7 +595,17 @@ begin: FillReturns(reg, f, returns, pc+1, C); if (call->Native) { - numret = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, returns, C); + try + { + numret = static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, returns, C); + } + catch (CVMAbortException &err) + { + err.MaybePrintMessage(); + err.stacktrace.AppendFormat("Called from %s\n", call->PrintableName.GetChars()); + // PrintParameters(reg.param + f->NumParam - B, B); + throw; + } } else { @@ -640,7 +649,17 @@ begin: if (call->Native) { - return static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, ret, numret); + try + { + return static_cast<VMNativeFunction *>(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, ret, numret); + } + catch (CVMAbortException &err) + { + err.MaybePrintMessage(); + err.stacktrace.AppendFormat("Called from %s\n", call->PrintableName.GetChars()); + // PrintParameters(reg.param + f->NumParam - B, B); + throw; + } } else { // FIXME: Not a true tail call @@ -704,7 +723,7 @@ begin: assert(try_depth < MAX_TRY_DEPTH); if (try_depth >= MAX_TRY_DEPTH) { - throw CVMAbortException(X_TOO_MANY_TRIES, nullptr); + ThrowAbortException(X_TOO_MANY_TRIES, nullptr); } assert((pc + JMPOFS(pc) + 1)->op == OP_CATCH); exception_frames[try_depth++] = pc + JMPOFS(pc) + 1; @@ -717,17 +736,17 @@ begin: if (a == 0) { ASSERTA(B); - throw((VMException *)reg.a[B]); + ThrowVMException((VMException *)reg.a[B]); } else if (a == 1) { ASSERTKA(B); assert(konstatag[B] == ATAG_OBJECT); - throw((VMException *)konsta[B].o); + ThrowVMException((VMException *)konsta[B].o); } else { - throw CVMAbortException(EVMAbortException(BC), nullptr); + ThrowAbortException(EVMAbortException(BC), nullptr); } NEXTOP; OP(CATCH): @@ -739,7 +758,7 @@ begin: OP(BOUND): if (reg.d[a] >= BC) { - throw CVMAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", BC, reg.d[a]); + ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", BC, reg.d[a]); } NEXTOP; @@ -747,7 +766,7 @@ begin: ASSERTKD(BC); if (reg.d[a] >= konstd[BC]) { - throw CVMAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", konstd[BC], reg.d[a]); + ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", konstd[BC], reg.d[a]); } NEXTOP; @@ -755,7 +774,7 @@ begin: ASSERTD(B); if (reg.d[a] >= reg.d[B]) { - throw CVMAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", reg.d[B], reg.d[a]); + ThrowAbortException(X_ARRAY_OUT_OF_BOUNDS, "Max.index = %u, current index = %u\n", reg.d[B], reg.d[a]); } NEXTOP; @@ -902,7 +921,7 @@ begin: ASSERTD(a); ASSERTD(B); ASSERTD(C); if (reg.d[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = reg.d[B] / reg.d[C]; NEXTOP; @@ -910,7 +929,7 @@ begin: ASSERTD(a); ASSERTD(B); ASSERTKD(C); if (konstd[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = reg.d[B] / konstd[C]; NEXTOP; @@ -918,7 +937,7 @@ begin: ASSERTD(a); ASSERTKD(B); ASSERTD(C); if (reg.d[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = konstd[B] / reg.d[C]; NEXTOP; @@ -927,7 +946,7 @@ begin: ASSERTD(a); ASSERTD(B); ASSERTD(C); if (reg.d[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = int((unsigned)reg.d[B] / (unsigned)reg.d[C]); NEXTOP; @@ -935,7 +954,7 @@ begin: ASSERTD(a); ASSERTD(B); ASSERTKD(C); if (konstd[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = int((unsigned)reg.d[B] / (unsigned)konstd[C]); NEXTOP; @@ -943,7 +962,7 @@ begin: ASSERTD(a); ASSERTKD(B); ASSERTD(C); if (reg.d[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = int((unsigned)konstd[B] / (unsigned)reg.d[C]); NEXTOP; @@ -952,7 +971,7 @@ begin: ASSERTD(a); ASSERTD(B); ASSERTD(C); if (reg.d[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = reg.d[B] % reg.d[C]; NEXTOP; @@ -960,7 +979,7 @@ begin: ASSERTD(a); ASSERTD(B); ASSERTKD(C); if (konstd[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = reg.d[B] % konstd[C]; NEXTOP; @@ -968,7 +987,7 @@ begin: ASSERTD(a); ASSERTKD(B); ASSERTD(C); if (reg.d[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = konstd[B] % reg.d[C]; NEXTOP; @@ -977,7 +996,7 @@ begin: ASSERTD(a); ASSERTD(B); ASSERTD(C); if (reg.d[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = int((unsigned)reg.d[B] % (unsigned)reg.d[C]); NEXTOP; @@ -985,7 +1004,7 @@ begin: ASSERTD(a); ASSERTD(B); ASSERTKD(C); if (konstd[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = int((unsigned)reg.d[B] % (unsigned)konstd[C]); NEXTOP; @@ -993,7 +1012,7 @@ begin: ASSERTD(a); ASSERTKD(B); ASSERTD(C); if (reg.d[C] == 0) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.d[a] = int((unsigned)konstd[B] % (unsigned)reg.d[C]); NEXTOP; @@ -1171,7 +1190,7 @@ begin: ASSERTF(a); ASSERTF(B); ASSERTF(C); if (reg.f[C] == 0.) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.f[a] = reg.f[B] / reg.f[C]; NEXTOP; @@ -1179,7 +1198,7 @@ begin: ASSERTF(a); ASSERTF(B); ASSERTKF(C); if (konstf[C] == 0.) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.f[a] = reg.f[B] / konstf[C]; NEXTOP; @@ -1187,7 +1206,7 @@ begin: ASSERTF(a); ASSERTKF(B); ASSERTF(C); if (reg.f[C] == 0.) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.f[a] = konstf[B] / reg.f[C]; NEXTOP; @@ -1198,7 +1217,7 @@ begin: Do_MODF: if (fc == 0.) { - throw CVMAbortException(X_DIVISION_BY_ZERO, nullptr); + ThrowAbortException(X_DIVISION_BY_ZERO, nullptr); } reg.f[a] = luai_nummod(fb, fc); NEXTOP; @@ -1611,6 +1630,13 @@ begin: // Nothing caught it. Rethrow and let somebody else deal with it. throw; } + catch (CVMAbortException &err) + { + err.MaybePrintMessage(); + err.stacktrace.AppendFormat("Called from %s at %s, line %d\n", sfunc->PrintableName.GetChars(), sfunc->SourceFileName.GetChars(), sfunc->PCToLine(pc)); + // PrintParameters(reg.param + f->NumParam - B, B); + throw; + } return 0; } diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 3a7d9b3bf..f9f04e425 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -3,6 +3,7 @@ ** **--------------------------------------------------------------------------- ** Copyright -2016 Randy Heit +** Copyright 2016 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -33,6 +34,7 @@ #include <new> #include "dobject.h" +#include "v_text.h" IMPLEMENT_CLASS(VMException, false, false) IMPLEMENT_CLASS(VMFunction, true, true) @@ -48,11 +50,13 @@ VMScriptFunction::VMScriptFunction(FName name) { Native = false; Name = name; + LineInfo = nullptr; Code = NULL; KonstD = NULL; KonstF = NULL; KonstS = NULL; KonstA = NULL; + LineInfoCount = 0; ExtraSpace = 0; CodeSize = 0; NumRegD = 0; @@ -82,7 +86,7 @@ VMScriptFunction::~VMScriptFunction() } } -void VMScriptFunction::Alloc(int numops, int numkonstd, int numkonstf, int numkonsts, int numkonsta) +void VMScriptFunction::Alloc(int numops, int numkonstd, int numkonstf, int numkonsts, int numkonsta, int numlinenumbers) { assert(Code == NULL); assert(numops > 0); @@ -90,14 +94,27 @@ void VMScriptFunction::Alloc(int numops, int numkonstd, int numkonstf, int numko assert(numkonstf >= 0 && numkonstf <= 65535); assert(numkonsts >= 0 && numkonsts <= 65535); assert(numkonsta >= 0 && numkonsta <= 65535); + assert(numlinenumbers >= 0 && numlinenumbers <= 65535); void *mem = M_Malloc(numops * sizeof(VMOP) + numkonstd * sizeof(int) + numkonstf * sizeof(double) + numkonsts * sizeof(FString) + - numkonsta * (sizeof(FVoidObj) + 1)); + numkonsta * (sizeof(FVoidObj) + 1) + + numlinenumbers * sizeof(FStatementInfo)); Code = (VMOP *)mem; mem = (void *)((VMOP *)mem + numops); + if (numlinenumbers > 0) + { + LineInfo = (FStatementInfo*)mem; + LineInfoCount = numlinenumbers; + mem = LineInfo + numlinenumbers; + } + else + { + LineInfo = nullptr; + LineInfoCount = 0; + } if (numkonstd > 0) { KonstD = (int *)mem; @@ -190,6 +207,20 @@ int VMScriptFunction::AllocExtraStack(PType *type) return address; } +int VMScriptFunction::PCToLine(const VMOP *pc) +{ + int PCIndex = int(pc - Code); + if (LineInfoCount == 1) return LineInfo[0].LineNumber; + for (unsigned i = 1; i < LineInfoCount; i++) + { + if (LineInfo[i].InstructionIndex > PCIndex) + { + return LineInfo[i - 1].LineNumber; + } + } + return -1; +} + //=========================================================================== // // VMFrame :: InitRegS @@ -453,20 +484,6 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur } throw; } - catch (CVMAbortException &exception) - { - if (allocated) - { - PopFrame(); - } - if (trap != nullptr) - { - *trap = nullptr; - } - - Printf("%s\n", exception.GetMessage()); - return -1; - } catch (...) { if (allocated) @@ -476,3 +493,83 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur throw; } } + +// Exception stuff for the VM is intentionally placed there, because having this in vmexec.cpp would subject it to inlining +// which we do not want because it increases the local stack requirements of Exec which are already too high. +FString CVMAbortException::stacktrace; + +CVMAbortException::CVMAbortException(EVMAbortException reason, const char *moreinfo, va_list ap) +{ + SetMessage("VM execution aborted: "); + switch (reason) + { + case X_READ_NIL: + AppendMessage("tried to read from address zero."); + break; + + case X_WRITE_NIL: + AppendMessage("tried to write to address zero."); + break; + + case X_TOO_MANY_TRIES: + AppendMessage("too many try-catch blocks."); + break; + + case X_ARRAY_OUT_OF_BOUNDS: + AppendMessage("array access out of bounds."); + break; + + case X_DIVISION_BY_ZERO: + AppendMessage("division by zero."); + break; + + case X_BAD_SELF: + AppendMessage("invalid self pointer."); + break; + + default: + { + size_t len = strlen(m_Message); + mysnprintf(m_Message + len, MAX_ERRORTEXT - len, "Unknown reason %d", reason); + break; + } + } + if (moreinfo != nullptr) + { + AppendMessage(" "); + size_t len = strlen(m_Message); + myvsnprintf(m_Message + len, MAX_ERRORTEXT - len, moreinfo, ap); + } + stacktrace = ""; +} + +// Print this only once on the first catch block. +void CVMAbortException::MaybePrintMessage() +{ + auto m = GetMessage(); + if (m != nullptr) + { + Printf(TEXTCOLOR_RED); + Printf("%s\n", m); + SetMessage(""); + } +} + + +void ThrowAbortException(EVMAbortException reason, const char *moreinfo, ...) +{ + va_list ap; + va_start(ap, moreinfo); + throw CVMAbortException(reason, moreinfo, ap); + va_end(ap); +} + +void NullParam(const char *varname) +{ + ThrowAbortException(X_READ_NIL, "In function parameter %s", varname); +} + +void ThrowVMException(VMException *x) +{ + throw x; +}