mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
Added Runtime.AddErrorHandler
This commit is contained in:
parent
9e80281d1a
commit
657a64f59c
8 changed files with 192 additions and 6 deletions
|
@ -1,3 +1,6 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace System
|
namespace System
|
||||||
{
|
{
|
||||||
interface IRawAllocator
|
interface IRawAllocator
|
||||||
|
@ -18,4 +21,32 @@ namespace System
|
||||||
Internal.StdFree(ptr);
|
Internal.StdFree(ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AllocWrapper<T> where T : new, delete
|
||||||
|
{
|
||||||
|
alloctype(T) mVal;
|
||||||
|
public alloctype(T) Val
|
||||||
|
{
|
||||||
|
get mut
|
||||||
|
{
|
||||||
|
if (mVal != default)
|
||||||
|
return mVal;
|
||||||
|
var newVal = new T();
|
||||||
|
let prevValue = Interlocked.CompareExchange(ref mVal, default, newVal);
|
||||||
|
if (prevValue != default)
|
||||||
|
{
|
||||||
|
delete newVal;
|
||||||
|
return prevValue;
|
||||||
}
|
}
|
||||||
|
return newVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() mut
|
||||||
|
{
|
||||||
|
delete mVal;
|
||||||
|
mVal = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ namespace System.Diagnostics
|
||||||
{
|
{
|
||||||
if (!condition)
|
if (!condition)
|
||||||
{
|
{
|
||||||
|
if (Runtime.CheckErrorHandlers(scope Runtime.AssertError(.Debug, error, filePath, line)) == .Ignore)
|
||||||
|
return;
|
||||||
String failStr = scope .()..AppendF("Assert failed: {} at line {} in {}", error, line, filePath);
|
String failStr = scope .()..AppendF("Assert failed: {} at line {} in {}", error, line, filePath);
|
||||||
Internal.FatalError(failStr, 1);
|
Internal.FatalError(failStr, 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Collections;
|
||||||
#if BF_ENABLE_OBJECT_DEBUG_FLAGS || BF_DEBUG_ALLOC
|
#if BF_ENABLE_OBJECT_DEBUG_FLAGS || BF_DEBUG_ALLOC
|
||||||
#define BF_DBG_RUNTIME
|
#define BF_DBG_RUNTIME
|
||||||
#endif
|
#endif
|
||||||
|
@ -8,7 +9,7 @@ namespace System
|
||||||
[StaticInitPriority(101)]
|
[StaticInitPriority(101)]
|
||||||
static class Runtime
|
static class Runtime
|
||||||
{
|
{
|
||||||
const int32 cVersion = 8;
|
const int32 cVersion = 9;
|
||||||
|
|
||||||
[CRepr, AlwaysInclude]
|
[CRepr, AlwaysInclude]
|
||||||
struct BfDebugMessageData
|
struct BfDebugMessageData
|
||||||
|
@ -125,6 +126,7 @@ namespace System
|
||||||
function void (char8* str) mDebugMessageData_SetupProfilerCmd;
|
function void (char8* str) mDebugMessageData_SetupProfilerCmd;
|
||||||
function void () mDebugMessageData_Fatal;
|
function void () mDebugMessageData_Fatal;
|
||||||
function void () mDebugMessageData_Clear;
|
function void () mDebugMessageData_Clear;
|
||||||
|
function int32 (char8* kind, char8* arg1, char8* arg2, int arg3) mCheckErrorHandler;
|
||||||
|
|
||||||
static void* Alloc(int size)
|
static void* Alloc(int size)
|
||||||
{
|
{
|
||||||
|
@ -228,6 +230,22 @@ namespace System
|
||||||
BfDebugMessageData.gBfDebugMessageData.Clear();
|
BfDebugMessageData.gBfDebugMessageData.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32 CheckErrorHandler(char8* kind, char8* arg1, char8* arg2, int arg3)
|
||||||
|
{
|
||||||
|
Error error = null;
|
||||||
|
switch (StringView(kind))
|
||||||
|
{
|
||||||
|
case "FatalError":
|
||||||
|
error = scope:: FatalError() { mError = new .(arg1) };
|
||||||
|
case "LoadSharedLibrary":
|
||||||
|
error = scope:: LoadSharedLibraryError() { mPath = new .(arg1) };
|
||||||
|
case "GetSharedProcAddress":
|
||||||
|
error = scope:: GetSharedProcAddressError() { mPath = new .(arg1), mProcName = new .(arg2) };
|
||||||
|
}
|
||||||
|
if (error == null)
|
||||||
|
return 0;
|
||||||
|
return (int32)Runtime.CheckErrorHandlers(error);
|
||||||
|
}
|
||||||
|
|
||||||
public void Init() mut
|
public void Init() mut
|
||||||
{
|
{
|
||||||
|
@ -248,6 +266,7 @@ namespace System
|
||||||
mDebugMessageData_SetupProfilerCmd = => DebugMessageData_SetupProfilerCmd;
|
mDebugMessageData_SetupProfilerCmd = => DebugMessageData_SetupProfilerCmd;
|
||||||
mDebugMessageData_Fatal = => DebugMessageData_Fatal;
|
mDebugMessageData_Fatal = => DebugMessageData_Fatal;
|
||||||
mDebugMessageData_Clear = => DebugMessageData_Clear;
|
mDebugMessageData_Clear = => DebugMessageData_Clear;
|
||||||
|
mCheckErrorHandler = => CheckErrorHandler;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -276,7 +295,68 @@ namespace System
|
||||||
NoThreadExitWait = 0x10
|
NoThreadExitWait = 0x10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ErrorHandlerResult
|
||||||
|
{
|
||||||
|
ContinueFailure,
|
||||||
|
Ignore,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Error
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FatalError : Error
|
||||||
|
{
|
||||||
|
public String mError ~ delete _;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LoadSharedLibraryError : Error
|
||||||
|
{
|
||||||
|
public String mPath ~ delete _;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetSharedProcAddressError : Error
|
||||||
|
{
|
||||||
|
public String mPath ~ delete _;
|
||||||
|
public String mProcName ~ delete _;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AssertError : Error
|
||||||
|
{
|
||||||
|
public enum Kind
|
||||||
|
{
|
||||||
|
Debug,
|
||||||
|
Runtime,
|
||||||
|
Test
|
||||||
|
}
|
||||||
|
|
||||||
|
public Kind mKind;
|
||||||
|
public String mError ~ delete _;
|
||||||
|
public String mFilePath ~ delete _;
|
||||||
|
public int mLineNum;
|
||||||
|
|
||||||
|
public this(Kind kind, String error, String filePath, int lineNum)
|
||||||
|
{
|
||||||
|
mKind = kind;
|
||||||
|
mError = new .(error);
|
||||||
|
mFilePath = new .(filePath);
|
||||||
|
mLineNum = lineNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ErrorStage
|
||||||
|
{
|
||||||
|
PreFail,
|
||||||
|
Fail
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate ErrorHandlerResult ErrorHandler(ErrorStage stage, Error error);
|
||||||
|
|
||||||
static RtFlags sExtraFlags;
|
static RtFlags sExtraFlags;
|
||||||
|
static AllocWrapper<Monitor> sMonitor ~ _.Dispose();
|
||||||
|
static List<ErrorHandler> sErrorHandlers ~ DeleteContainerAndItems!(_);
|
||||||
|
static bool sInsideErrorHandler;
|
||||||
|
|
||||||
public static this()
|
public static this()
|
||||||
{
|
{
|
||||||
|
@ -317,9 +397,64 @@ namespace System
|
||||||
{
|
{
|
||||||
if (!condition)
|
if (!condition)
|
||||||
{
|
{
|
||||||
|
if (Runtime.CheckErrorHandlers(scope Runtime.AssertError(.Runtime, error, filePath, line)) == .Ignore)
|
||||||
|
return;
|
||||||
String failStr = scope .()..AppendF("Assert failed: {} at line {} in {}", error, line, filePath);
|
String failStr = scope .()..AppendF("Assert failed: {} at line {} in {}", error, line, filePath);
|
||||||
Internal.FatalError(failStr, 1);
|
Internal.FatalError(failStr, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddErrorHandler(ErrorHandler handler)
|
||||||
|
{
|
||||||
|
using (sMonitor.Val.Enter())
|
||||||
|
{
|
||||||
|
if (sErrorHandlers == null)
|
||||||
|
sErrorHandlers = new .();
|
||||||
|
sErrorHandlers.Add(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<void> RemoveErrorHandler(ErrorHandler handler)
|
||||||
|
{
|
||||||
|
using (sMonitor.Val.Enter())
|
||||||
|
{
|
||||||
|
if (sErrorHandlers.Remove(handler))
|
||||||
|
return .Ok;
|
||||||
|
}
|
||||||
|
return .Err;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ErrorHandlerResult CheckErrorHandlers(Error error)
|
||||||
|
{
|
||||||
|
using (sMonitor.Val.Enter())
|
||||||
|
{
|
||||||
|
if (sInsideErrorHandler)
|
||||||
|
return .ContinueFailure;
|
||||||
|
|
||||||
|
sInsideErrorHandler = true;
|
||||||
|
defer { sInsideErrorHandler = false; }
|
||||||
|
|
||||||
|
for (int pass = 0; pass < 2; pass++)
|
||||||
|
{
|
||||||
|
int idx = (sErrorHandlers?.Count).GetValueOrDefault() - 1;
|
||||||
|
while (idx >= 0)
|
||||||
|
{
|
||||||
|
if (idx < sErrorHandlers.Count)
|
||||||
|
{
|
||||||
|
var handler = sErrorHandlers[idx];
|
||||||
|
var result = handler((pass == 0) ? .PreFail : .Fail, error);
|
||||||
|
if (result == .Ignore)
|
||||||
|
{
|
||||||
|
if (pass == 1)
|
||||||
|
Internal.FatalError("Can only ignore error on prefail");
|
||||||
|
return .Ignore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .ContinueFailure;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,8 @@ namespace System
|
||||||
{
|
{
|
||||||
if (!condition)
|
if (!condition)
|
||||||
{
|
{
|
||||||
|
if (Runtime.CheckErrorHandlers(scope Runtime.AssertError(.Test, error, filePath, line)) == .Ignore)
|
||||||
|
return;
|
||||||
String failStr = scope .()..AppendF("Assert failed: {} at line {} in {}", error, line, filePath);
|
String failStr = scope .()..AppendF("Assert failed: {} at line {} in {}", error, line, filePath);
|
||||||
Internal.[Friend]Test_Error(failStr);
|
Internal.[Friend]Test_Error(failStr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "BeefySysLib/Common.h"
|
#include "BeefySysLib/Common.h"
|
||||||
#include "BeefySysLib/util/String.h"
|
#include "BeefySysLib/util/String.h"
|
||||||
|
|
||||||
#define BFRT_VERSION 8
|
#define BFRT_VERSION 9
|
||||||
|
|
||||||
#ifdef BFRT_DYNAMIC
|
#ifdef BFRT_DYNAMIC
|
||||||
#define BFRT_EXPORT __declspec(dllexport)
|
#define BFRT_EXPORT __declspec(dllexport)
|
||||||
|
@ -107,6 +107,7 @@ namespace bf
|
||||||
void(*DebugMessageData_SetupProfilerCmd)(const char* str);
|
void(*DebugMessageData_SetupProfilerCmd)(const char* str);
|
||||||
void(*DebugMessageData_Fatal)();
|
void(*DebugMessageData_Fatal)();
|
||||||
void(*DebugMessageData_Clear)();
|
void(*DebugMessageData_Clear)();
|
||||||
|
int(*CheckErrorHandler)(const char* kind, const char* arg1, const char* arg2, intptr arg3);
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -214,6 +214,9 @@ static void TestReadCmd(Beefy::String& str);
|
||||||
|
|
||||||
static void Internal_FatalError(const char* error)
|
static void Internal_FatalError(const char* error)
|
||||||
{
|
{
|
||||||
|
if (gBfRtCallbacks.CheckErrorHandler != NULL)
|
||||||
|
gBfRtCallbacks.CheckErrorHandler("FatalError", error, NULL, 0);
|
||||||
|
|
||||||
if ((gClientPipe != NULL) && (!gTestBreakOnFailure))
|
if ((gClientPipe != NULL) && (!gTestBreakOnFailure))
|
||||||
{
|
{
|
||||||
Beefy::String str = ":TestFatal\t";
|
Beefy::String str = ":TestFatal\t";
|
||||||
|
@ -532,6 +535,12 @@ void* Internal::LoadSharedLibrary(char* libName)
|
||||||
void* libHandle = BfpDynLib_Load(libName);
|
void* libHandle = BfpDynLib_Load(libName);
|
||||||
if (libHandle == NULL)
|
if (libHandle == NULL)
|
||||||
{
|
{
|
||||||
|
if (gBfRtCallbacks.CheckErrorHandler != NULL)
|
||||||
|
{
|
||||||
|
if (gBfRtCallbacks.CheckErrorHandler("LoadSharedLibrary", libName, NULL, 0) == 1)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Beefy::String errorStr = StrFormat("Failed to load shared library: %s", libName);
|
Beefy::String errorStr = StrFormat("Failed to load shared library: %s", libName);
|
||||||
SETUP_ERROR(errorStr.c_str(), 1);
|
SETUP_ERROR(errorStr.c_str(), 1);
|
||||||
BF_DEBUG_BREAK();
|
BF_DEBUG_BREAK();
|
||||||
|
@ -558,6 +567,12 @@ void* Internal::GetSharedProcAddress(void* libHandle, char* procName)
|
||||||
int libFileNameLen = 4096;
|
int libFileNameLen = 4096;
|
||||||
BfpDynLib_GetFilePath((BfpDynLib*)libHandle, libFileName, &libFileNameLen, NULL);
|
BfpDynLib_GetFilePath((BfpDynLib*)libHandle, libFileName, &libFileNameLen, NULL);
|
||||||
|
|
||||||
|
if (gBfRtCallbacks.CheckErrorHandler != NULL)
|
||||||
|
{
|
||||||
|
if (gBfRtCallbacks.CheckErrorHandler("GetSharedProcAddress", libFileName, procName, 0) == 1)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Beefy::String errorStr = StrFormat("Failed to load shared procedure '%s' from '%s'", procName, libFileName);
|
Beefy::String errorStr = StrFormat("Failed to load shared procedure '%s' from '%s'", procName, libFileName);
|
||||||
SETUP_ERROR(errorStr.c_str(), 1);
|
SETUP_ERROR(errorStr.c_str(), 1);
|
||||||
BF_DEBUG_BREAK();
|
BF_DEBUG_BREAK();
|
||||||
|
|
|
@ -373,7 +373,7 @@ bool BfConstResolver::PrepareMethodArguments(BfAstNode* targetSrc, BfMethodMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mModule->PreFail())
|
if (mModule->PreFail())
|
||||||
mModule->Fail(StrFormat("Not enough parameters specified. Expected %d fewer.", methodInstance->GetParamCount() - (int)arguments.size()), refNode);
|
mModule->Fail(StrFormat("Not enough parameters specified. Expected %d more.", methodInstance->GetParamCount() - (int)arguments.size()), refNode);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19107,7 +19107,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup,
|
||||||
{
|
{
|
||||||
BfIRValue dllImportGlobalVar = CreateDllImportGlobalVar(methodInstance, true);
|
BfIRValue dllImportGlobalVar = CreateDllImportGlobalVar(methodInstance, true);
|
||||||
methodInstance->mIRFunction = mBfIRBuilder->GetFakeVal();
|
methodInstance->mIRFunction = mBfIRBuilder->GetFakeVal();
|
||||||
BF_ASSERT(dllImportGlobalVar);
|
BF_ASSERT(dllImportGlobalVar || methodInstance->mHasFailed);
|
||||||
mFuncReferences[mCurMethodInstance] = dllImportGlobalVar;
|
mFuncReferences[mCurMethodInstance] = dllImportGlobalVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue