mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 03:28:20 +02:00

Added PICLevel, RelocKind DarwinCommon/LinuxCommon/AndroidCommon merged into PosixCommon Mangling changed to avoid '@'
916 lines
21 KiB
C++
916 lines
21 KiB
C++
#pragma warning(disable:4996)
|
|
#define HEAPHOOK
|
|
|
|
|
|
//#define USE_CHARCONV
|
|
|
|
#include <stdio.h>
|
|
|
|
//#include <crtdefs.h>
|
|
//#include <malloc.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
//#include <intrin.h>
|
|
|
|
#ifdef USE_CHARCONV
|
|
#include <charconv>
|
|
#endif
|
|
|
|
//#define OBJECT_GUARD_END_SIZE 8
|
|
#define OBJECT_GUARD_END_SIZE 0
|
|
|
|
//#define BF_USE_STOMP_ALLOC 1
|
|
|
|
//extern "C"
|
|
//{
|
|
//#include "gperftools/stacktrace.h"
|
|
//}
|
|
|
|
#ifdef _MSC_VER
|
|
#include <intrin.h>
|
|
#pragma intrinsic(_ReturnAddress)
|
|
#define BF_RETURN_ADDRESS _ReturnAddress()
|
|
#else
|
|
#define BF_RETURN_ADDRESS __builtin_return_address(0)
|
|
#endif
|
|
|
|
#include "BeefySysLib/Common.h"
|
|
#include "BfObjects.h"
|
|
//#include "gc.h"
|
|
#include "StompAlloc.h"
|
|
#include "BeefySysLib/platform/PlatformHelper.h"
|
|
#include "ffi.h"
|
|
#include "Thread.h"
|
|
|
|
#ifdef BF_PLATFORM_WINDOWS
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#endif
|
|
|
|
USING_NS_BF;
|
|
|
|
static Beefy::StringT<0> gCmdLineString;
|
|
bf::System::Runtime::BfRtCallbacks gBfRtCallbacks;
|
|
BfRtFlags gBfRtFlags = (BfRtFlags)0;
|
|
|
|
namespace bf
|
|
{
|
|
namespace System
|
|
{
|
|
class Object;
|
|
class Exception;
|
|
|
|
//System::Threading::Thread* gMainThread;
|
|
|
|
class Internal
|
|
{
|
|
private:
|
|
BFRT_EXPORT static void __BfStaticCtor();
|
|
BFRT_EXPORT static void __BfStaticDtor();
|
|
|
|
BFRT_EXPORT static void BfStaticCtor();
|
|
BFRT_EXPORT static void BfStaticDtor();
|
|
BFRT_EXPORT static void Shutdown();
|
|
public:
|
|
BFRT_EXPORT static Object* UnsafeCastToObject(void* inPtr);
|
|
BFRT_EXPORT static void* UnsafeCastToPtr(Object* obj);
|
|
BFRT_EXPORT static void ObjectDynCheck(Object* object, int typeId, bool allowNull);
|
|
BFRT_EXPORT static void ObjectDynCheckFailed(Object* object, int typeId);
|
|
BFRT_EXPORT static void Throw(Exception* ex);
|
|
BFRT_EXPORT static void ThrowIndexOutOfRange(intptr stackOffset);
|
|
BFRT_EXPORT static void FatalError(String* error, intptr stackOffset = 0);
|
|
BFRT_EXPORT static void MemCpy(void* dest, void* src, intptr length);
|
|
BFRT_EXPORT static void MemMove(void* dest, void* src, intptr length);
|
|
BFRT_EXPORT static void MemSet(void* addr, uint8 val, intptr length);
|
|
BFRT_EXPORT static int CStrLen(char* charPtr);
|
|
BFRT_EXPORT static void* Malloc(intptr length);
|
|
BFRT_EXPORT static void Free(void* ptr);
|
|
BFRT_EXPORT static void* VirtualAlloc(intptr size, bool canExecute, bool canWrite);
|
|
BFRT_EXPORT static int64 GetTickCountMicro();
|
|
BFRT_EXPORT static void BfDelegateTargetCheck(void* target);
|
|
BFRT_EXPORT static void* LoadSharedLibrary(char* filePath);
|
|
BFRT_EXPORT static void LoadSharedLibraryInto(char* filePath, void** libDest);
|
|
BFRT_EXPORT static void* GetSharedProcAddress(void* libHandle, char* procName);
|
|
BFRT_EXPORT static void GetSharedProcAddressInto(void* libHandle, char* procName, void** procDest);
|
|
BFRT_EXPORT static char* GetCommandLineArgs();
|
|
BFRT_EXPORT static void BfLog(char* str);
|
|
BFRT_EXPORT static void ProfilerCmd(char* str);
|
|
BFRT_EXPORT static void ReportMemory();
|
|
|
|
private:
|
|
BFRT_EXPORT static void Test_Init(char* testData);
|
|
BFRT_EXPORT static int32 Test_Query();
|
|
BFRT_EXPORT static void Test_Finish();
|
|
};
|
|
|
|
namespace IO
|
|
{
|
|
class File
|
|
{
|
|
private:
|
|
BFRT_EXPORT static bool Exists(char* fileName);
|
|
};
|
|
|
|
class Directory
|
|
{
|
|
private:
|
|
BFRT_EXPORT static bool Exists(char* fileName);
|
|
};
|
|
}
|
|
|
|
namespace Diagnostics
|
|
{
|
|
namespace Contracts
|
|
{
|
|
class Contract
|
|
{
|
|
public:
|
|
enum ContractFailureKind : uint8
|
|
{
|
|
ContractFailureKind_Precondition,
|
|
//[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
|
|
ContractFailureKind_Postcondition,
|
|
//[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
|
|
ContractFailureKind_PostconditionOnException,
|
|
ContractFailureKind_Invariant,
|
|
ContractFailureKind_Assert,
|
|
ContractFailureKind_Assume,
|
|
};
|
|
|
|
private:
|
|
BFRT_EXPORT static void ReportFailure(ContractFailureKind failureKind, char* userMessage, int userMessageLen, char* conditionText, int conditionTextLen);
|
|
};
|
|
}
|
|
|
|
class Debug
|
|
{
|
|
private:
|
|
BFRT_EXPORT static void Write(char* str, intptr strLen);
|
|
};
|
|
}
|
|
|
|
namespace FFI
|
|
{
|
|
enum FFIABI : int32;
|
|
enum FFIResult : int32;
|
|
|
|
struct FFIType;
|
|
|
|
struct FFILIB
|
|
{
|
|
struct FFICIF;
|
|
|
|
BFRT_EXPORT static void* ClosureAlloc(intptr size, void** outFunc);
|
|
BFRT_EXPORT static FFIResult PrepCif(FFICIF* cif, FFIABI abi, int32 nargs, FFIType* rtype, FFIType** argTypes);
|
|
BFRT_EXPORT static void Call(FFICIF* cif, void* funcPtr, void* rvalue, void** args);
|
|
};
|
|
}
|
|
|
|
struct Float
|
|
{
|
|
private:
|
|
BFRT_EXPORT static int ToString(float f, char* outStr);
|
|
};
|
|
|
|
struct Double
|
|
{
|
|
private:
|
|
BFRT_EXPORT static int ToString(double f, char* outStr);
|
|
};
|
|
}
|
|
}
|
|
|
|
//#define BF_TRACK_SIZES 1
|
|
|
|
#if BF_TRACK_SIZES
|
|
static int sAllocSizes[1024*1024];
|
|
static int sHighestId = 0;
|
|
#endif
|
|
|
|
using namespace bf::System;
|
|
|
|
#ifndef BF_PLATFORM_WINDOWS
|
|
|
|
bool IsDebuggerPresent()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
extern "C" BFRT_EXPORT int BF_CALLTYPE ftoa(float val, char* str)
|
|
{
|
|
return sprintf(str, "%1.9f", val);
|
|
}
|
|
|
|
/*static void* MallocHook(size_t size, const void *caller)
|
|
{
|
|
printf("MallocHook\n");
|
|
return NULL;
|
|
}*/
|
|
|
|
|
|
/*static int __cdecl HeapHook(int a, size_t b, void* c, void** d)
|
|
{
|
|
printf("Heap Hook\n");
|
|
return 0;
|
|
}*/
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//static Beefy::StringT<0> gErrorString;
|
|
|
|
static const char* volatile gErrorString = NULL;
|
|
|
|
void SetErrorString(const char* str)
|
|
{
|
|
char* newStr = strdup(str);
|
|
while (true)
|
|
{
|
|
const char* prevStr = gErrorString;
|
|
auto result = (void*)BfpSystem_InterlockedCompareExchangePtr((uintptr*)&gErrorString, (uintptr)prevStr, (uintptr)newStr);
|
|
if (result != prevStr)
|
|
continue;
|
|
if (prevStr != NULL)
|
|
free((void*)prevStr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define SETUP_ERROR(str, skip) SetErrorString(str); gBfRtCallbacks.DebugMessageData_SetupError(str, skip)
|
|
|
|
static void GetCrashInfo()
|
|
{
|
|
auto errorString = gErrorString;
|
|
if (errorString != NULL)
|
|
{
|
|
Beefy::String debugStr;
|
|
debugStr += "Beef Error: ";
|
|
debugStr += (const char*)errorString;
|
|
BfpSystem_AddCrashInfo(debugStr.c_str());
|
|
}
|
|
}
|
|
|
|
void bf::System::Runtime::Init(int version, int flags, BfRtCallbacks* callbacks)
|
|
{
|
|
BfpSystemInitFlags sysInitFlags = BfpSystemInitFlag_InstallCrashCatcher;
|
|
if ((flags & 4) != 0)
|
|
sysInitFlags = (BfpSystemInitFlags)(sysInitFlags | BfpSystemInitFlag_SilentCrash);
|
|
BfpSystem_Init(BFP_VERSION, sysInitFlags);
|
|
BfpSystem_AddCrashInfoFunc(GetCrashInfo);
|
|
|
|
if (gBfRtCallbacks.Alloc != NULL)
|
|
{
|
|
BfpSystem_FatalError(StrFormat("BeefRT already initialized. Multiple executable modules in the same process cannot dynamically link to the Beef runtime.").c_str(), "BEEF FATAL ERROR");
|
|
}
|
|
|
|
if (version != BFRT_VERSION)
|
|
{
|
|
BfpSystem_FatalError(StrFormat("BeefRT build version '%d' does not match requested version '%d'", BFRT_VERSION, version).c_str(), "BEEF FATAL ERROR");
|
|
}
|
|
|
|
gBfRtCallbacks = *callbacks;
|
|
gBfRtFlags = (BfRtFlags)flags;
|
|
|
|
Beefy::String cmdLine;
|
|
|
|
BfpSystemResult result;
|
|
BFP_GETSTR_HELPER(cmdLine, result, BfpSystem_GetCommandLine(__STR, __STRLEN, &result));
|
|
|
|
char* cmdLineStr = (char*)cmdLine.c_str();
|
|
|
|
//::MessageBoxA(NULL, cmdLineStr, "BFRT", 0);
|
|
|
|
char* useCmdLineStr = cmdLineStr;
|
|
|
|
if (cmdLineStr[0] != 0)
|
|
{
|
|
bool nameQuoted = cmdLineStr[0] == '\"';
|
|
|
|
Beefy::String passedName;
|
|
int i;
|
|
for (i = (nameQuoted ? 1 : 0); cmdLineStr[i] != 0; i++)
|
|
{
|
|
wchar_t c = cmdLineStr[i];
|
|
|
|
if (((nameQuoted) && (c == '"')) ||
|
|
((!nameQuoted) && (c == ' ')))
|
|
{
|
|
i++;
|
|
break;
|
|
}
|
|
passedName += cmdLineStr[i];
|
|
}
|
|
|
|
useCmdLineStr += i;
|
|
while (*useCmdLineStr == L' ')
|
|
useCmdLineStr++;
|
|
}
|
|
gCmdLineString = useCmdLineStr;
|
|
}
|
|
|
|
void bf::System::Runtime::SetErrorString(char* errorStr)
|
|
{
|
|
::SetErrorString(errorStr);
|
|
}
|
|
|
|
void bf::System::Runtime::AddCrashInfoFunc(void* func)
|
|
{
|
|
BfpSystem_AddCrashInfoFunc(*(BfpCrashInfoFunc*)&func);
|
|
}
|
|
|
|
void bf::System::Runtime::SetCrashReportKind(bf::System::Runtime::RtCrashReportKind crashReportKind)
|
|
{
|
|
BfpSystem_SetCrashReportKind((BfpCrashReportKind)crashReportKind);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void Internal::Shutdown()
|
|
{
|
|
BfInternalThread::WaitForAllDone();
|
|
if (gBfRtCallbacks.GC_Shutdown != NULL)
|
|
gBfRtCallbacks.GC_Shutdown();
|
|
BfpSystem_Shutdown();
|
|
}
|
|
|
|
void Internal::BfStaticCtor()
|
|
{
|
|
__BfStaticCtor();
|
|
}
|
|
|
|
void Internal::__BfStaticCtor()
|
|
{
|
|
|
|
}
|
|
|
|
void Internal::BfStaticDtor()
|
|
{
|
|
|
|
}
|
|
|
|
void Internal::__BfStaticDtor()
|
|
{
|
|
|
|
}
|
|
|
|
Object* Internal::UnsafeCastToObject(void* inPtr)
|
|
{
|
|
return (Object*)inPtr;
|
|
}
|
|
|
|
void* Internal::UnsafeCastToPtr(Object* obj)
|
|
{
|
|
return (void*)obj;
|
|
}
|
|
|
|
void Internal::Throw(Exception* ex)
|
|
{
|
|
bf::System::String* exStr = gBfRtCallbacks.String_Alloc();
|
|
gBfRtCallbacks.Object_ToString(ex, exStr);
|
|
|
|
Beefy::String errorStr = StrFormat("FATAL: %s", exStr->CStr());
|
|
SETUP_ERROR(errorStr.c_str(), 1);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
|
|
printf("Thrown: %s", errorStr.c_str());
|
|
//TODO: What about capturing callstack?
|
|
|
|
exit(3);
|
|
|
|
//throw ex;
|
|
}
|
|
|
|
void Internal::ThrowIndexOutOfRange(intptr stackOffset)
|
|
{
|
|
if ((stackOffset != -1) && (::IsDebuggerPresent()))
|
|
{
|
|
SETUP_ERROR("Index out of range", (int)(2 + stackOffset));
|
|
BF_DEBUG_BREAK();
|
|
}
|
|
|
|
BfpSystem_FatalError("Index out of range", "FATAL ERROR");
|
|
}
|
|
|
|
void Internal::FatalError(bf::System::String* error, intptr stackOffset)
|
|
{
|
|
if ((stackOffset != -1) && (::IsDebuggerPresent()))
|
|
{
|
|
SETUP_ERROR(error->CStr(), (int)(2 + stackOffset));
|
|
BF_DEBUG_BREAK();
|
|
}
|
|
|
|
BfpSystem_FatalError(error->CStr(), "FATAL ERROR");
|
|
}
|
|
|
|
void Internal::MemCpy(void* dest, void* src, intptr length)
|
|
{
|
|
memcpy(dest, src, length);
|
|
}
|
|
|
|
void Internal::MemMove(void* dest, void* src, intptr length)
|
|
{
|
|
memmove(dest, src, length);
|
|
}
|
|
|
|
void Internal::MemSet(void* addr, uint8 val, intptr length)
|
|
{
|
|
memset(addr, val, length);
|
|
}
|
|
|
|
int Internal::CStrLen(char* charPtr)
|
|
{
|
|
return (int)strlen(charPtr);
|
|
}
|
|
|
|
void* Internal::Malloc(intptr length)
|
|
{
|
|
#if BF_USE_STOMP_ALLOC
|
|
return StompAlloc(length);
|
|
#elif BF_TRACK_SIZES
|
|
uint8* allocPtr = (uint8*)malloc(length + 16);
|
|
*((int*)allocPtr) = length;
|
|
sAllocSizes[0] += length;
|
|
return allocPtr + 16;
|
|
#else
|
|
return malloc(length);
|
|
#endif
|
|
}
|
|
|
|
void* Internal::VirtualAlloc(intptr size, bool canExecute, bool canWrite)
|
|
{
|
|
#ifdef BF_PLATFORM_WINDOWS
|
|
OutputDebugStrF("Performing VirtualAlloc: %d %d %d\n", size, canExecute, canWrite);
|
|
int prot = PAGE_READWRITE;
|
|
if (canExecute && canWrite)
|
|
prot = PAGE_EXECUTE_READWRITE;
|
|
else if (canExecute)
|
|
prot = PAGE_EXECUTE_READ;
|
|
void* ptr = ::VirtualAlloc(NULL, size, MEM_RESERVE, prot);
|
|
return ptr;
|
|
#else
|
|
BF_FATAL("Not supported");
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
void Internal::Free(void* ptr)
|
|
{
|
|
#if BF_USE_STOMP_ALLOC
|
|
StompFree(ptr);
|
|
#elif BF_TRACK_SIZES
|
|
uint8* allocPtr = ((uint8*)ptr) - 16;
|
|
sAllocSizes[0] -= *((int*)allocPtr);
|
|
free(allocPtr);
|
|
#else
|
|
free(ptr);
|
|
#endif
|
|
}
|
|
|
|
BFRT_EXPORT int64 Internal::GetTickCountMicro()
|
|
{
|
|
return BFGetTickCountMicro();
|
|
}
|
|
|
|
void Internal::BfDelegateTargetCheck(void* target)
|
|
{
|
|
if (target != NULL)
|
|
{
|
|
SETUP_ERROR("Attempting pass non-static method reference to extern method", 2);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
}
|
|
}
|
|
|
|
void* Internal::LoadSharedLibrary(char* libName)
|
|
{
|
|
//::MessageBox(NULL, "Hey", "Dude", 0);
|
|
void* libHandle = BfpDynLib_Load(libName);
|
|
if (libHandle == NULL)
|
|
{
|
|
Beefy::String errorStr = StrFormat("Failed to load shared library: %s", libName);
|
|
SETUP_ERROR(errorStr.c_str(), 1);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
}
|
|
return libHandle;
|
|
}
|
|
|
|
void Internal::LoadSharedLibraryInto(char* libName, void** libDest)
|
|
{
|
|
if (*libDest == NULL)
|
|
*libDest = LoadSharedLibrary(libName);
|
|
}
|
|
|
|
void* Internal::GetSharedProcAddress(void* libHandle, char* procName)
|
|
{
|
|
if (libHandle == NULL)
|
|
return NULL;
|
|
|
|
void* procAddr = BfpDynLib_GetProcAddress((BfpDynLib*)libHandle, procName);
|
|
if (procAddr == NULL)
|
|
{
|
|
char libFileName[4096];
|
|
int libFileNameLen = 4096;
|
|
BfpDynLib_GetFilePath((BfpDynLib*)libHandle, libFileName, &libFileNameLen, NULL);
|
|
|
|
Beefy::String errorStr = StrFormat("Failed to load shared procedure '%s' from '%s'", procName, libFileName);
|
|
SETUP_ERROR(errorStr.c_str(), 1);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
}
|
|
return procAddr;
|
|
}
|
|
|
|
void Internal::GetSharedProcAddressInto(void* libHandle, char* procName, void** procDest)
|
|
{
|
|
*procDest = GetSharedProcAddress(libHandle, procName);
|
|
}
|
|
|
|
char* Internal::GetCommandLineArgs()
|
|
{
|
|
return (char*)gCmdLineString.c_str();
|
|
}
|
|
|
|
void Internal::BfLog(char* str)
|
|
{
|
|
// static int lineNum = 0;
|
|
// lineNum++;
|
|
//
|
|
// static FILE* fp = fopen("dbg_internal.txt", "wb");
|
|
//
|
|
// Beefy::String aResult = StrFormat("%d ", lineNum) + str;
|
|
// fwrite(aResult.c_str(), 1, aResult.length(), fp);
|
|
// fflush(fp);
|
|
}
|
|
|
|
void Internal::ProfilerCmd(char* str)
|
|
{
|
|
if (!::IsDebuggerPresent())
|
|
return;
|
|
|
|
gBfRtCallbacks.DebugMessageData_SetupProfilerCmd(str);
|
|
BF_DEBUG_BREAK();
|
|
}
|
|
|
|
void Internal::ReportMemory()
|
|
{
|
|
int totalMem = 0;
|
|
#if BF_TRACK_SIZES
|
|
for (int i = 0; i <= sHighestId; i++)
|
|
totalMem += sAllocSizes[i];
|
|
OutputDebugStrF("Beef Object Memory: %dk\n", totalMem / 1024);
|
|
#endif
|
|
}
|
|
|
|
static int gTestMethodIdx = -1;
|
|
static uint32 gTestStartTick = 0;
|
|
|
|
static BfpFile* gClientPipe = NULL;
|
|
static Beefy::String gTestInBuffer;
|
|
|
|
static void TestString(const StringImpl& str)
|
|
{
|
|
BfpFileResult fileResult;
|
|
BfpFile_Write(gClientPipe, str.c_str(), str.length(), -1, &fileResult);
|
|
BF_ASSERT_REL(fileResult == BfpFileResult_Ok);
|
|
}
|
|
|
|
static void TestReadCmd(Beefy::String& str)
|
|
{
|
|
while (true)
|
|
{
|
|
int crPos = (int)gTestInBuffer.IndexOf('\n');
|
|
if (crPos != -1)
|
|
{
|
|
str = gTestInBuffer.Substring(0, crPos);
|
|
gTestInBuffer.Remove(0, crPos + 1);
|
|
return;
|
|
}
|
|
|
|
char data[1024];
|
|
BfpFileResult fileResult;
|
|
int readSize = (int)BfpFile_Read(gClientPipe, data, 1024, -1, &fileResult);
|
|
if ((fileResult == BfpFileResult_Ok) || (fileResult == BfpFileResult_PartialData))
|
|
{
|
|
gTestInBuffer.Append(data, readSize);
|
|
}
|
|
else
|
|
{
|
|
BF_FATAL("Failed to read pipe to test manager");
|
|
}
|
|
}
|
|
}
|
|
|
|
void Internal::Test_Init(char* testData)
|
|
{
|
|
BfpSystem_SetCrashReportKind(BfpCrashReportKind_None);
|
|
|
|
Beefy::String args = GetCommandLineArgs();
|
|
|
|
BfpFileResult fileResult;
|
|
gClientPipe = BfpFile_Create(args.c_str(), BfpFileCreateKind_OpenExisting, (BfpFileCreateFlags)(BfpFileCreateFlag_Read | BfpFileCreateFlag_Write | BfpFileCreateFlag_Pipe), BfpFileAttribute_None, &fileResult);
|
|
if (fileResult != BfpFileResult_Ok)
|
|
BF_FATAL("Test_Init failed to create pipe to test manager");
|
|
|
|
Beefy::String outStr;
|
|
outStr += ":TestInit\n";
|
|
outStr += testData;
|
|
outStr += "\n";
|
|
outStr += ":TestBegin\n";
|
|
TestString(outStr);
|
|
}
|
|
|
|
int32 Internal::Test_Query()
|
|
{
|
|
if (gTestMethodIdx != -1)
|
|
{
|
|
uint32 tickEnd = BfpSystem_TickCount();
|
|
TestString(StrFormat(":TestResult\t%d\n", tickEnd - gTestStartTick));
|
|
}
|
|
|
|
TestString(":TestQuery\n");
|
|
|
|
Beefy::String result;
|
|
TestReadCmd(result);
|
|
|
|
Beefy::String param;
|
|
int tabPos = (int)result.IndexOf('\t');
|
|
if (tabPos != -1)
|
|
{
|
|
param = result.Substring(tabPos + 1);
|
|
result.RemoveToEnd(tabPos);
|
|
}
|
|
|
|
if (result == ":TestRun")
|
|
{
|
|
gTestStartTick = BfpSystem_TickCount();
|
|
gTestMethodIdx = atoi(param.c_str());
|
|
return gTestMethodIdx;
|
|
}
|
|
else if (result == ":TestFinish")
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
printf("Command Str: %s\n", result.c_str());
|
|
BF_FATAL("Invalid test command string from test manager");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Internal::Test_Finish()
|
|
{
|
|
TestString(":TestFinish\n");
|
|
|
|
if (gClientPipe != NULL)
|
|
{
|
|
BfpFile_Release(gClientPipe);
|
|
gClientPipe = NULL;
|
|
}
|
|
}
|
|
|
|
///
|
|
|
|
int GetStackTrace(void **result, int max_depth, int skip_count);
|
|
|
|
void BfLog(const char* fmt ...);
|
|
|
|
static const int cMaxStackTraceCount = 1024;
|
|
struct PendingAllocState
|
|
{
|
|
bool mHasData;
|
|
void* mStackTrace[cMaxStackTraceCount];
|
|
int mStackTraceCount;
|
|
int mMetadataBytes;
|
|
|
|
bool IsSmall(intptr curAllocBytes)
|
|
{
|
|
if ((mStackTraceCount > 255) || (mMetadataBytes > 255))
|
|
return false;
|
|
|
|
const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1;
|
|
if (curAllocBytes <= maxSmallObjectSize)
|
|
return true;
|
|
|
|
intptr objBytes = curAllocBytes - mStackTraceCount*sizeof(intptr) - mMetadataBytes;
|
|
return (objBytes < maxSmallObjectSize);
|
|
}
|
|
};
|
|
|
|
void Internal::ObjectDynCheck(bf::System::Object* object, int typeId, bool allowNull)
|
|
{
|
|
return;
|
|
if (object == NULL)
|
|
{
|
|
if (allowNull)
|
|
return;
|
|
SETUP_ERROR("Attempting unboxing on null object", 1);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
return;
|
|
}
|
|
|
|
auto result = gBfRtCallbacks.Object_DynamicCastToTypeId(object, typeId);
|
|
if (result == NULL)
|
|
{
|
|
Beefy::String errorStr = "Attempting invalid cast on object";
|
|
//errorStr += StrFormat("\x1LEAK\t0x%@\n (%s)0x%@\n", object, object->GetTypeName().c_str(), object);
|
|
errorStr += StrFormat("\x1LEAK\t0x%@\n (%s)0x%@\n", object, "System.Object", object);
|
|
SETUP_ERROR(errorStr.c_str(), 2);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
}
|
|
}
|
|
|
|
void Internal::ObjectDynCheckFailed(bf::System::Object* object, int typeId)
|
|
{
|
|
if (object == NULL)
|
|
{
|
|
SETUP_ERROR("Attempting unboxing on null object", 1);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
return;
|
|
}
|
|
|
|
Beefy::String errorStr = "Attempting invalid cast on object";
|
|
|
|
errorStr += StrFormat("\x1LEAK\t0x%@\n (%s)0x%@\n", object, "System.Object", object);
|
|
SETUP_ERROR(errorStr.c_str(), 2);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
}
|
|
|
|
extern "C" BFRT_EXPORT int PrintF(const char* fmt, ...)
|
|
{
|
|
int ret;
|
|
/* Declare a va_list type variable */
|
|
va_list myargs;
|
|
/* Initialise the va_list variable with the ... after fmt */
|
|
va_start(myargs, fmt);
|
|
/* Forward the '...' to vprintf */
|
|
ret = vprintf(fmt, myargs);
|
|
/* Clean up the va_list */
|
|
va_end(myargs);
|
|
|
|
return ret;
|
|
}
|
|
|
|
///
|
|
|
|
using namespace bf::System::Diagnostics::Contracts;
|
|
|
|
void Contract::ReportFailure(Contract::ContractFailureKind failureKind, char* userMessage, int userMessageLen, char* conditionText, int conditionTextLen)
|
|
{
|
|
Beefy::String userMessageStr;
|
|
if (userMessageLen > 0)
|
|
userMessageStr.Reference(userMessage, userMessageLen);
|
|
Beefy::String conditionTextStr;
|
|
if (conditionTextLen > 0)
|
|
conditionTextStr.Reference(conditionText, conditionTextLen);
|
|
|
|
Beefy::String errorMsg = "Contract";
|
|
if (failureKind == Contract::ContractFailureKind_Assert)
|
|
errorMsg += ": Assert failed";
|
|
if (userMessage != NULL)
|
|
errorMsg += Beefy::String(": ") + userMessageStr;
|
|
if (conditionText != NULL)
|
|
errorMsg += Beefy::String(": ") + conditionTextStr;
|
|
|
|
if (::IsDebuggerPresent())
|
|
{
|
|
SETUP_ERROR(errorMsg.c_str(), 3);
|
|
BF_DEBUG_BREAK();
|
|
gBfRtCallbacks.DebugMessageData_Fatal();
|
|
}
|
|
|
|
BfpSystem_FatalError(errorMsg.c_str(), "CONTRACT ERROR");
|
|
|
|
return;
|
|
}
|
|
|
|
void bf::System::Diagnostics::Debug::Write(char* str, intptr strLen)
|
|
{
|
|
Beefy::String strVal(str, strLen);
|
|
OutputDebugStr(strVal);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool IO::File::Exists(char* fileName)
|
|
{
|
|
return BfpFile_Exists(fileName);
|
|
}
|
|
|
|
bool IO::Directory::Exists(char* fileName)
|
|
{
|
|
return BfpDirectory_Exists(fileName);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void* bf::System::FFI::FFILIB::ClosureAlloc(intptr size, void** outFunc)
|
|
{
|
|
return ffi_closure_alloc(size, outFunc);
|
|
}
|
|
|
|
bf::System::FFI::FFIResult bf::System::FFI::FFILIB::PrepCif(bf::System::FFI::FFILIB::FFICIF* cif, bf::System::FFI::FFIABI abi, int32 nargs, bf::System::FFI::FFIType* rtype, bf::System::FFI::FFIType** argTypes)
|
|
{
|
|
return (bf::System::FFI::FFIResult)ffi_prep_cif((ffi_cif*)cif, (ffi_abi)abi, nargs, (ffi_type*)rtype, (ffi_type**)argTypes);
|
|
}
|
|
|
|
void bf::System::FFI::FFILIB::Call(bf::System::FFI::FFILIB::FFICIF* cif, void* funcPtr, void* rvalue, void** args)
|
|
{
|
|
ffi_call((ffi_cif*)cif, (void(*)())funcPtr, rvalue, args);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
static int ToString(float d, char* outStr)
|
|
{
|
|
sprintf(outStr, "%1.9g", d);
|
|
int len = (int)strlen(outStr);
|
|
for (int i = 0; outStr[i] != 0; i++)
|
|
{
|
|
if (outStr[i] == '.')
|
|
{
|
|
int checkC = len - 1;
|
|
while (true)
|
|
{
|
|
char c = outStr[checkC];
|
|
if (c == '.')
|
|
{
|
|
return checkC;
|
|
}
|
|
else if (c != '0')
|
|
{
|
|
for (int j = i + 1; j <= checkC; j++)
|
|
if (outStr[j] == 'e')
|
|
return len;
|
|
return checkC + 1;
|
|
}
|
|
checkC--;
|
|
}
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static int ToString(double d, char* outStr)
|
|
{
|
|
sprintf(outStr, "%1.17g", d);
|
|
int len = (int)strlen(outStr);
|
|
for (int i = 0; outStr[i] != 0; i++)
|
|
{
|
|
if (outStr[i] == '.')
|
|
{
|
|
int checkC = len - 1;
|
|
while (true)
|
|
{
|
|
char c = outStr[checkC];
|
|
if (c == '.')
|
|
{
|
|
return checkC;
|
|
}
|
|
else if (c == 'e')
|
|
{
|
|
return len;
|
|
}
|
|
else if (c != '0')
|
|
{
|
|
for (int j = i + 1; j <= checkC; j++)
|
|
if (outStr[j] == 'e')
|
|
return len;
|
|
return checkC + 1;
|
|
}
|
|
checkC--;
|
|
}
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int Float::ToString(float f, char* outStr)
|
|
{
|
|
#ifdef USE_CHARCONV
|
|
auto result = std::to_chars(outStr, outStr + 256, f);
|
|
return (int)(result.ptr - outStr);
|
|
#else
|
|
return ::ToString(f, outStr);
|
|
#endif
|
|
}
|
|
|
|
int Double::ToString(double d, char* outStr)
|
|
{
|
|
#ifdef USE_CHARCONV
|
|
auto result = std::to_chars(outStr, outStr + 256, d);
|
|
return (int)(result.ptr - outStr);
|
|
#else
|
|
return ::ToString(d, outStr);
|
|
#endif
|
|
}
|