diff --git a/BeefLibs/Beefy2D/src/geom/Oval.bf b/BeefLibs/Beefy2D/src/geom/Oval.bf new file mode 100644 index 00000000..b6112a0b --- /dev/null +++ b/BeefLibs/Beefy2D/src/geom/Oval.bf @@ -0,0 +1,40 @@ +namespace Beefy.geom; + +struct Oval +{ + public float mX; + public float mY; + public float mRadiusX; + public float mRadiusY; + + public this(float x, float y, float radiusX, float radiusY) + { + mX = x; + mY = y; + mRadiusX = radiusX; + mRadiusY = radiusY; + } + + public this(Rect rect) + { + mX = rect.CenterX; + mY = rect.CenterY; + mRadiusX = rect.mWidth / 2; + mRadiusY = rect.mHeight / 2; + } + + public bool Contains(float x, float y) + { + float dx = (x - mX) / mRadiusX; + float dy = (y - mY) / mRadiusY; + return dx*dx + dy*dy <= 1.0f; + } + + public void Inflate(float x, float y) mut + { + mRadiusX += x; + mRadiusY += y; + } + + public bool Contains(Vector2 vec) => Contains(vec.mX, vec.mY); +} \ No newline at end of file diff --git a/BeefLibs/Beefy2D/src/geom/Rect.bf b/BeefLibs/Beefy2D/src/geom/Rect.bf index b14504f1..4f168951 100644 --- a/BeefLibs/Beefy2D/src/geom/Rect.bf +++ b/BeefLibs/Beefy2D/src/geom/Rect.bf @@ -212,6 +212,12 @@ namespace Beefy.geom (y >= mY) && (y < mY + mHeight)); } + public bool ContainsInclusive(T x, T y) + { + return ((x >= mX) && (x <= mX + mWidth) && + (y >= mY) && (y <= mY + mHeight)); + } + public bool Contains(Point pt) { return Contains(pt.x, pt.y); @@ -219,7 +225,7 @@ namespace Beefy.geom public bool Contains(Self rect) { - return Contains(rect.mX, rect.mY) && Contains(rect.mX + rect.mWidth, rect.mY + rect.mHeight); + return Contains(rect.mX, rect.mY) && ContainsInclusive(rect.mX + rect.mWidth, rect.mY + rect.mHeight); } public void Offset(T x, T y) mut diff --git a/BeefLibs/Beefy2D/src/gfx/Graphics.bf b/BeefLibs/Beefy2D/src/gfx/Graphics.bf index 00d33739..cc8ceb35 100644 --- a/BeefLibs/Beefy2D/src/gfx/Graphics.bf +++ b/BeefLibs/Beefy2D/src/gfx/Graphics.bf @@ -1035,6 +1035,27 @@ namespace Beefy.gfx { Gfx_CopyDrawVertex((.)idx, (.)srcIdx); } + + public void OutlineOval(float x, float y, float radiusX, float radiusY) + { + int numSections = 12 + (.)((radiusX + radiusY) * 0.15f); + for (int section < numSections) + { + float ang0 = (section * Math.PI_f * 2) / numSections; + float ang1 = ((section + 1) * Math.PI_f * 2) / numSections; + + float x0 = x + Math.Cos(ang0) * radiusX; + float y0 = y + Math.Sin(ang0) * radiusY; + float x1 = x + Math.Cos(ang1) * radiusX; + float y1 = y + Math.Sin(ang1) * radiusY; + DrawLine(x0, y0, x1, y1); + } + } + + public void OutlineCircle(float x, float y, float radius) + { + OutlineOval(x, y, radius, radius); + } } #else public class Graphics : GraphicsBase diff --git a/BeefLibs/Beefy2D/src/gfx/Image.bf b/BeefLibs/Beefy2D/src/gfx/Image.bf index f5db7e87..c4279ec4 100644 --- a/BeefLibs/Beefy2D/src/gfx/Image.bf +++ b/BeefLibs/Beefy2D/src/gfx/Image.bf @@ -103,6 +103,7 @@ namespace Beefy.gfx scope AutoBeefPerf("Image.LoadFromFile"); var useFileName = scope String()..Append(fileName); + useFileName.Replace('\\', '/'); FilePackManager.TryMakeMemoryString(useFileName); void* aNativeTextureSegment = Gfx_LoadTexture(useFileName, (int32)flags); if (aNativeTextureSegment == null) diff --git a/BeefLibs/Beefy2D/src/widgets/KeyCode.bf b/BeefLibs/Beefy2D/src/widgets/KeyCode.bf index 42d2fe62..cfa6221e 100644 --- a/BeefLibs/Beefy2D/src/widgets/KeyCode.bf +++ b/BeefLibs/Beefy2D/src/widgets/KeyCode.bf @@ -125,7 +125,11 @@ namespace Beefy.widgets case .LWin, .RWin, .Alt, + .LAlt, + .RAlt, .Control, + .LCtrl, + .RCtrl, .Command, .Shift: return true; @@ -144,7 +148,7 @@ namespace Beefy.widgets return (KeyCode)c; if ((c >= '0') && (c <= '9')) return (KeyCode)c; - if ((c >= 'a') && (c <= 'a')) + if ((c >= 'a') && (c <= 'z')) return (KeyCode)(c.ToUpper); if (c == '[') return (KeyCode)LBracket; @@ -160,6 +164,14 @@ namespace Beefy.widgets return (KeyCode)Period; if (c == ',') return (KeyCode)Comma; + if (c == ' ') + return (KeyCode)Space; + if (c == '-') + return (KeyCode)Minus; + if (c == '+') + return (KeyCode)Add; + if (c == '=') + return (KeyCode)Equals; } if (str.StartsWith("0x")) diff --git a/BeefLibs/corlib/src/Array.bf b/BeefLibs/corlib/src/Array.bf index 4011c2d5..0476165f 100644 --- a/BeefLibs/corlib/src/Array.bf +++ b/BeefLibs/corlib/src/Array.bf @@ -568,7 +568,13 @@ namespace System public T* CArray() { return &mFirstElement; - } + } + + public void CopyTo(T[,] arrayTo) + { + Debug.Assert(arrayTo.mLength >= mLength); + Internal.MemCpy(&arrayTo.GetRef(0), &GetRef(0), strideof(T) * mLength, alignof(T)); + } public Span.Enumerator GetEnumerator() { diff --git a/BeefLibs/corlib/src/Console.bf b/BeefLibs/corlib/src/Console.bf index f5f8fd3c..d00c379c 100644 --- a/BeefLibs/corlib/src/Console.bf +++ b/BeefLibs/corlib/src/Console.bf @@ -85,10 +85,19 @@ namespace System static void SetupOutStringEx() { - OutString = => OutString_Ex; + sOutString = => OutString_Ex; } - static function void(StringView str) OutString = => OutString_Simple; + static function void(StringView str) sOutString; + static function void(StringView str) OutString + { + get + { + if (sOutString == null) + sOutString = => OutString_Simple; + return sOutString; + } + } #if !BF_RUNTIME_DISABLE && !BF_PLATFORM_WASM private static extern void PutChars(char8* c, int32 len); diff --git a/BeefLibs/corlib/src/GC.bf b/BeefLibs/corlib/src/GC.bf index 2d2e6478..8e67b599 100644 --- a/BeefLibs/corlib/src/GC.bf +++ b/BeefLibs/corlib/src/GC.bf @@ -99,8 +99,11 @@ namespace System [CallingConvention(.Cdecl)] public extern static void SetMaxRawDeferredObjectFreePercentage(int maxPercentage); #else + [LinkName("__GC_Report")] public static void Report() {} + [LinkName("__GC_Shutdown")] public static void Shutdown() {} + [LinkName("__GC_SetMaxRawDeferredObjectFreePercentage")] public static void SetMaxRawDeferredObjectFreePercentage(int maxPercentage) {} #endif @@ -138,17 +141,27 @@ namespace System [CallingConvention(.Cdecl)] public extern static void ExcludeThreadId(int thereadId); #else + [LinkName("__GC_Disable")] public static void Disable() {} + [LinkName("__GC_Collect")] public static void Collect(bool async = true) {} + [LinkName("__GC_MarkAllStaticMembers")] private static void MarkAllStaticMembers() {} + [LinkName("__GC_DebugDumpLeaks")] public static void DebugDumpLeaks() {} - [SkipCall] - public static void Mark(Object obj) {} + [SkipCall, LinkName("__GC_Mark1")] + public static void Mark(Object obj) {} + [LinkName("__GC_Mark2")] public static void Mark(void* ptr, int size) {} + [LinkName("__GC_SetAutoCollectPeriod")] public static void SetAutoCollectPeriod(int periodMS) {} + [LinkName("__GC_SetCollectFreeThreshold")] public static void SetCollectFreeThreshold(int freeBytes) {} + [LinkName("__GC_SetMaxPausePercentage")] public static void SetMaxPausePercentage(int maxPausePercentage) {} + [LinkName("__GC_AddPendingThread")] static void AddPendingThread(void* internalThreadInfo) {} + [LinkName("__GC_ExcludeThreadId")] public static void ExcludeThreadId(int thereadId) {} #endif diff --git a/BeefLibs/corlib/src/IO/FileDialog.bf b/BeefLibs/corlib/src/IO/FileDialog.bf index 6b4893c8..6aaf47cb 100644 --- a/BeefLibs/corlib/src/IO/FileDialog.bf +++ b/BeefLibs/corlib/src/IO/FileDialog.bf @@ -623,6 +623,9 @@ abstract class CommonDialog public Result ShowDialog(INativeWindow owner = null) { + if (!Linux.IsSystemdAvailable) + return .Err; + TryC!(Linux.SdBusOpenUser(&mBus)); // Maybe keep the bus open while the program is running ? Linux.DBusMsg* call = ?; diff --git a/BeefLibs/corlib/src/IRefCounted.bf b/BeefLibs/corlib/src/IRefCounted.bf index 04f61494..aeee3315 100644 --- a/BeefLibs/corlib/src/IRefCounted.bf +++ b/BeefLibs/corlib/src/IRefCounted.bf @@ -82,7 +82,7 @@ namespace System } } - class RefCounted : IRefCounted where T : class, delete + class RefCounted : IRefCounted where T : delete { public T mVal; public int mRefCount = 1; @@ -178,7 +178,7 @@ namespace System public virtual T Detach() { var val = mVal; - mVal = null; + mVal = default; return val; } diff --git a/BeefLibs/corlib/src/Linux.bf b/BeefLibs/corlib/src/Linux.bf index fe07c556..452a6f9e 100644 --- a/BeefLibs/corlib/src/Linux.bf +++ b/BeefLibs/corlib/src/Linux.bf @@ -44,6 +44,33 @@ class Linux public typealias DBusMsgHandler = function int32(DBusMsg *m, void *userdata, DBusErr *ret_error); + public static bool IsSystemdAvailable { get; private set; } = true; + + [AlwaysInclude, StaticInitPriority(100)] + static class AllowFail + { + public static this() + { + Runtime.AddErrorHandler(new => Handle); + } + + public static Runtime.ErrorHandlerResult Handle(Runtime.ErrorStage errorStage, Runtime.Error error) + { + if (errorStage == .PreFail) + { + if (var loadLibaryError = error as Runtime.LoadSharedLibraryError) + { + if (loadLibaryError.mPath == "libsystemd.so") + { + IsSystemdAvailable = false; + return .Ignore; + } + } + } + return .ContinueFailure; + } + } + [Import("libsystemd.so"), LinkName("sd_bus_open_user")] public static extern c_int SdBusOpenUser(DBus **ret); [Import("libsystemd.so"), LinkName("sd_bus_open_system")] diff --git a/BeefLibs/corlib/src/Net/Socket.bf b/BeefLibs/corlib/src/Net/Socket.bf index dcc2bc75..f55f979b 100644 --- a/BeefLibs/corlib/src/Net/Socket.bf +++ b/BeefLibs/corlib/src/Net/Socket.bf @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Interop; namespace System.Net { @@ -134,6 +135,26 @@ namespace System.Net } } + [CRepr, Union] + public struct IPv6Address + { + public uint8[16] byte; + public uint16[8] word; + + public this(params uint16[8] addr) + { + for(let i < 8) + { + this.word[i] = (.)htons((int16)addr[i]); + } + } + + public this(params uint8[16] addr) + { + this.byte = addr; + } + } + [CRepr] public struct SockAddr { @@ -149,6 +170,16 @@ namespace System.Net public char8[8] sin_zero; } + [CRepr] + public struct SockAddr_in6 : SockAddr + { + public int16 sin6_family; + public uint16 sin6_port; + public uint32 sin6_flowinfo; + public IPv6Address sin6_addr; + public uint32 sin6_scope_id; + } + [CRepr] public struct HostEnt { @@ -159,13 +190,33 @@ namespace System.Net public char8** h_addr_list; /* list of addresses */ } + [CRepr] + public struct AddrInfo + { + public int32 ai_flags; + public int32 ai_family; + public int32 ai_socktype; + public int32 ai_protocol; + public c_size ai_addrlen; +#if BF_PLATFORM_WINDOWS + public char8* ai_canonname; + public SockAddr* ai_addr; +#else + public SockAddr* ai_addr; + public char8* ai_canonname; +#endif + public SockAddr* ai_next; + } + public const HSocket INVALID_SOCKET = (HSocket)-1; public const int32 SOCKET_ERROR = -1; public const int AF_INET = 2; + public const int AF_INET6 = 23; public const int SOCK_STREAM = 1; public const int SOCK_DGRAM = 2; public const int IPPROTO_TCP = 6; public const int IPPROTO_UDP = 17; + public const int IPPROTO_IPV6 = 41; public const int TCP_NODELAY = 1; public const int TCP_MAXSEG = 2; @@ -176,12 +227,16 @@ namespace System.Net public const int SOL_SOCKET = 0xffff; public const int SO_REUSEADDR = 0x0004; public const int SO_BROADCAST = 0x0020; + public const int IPV6_V6ONLY = 27; #else public const int SOL_SOCKET = 1; public const int SO_REUSEADDR = 2; public const int SO_BROADCAST = 6; + public const int IPV6_V6ONLY = 26; #endif + public const IPv4Address INADDR_ANY = default; + public const IPv6Address IN6ADDR_ANY = default; #if BF_PLATFORM_WINDOWS const int FIONBIO = (int)0x8004667e; @@ -252,6 +307,9 @@ namespace System.Net [CLink, CallingConvention(.Stdcall)] static extern HostEnt* gethostbyname(char8* name); + [CLink, CallingConvention(.Stdcall)] + static extern int32 getaddrinfo(char8* pNodeName, char8* pServiceName, AddrInfo* pHints, AddrInfo** ppResult); + [CLink, CallingConvention(.Stdcall)] static extern HSocket socket(int32 af, int32 type, int32 protocol); @@ -395,8 +453,49 @@ namespace System.Net int32 size = sizeof(SockAddr_in); if (bind(mHandle, &service, size) == SOCKET_ERROR) { - int err = WSAGetLastError(); + Close(); + return .Err; + } + if (listen(mHandle, backlog) == SOCKET_ERROR) + { +#unwarn + int err = GetLastError(); + Close(); + return .Err; + } + + return .Ok; + } + + public Result Listen(IPv6Address address, int32 port, int32 backlog = 5, bool v6Only = false) + { + Debug.Assert(mHandle == INVALID_SOCKET); + + mHandle = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + + if (mHandle == INVALID_SOCKET) + { +#unwarn + int32 err = GetLastError(); + return .Err; + } + + int32 ipv6Opt = v6Only ? 1 : 0; + setsockopt(mHandle, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6Opt, 4); + + RehupSettings(); + + SockAddr_in6 service; + service.sin6_family = AF_INET6; + service.sin6_addr = address; + service.sin6_port = (uint16)htons((int16)port); + + int32 size = sizeof(SockAddr_in6); + if (bind(mHandle, &service, size) == SOCKET_ERROR) + { +#unwarn + int err = GetLastError(); Close(); return .Err; } @@ -443,13 +542,47 @@ namespace System.Net return .Ok; } + public Result Connect(StringView addr, int32 port, out SockAddr* sockAddr, out int addrFamily) + { + sockAddr = null; + addrFamily = default; + + AddrInfo hints = default; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + AddrInfo* addrInfo = null; + if (getaddrinfo(addr.Ptr, null, &hints, &addrInfo) < 0) + return .Err; + + sockAddr = addrInfo.ai_addr; + addrFamily = addrInfo.ai_family; + + mHandle = socket(addrInfo.ai_family, SOCK_STREAM, IPPROTO_TCP); + if (mHandle == INVALID_SOCKET) + return .Err; + + if (connect(mHandle, sockAddr, (.)addrInfo.ai_addrlen) == SOCKET_ERROR) + return .Err; + + if (mHandle == INVALID_SOCKET) + { +#unwarn + int32 err = GetLastError(); + return .Err; + } + + mIsConnected = true; + RehupSettings(); + + return .Ok; + } + public Result Connect(StringView addr, int32 port) => Connect(addr, port, ?); - public Result AcceptFrom(Socket listenSocket, out SockAddr_in clientAddr) + public Result AcceptFrom(Socket listenSocket, SockAddr* from, int32* fromLen) { - clientAddr = default; - int32 clientAddrLen = sizeof(SockAddr_in); - mHandle = accept(listenSocket.mHandle, &clientAddr, &clientAddrLen); + mHandle = accept(listenSocket.mHandle, from, fromLen); if (mHandle == INVALID_SOCKET) { #unwarn @@ -462,7 +595,14 @@ namespace System.Net return .Ok; } - public Result AcceptFrom(Socket listenSocket) => AcceptFrom(listenSocket, ?); + public Result AcceptFrom(Socket listenSocket, out SockAddr_in clientAddr) + { + clientAddr = default; + int32 clientAddrLen = sizeof(SockAddr_in); + return AcceptFrom(listenSocket, &clientAddr, &clientAddrLen); + } + + public Result AcceptFrom(Socket listenSocket) => AcceptFrom(listenSocket, null, null); public static int32 Select(FDSet* readFDS, FDSet* writeFDS, FDSet* exceptFDS, int waitTimeMS) { @@ -597,6 +737,8 @@ namespace System.Net #unwarn public Result SendTo(void* ptr, int size, SockAddr_in to) => SendTo(ptr, size, &to, sizeof(SockAddr_in)); +#unwarn + public Result SendTo(void* ptr, int size, SockAddr_in6 to) => SendTo(ptr, size, &to, sizeof(SockAddr_in6)); public void Close() { @@ -632,5 +774,32 @@ namespace System.Net status = bind(mHandle, &bindAddr, sizeof(SockAddr_in)); return .Ok; } + + public Result OpenUDPIPv6(int32 port = -1, bool v6Only = false) + { + SockAddr_in6 bindAddr = default; + + mHandle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (mHandle == INVALID_SOCKET) + { + return .Err; + } + + RehupSettings(); + + int32 yes = 1; + //setsockopt(mHandle, SOL_SOCKET, SO_REUSEADDR, &yes, 4); + int32 status = setsockopt(mHandle, SOL_SOCKET, SO_BROADCAST, &yes, 4); + + int32 ipv6Opt = v6Only ? 1 : 0; + setsockopt(mHandle, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6Opt, 4); + + bindAddr.sin6_addr = IN6ADDR_ANY; + bindAddr.sin6_port = (.)htons((int16)port); + bindAddr.sin6_family = AF_INET6; + + status = bind(mHandle, &bindAddr, sizeof(SockAddr_in6)); + return .Ok; + } } } diff --git a/BeefLibs/corlib/src/Runtime.bf b/BeefLibs/corlib/src/Runtime.bf index 9582395b..f140ed5e 100644 --- a/BeefLibs/corlib/src/Runtime.bf +++ b/BeefLibs/corlib/src/Runtime.bf @@ -14,7 +14,6 @@ namespace System public bool AVX, AVX2, AVX512; } - [StaticInitPriority(201)] static class Runtime { const int32 cVersion = 10; @@ -284,7 +283,7 @@ namespace System mDebugMessageData_SetupProfilerCmd = => DebugMessageData_SetupProfilerCmd; mDebugMessageData_Fatal = => DebugMessageData_Fatal; mDebugMessageData_Clear = => DebugMessageData_Clear; - mCheckErrorHandler = => CheckErrorHandler; + mCheckErrorHandler = => CheckErrorHandler_Thunk; } }; @@ -389,13 +388,23 @@ namespace System public static bool sInsideErrorHandler; } + + [AlwaysInclude, StaticInitPriority(201)] + static struct RuntimeInit + { + public static this() + { + Runtime.Init(); + } + } + static RtFlags sExtraFlags; static bool sQueriedFeatures = false; static RuntimeFeatures sFeatures; static function void() sThreadInit; - public static this() + static void Init() { #if !BF_RUNTIME_DISABLE BfRtCallbacks.sCallbacks.Init(); @@ -422,6 +431,11 @@ namespace System #endif } + public static this() + { + + } + [NoReturn] public static void FatalError(String msg = "Fatal error encountered", String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum) { @@ -497,6 +511,13 @@ namespace System public static function int32(char8* kind, char8* arg1, char8* arg2, int arg3) CheckErrorHandler; public static function void*(char8* filePath) LibraryLoadCallback; + public static int32 CheckErrorHandler_Thunk(char8* kind, char8* arg1, char8* arg2, int arg3) + { + if (CheckErrorHandler != null) + return CheckErrorHandler(kind, arg1, arg2, arg3); + return 0; + } + static ErrorHandlerResult CheckAssertError_Impl(AssertError.Kind kind, String error, String filePath, int lineNum) { return CheckErrorHandlers(scope AssertError(kind, error, filePath, lineNum)); diff --git a/BeefLibs/corlib/src/String.bf b/BeefLibs/corlib/src/String.bf index 088cfa81..9073d988 100644 --- a/BeefLibs/corlib/src/String.bf +++ b/BeefLibs/corlib/src/String.bf @@ -972,6 +972,9 @@ namespace System public void Append(char8* appendPtr, int length) { + Debug.Assert(length >= 0); + if (length <= 0) + return; int newCurrentIndex = mLength + length; char8* ptr; if (newCurrentIndex > AllocSize) @@ -996,6 +999,9 @@ namespace System public void Append(char8[] arr, int idx, int length) { + Debug.Assert(length >= 0); + if (length <= 0) + return; int newCurrentIndex = mLength + length; char8* ptr; if (newCurrentIndex > AllocSize) diff --git a/BeefLibs/corlib/src/Windows.bf b/BeefLibs/corlib/src/Windows.bf index be1ce04f..ff7f7c23 100644 --- a/BeefLibs/corlib/src/Windows.bf +++ b/BeefLibs/corlib/src/Windows.bf @@ -107,6 +107,7 @@ namespace System public int32 Height => bottom - top; } + [CRepr] public struct Point : this(int32 x, int32 y) { @@ -1715,6 +1716,9 @@ namespace System [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] public static extern HWnd SetForegroundWindow(HWnd wnd); + [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] + public static extern HWnd ShowWindow(HWnd wnd, int32 cmdShow); + [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] public static extern HWnd GetForegroundWindow(); diff --git a/BeefTools/BeefPerf/src/BPApp.bf b/BeefTools/BeefPerf/src/BPApp.bf index 2b255562..f5d5d188 100644 --- a/BeefTools/BeefPerf/src/BPApp.bf +++ b/BeefTools/BeefPerf/src/BPApp.bf @@ -155,8 +155,9 @@ namespace BeefPerf LogLine(text); mFailed = true; - if (!mShuttingDown) - Shutdown(); + /*if (!mShuttingDown) + Shutdown();*/ + //Stop(); return; } @@ -647,7 +648,7 @@ namespace BeefPerf public override void UnhandledCommandLine(String key, String value) { - Fail(StackStringFormat!("Unhandled command line param: {0}", key)); + Fail(scope:: String()..AppendF("Unhandled command line param: {0}", key)); } void SetupNewWindow(WidgetWindow window) @@ -729,6 +730,9 @@ namespace BeefPerf public override void Update(bool batchStart) { + if (mFailed) + Stop(); + base.Update(batchStart); /*if (!mListenSocket.IsConnected) diff --git a/BeefTools/BeefPerf/src/BPClient.bf b/BeefTools/BeefPerf/src/BPClient.bf index 3e38662c..ca4cd5aa 100644 --- a/BeefTools/BeefPerf/src/BPClient.bf +++ b/BeefTools/BeefPerf/src/BPClient.bf @@ -1441,6 +1441,8 @@ namespace BeefPerf if (cNext == 'd') zoneName.mParamsSize += 4; + else if (cNext == 'p') + zoneName.mParamsSize += 8; else if (cNext == 'f') zoneName.mParamsSize += 4; else if (cNext == 's') diff --git a/BeefTools/BeefPerf/src/BpStateContext.bf b/BeefTools/BeefPerf/src/BpStateContext.bf index 236bc301..45ce7842 100644 --- a/BeefTools/BeefPerf/src/BpStateContext.bf +++ b/BeefTools/BeefPerf/src/BpStateContext.bf @@ -216,6 +216,12 @@ namespace BeefPerf Read(&val, 4); val.ToString(outStr); } + else if (cNext == 'p') + { + int64 val = 0; + Read(&val, 8); + val.ToString(outStr, "X", null); + } else if (cNext == 'f') { float val = 0; diff --git a/BeefTools/BeefPerf/src/PerfView.bf b/BeefTools/BeefPerf/src/PerfView.bf index e844201e..429d3424 100644 --- a/BeefTools/BeefPerf/src/PerfView.bf +++ b/BeefTools/BeefPerf/src/PerfView.bf @@ -767,6 +767,12 @@ namespace BeefPerf stateCtx.Read(&val, 4); val.ToString(str); } + else if (cNext == 'p') + { + int64 val = 0; + stateCtx.Read(&val, 8); + val.ToString(str, "X", null); + } else if (cNext == 'f') { float val = 0; @@ -2316,6 +2322,11 @@ namespace BeefPerf { switch (keyCode) { + case (KeyCode)'C': + if (DarkTooltipManager.sTooltip != null) + { + gApp.SetClipboardText(DarkTooltipManager.sTooltip.mText); + } case (KeyCode)'Z': mUndoManager.Undo(); case (KeyCode)'Y': diff --git a/BeefTools/LogViewer/src/Board.bf b/BeefTools/LogViewer/src/Board.bf index a76b8e37..1f6d624a 100644 --- a/BeefTools/LogViewer/src/Board.bf +++ b/BeefTools/LogViewer/src/Board.bf @@ -31,6 +31,9 @@ namespace LogViewer public List mNewMatches ~ delete _; public bool mRefreshing; + public String mFilePath ~ delete _; + public String mMemLogName ~ delete _; + public uint32[] mColors = new .( 0xFFFFFFFF, 0xFFFC5858, @@ -54,6 +57,7 @@ namespace LogViewer ewc.mFont = gApp.mFont; ewc.mWordWrap = false; ewc.mTextColors = mColors; + ewc.mIsReadOnly = true; mDocEdit.InitScrollbars(true, true); AddWidget(mDocEdit); @@ -79,6 +83,12 @@ namespace LogViewer public void Load(StringView filePath) { + DeleteAndNullify!(mFilePath); + DeleteAndNullify!(mMemLogName); + mFilePath = new .(filePath); + + mWidgetWindow.SetTitle(scope $"LogViewer - {mFilePath}"); + scope AutoBeefPerf("Board.Load"); delete mContent; @@ -89,9 +99,45 @@ namespace LogViewer { gApp.Fail("Failed to open file '{0}'", filePath); } + + mFilterDirtyCountdown = 1; + //Refresh(); //mDocEdit.SetText(mContent); } + [CallingConvention(.Stdcall), CLink] + static extern char8* MemLogger_Get(char8* name); + + public void LoadMemLog(StringView name) + { + DeleteAndNullify!(mFilePath); + DeleteAndNullify!(mMemLogName); + mMemLogName = new .(name); + + mWidgetWindow.SetTitle(scope $"LogViewer - {mMemLogName}"); + + var result = MemLogger_Get(name.ToScopeCStr!()); + if (result == null) + { + gApp.Fail("Failed to open MemLog '{0}'", name); + return; + } + + delete mContent; + mContent = new String(); + mContent.Append(result); + + mFilterDirtyCountdown = 1; + } + + public void Reload() + { + if (mFilePath != null) + Load(mFilePath); + if (mMemLogName != null) + LoadMemLog(mMemLogName); + } + void Refresh() { scope AutoBeefPerf("Board.Refresh"); @@ -105,37 +151,40 @@ namespace LogViewer let filters = mFilter.Split!('\n'); - for (var line in mContent.Split('\n')) + if (mContent != null) { - bool hadMatch = false; - bool hadFilter = false; - - for (var filter in filters) + for (var line in mContent.Split('\n')) { - if (filter.Length == 0) - continue; - hadFilter = true; - int lastIdx = -1; - while (true) + bool hadMatch = false; + bool hadFilter = false; + + for (var filter in filters) { - int findIdx = line.IndexOf(filter, lastIdx + 1); - if (findIdx == -1) - break; + if (filter.Length == 0) + continue; + hadFilter = true; + int lastIdx = -1; + while (true) + { + int findIdx = line.IndexOf(filter, lastIdx + 1); + if (findIdx == -1) + break; - hadMatch = true; - lastIdx = findIdx + filter.Length - 1; + hadMatch = true; + lastIdx = findIdx + filter.Length - 1; - Match match; - match.mFilterIdx = (.)@filter; - match.mTextIdx = (.)(mNewContent.Length + findIdx); - mNewMatches.Add(match); + Match match; + match.mFilterIdx = (.)@filter; + match.mTextIdx = (.)(mNewContent.Length + findIdx); + mNewMatches.Add(match); + } } - } - if ((hadMatch) || (!hadFilter)) - { - mNewContent.Append(line); - mNewContent.Append('\n'); + if ((hadMatch) || (!hadFilter)) + { + mNewContent.Append(line); + mNewContent.Append('\n'); + } } } diff --git a/BeefTools/LogViewer/src/LVApp.bf b/BeefTools/LogViewer/src/LVApp.bf index cbf80e79..9824e04b 100644 --- a/BeefTools/LogViewer/src/LVApp.bf +++ b/BeefTools/LogViewer/src/LVApp.bf @@ -8,6 +8,7 @@ using Beefy.utils; using System.IO; using System.Diagnostics; using System.Threading; +using Beefy.sys; namespace LogViewer { @@ -26,7 +27,7 @@ namespace LogViewer { base.Init(); - var dialog = scope OpenFileDialog(); + /*var dialog = scope OpenFileDialog(); dialog.SetFilter("All files (*.*)|*.*"); dialog.InitialDirectory = mInstallDir; dialog.Title = "Open Log"; @@ -35,7 +36,7 @@ namespace LogViewer { Stop(); return; - } + }*/ BeefPerf.Init("127.0.0.1", "LogViewer"); @@ -45,7 +46,7 @@ namespace LogViewer BFWindow.Flags windowFlags = BFWindow.Flags.Border | //BFWindow.Flags.SysMenu | //| BFWindow.Flags.CaptureMediaKeys | BFWindow.Flags.Caption | BFWindow.Flags.Minimize | BFWindow.Flags.QuitOnClose | BFWindowBase.Flags.Resizable | - BFWindow.Flags.SysMenu; + BFWindow.Flags.SysMenu | .Menu; mFont = new Font(); float fontSize = 12; @@ -55,17 +56,43 @@ namespace LogViewer mFont.AddAlternate("Segoe UI Emoji", fontSize); mBoard = new Board(); - mBoard.Load(dialog.FileNames[0]); - mMainWindow = new WidgetWindow(null, "LogViewer", 0, 0, 1600, 1200, windowFlags, mBoard); + //mBoard.Load(dialog.FileNames[0]); + mMainWindow = new WidgetWindow(null, "LogViewer", 20, 20, 1600, 1200, windowFlags, mBoard); //mMainWindow.mWindowKeyDownDelegate.Add(new => SysKeyDown); mMainWindow.SetMinimumSize(480, 360); mMainWindow.mIsMainWindow = true; + + SysMenu root = mMainWindow.mSysMenu; + var subMenu = root.AddMenuItem("&File"); + subMenu.AddMenuItem("&Open", "Ctrl+O", new (menu) => + { + var dialog = scope OpenFileDialog(); + dialog.SetFilter("All files (*.*)|*.*"); + dialog.InitialDirectory = mInstallDir; + dialog.Title = "Open Log"; + let result = dialog.ShowDialog(); + if ((result case .Err) || (dialog.FileNames.Count == 0)) + { + Stop(); + return; + } + mBoard.Load(dialog.FileNames[0]); + }); + subMenu.AddMenuItem("Read &MemLog", "Ctrl+M", new (menu) => + { + var dialog = new MemLogDialog(); + dialog.PopupWindow(mMainWindow); + }); + subMenu.AddMenuItem("&Reload", "Ctrl+R", new (menu) => + { + mBoard.Reload(); + }); } public void Fail(String str, params Object[] paramVals) { var errStr = scope String(); - errStr.AppendF(str, paramVals); + errStr.AppendF(str, params paramVals); Fail(errStr); } diff --git a/BeefTools/LogViewer/src/MemLogDialog.bf b/BeefTools/LogViewer/src/MemLogDialog.bf new file mode 100644 index 00000000..8128b9e8 --- /dev/null +++ b/BeefTools/LogViewer/src/MemLogDialog.bf @@ -0,0 +1,21 @@ +using Beefy.theme.dark; +using Beefy.widgets; + +namespace LogViewer; + +class MemLogDialog : DarkDialog +{ + EditWidget mEditWidget; + + public this() : base("Open MemLog", "MemLog Name") + { + + mDefaultButton = AddButton("OK", new (evt) => + { + var name = mEditWidget.GetText(.. scope .()); + gApp.mBoard.LoadMemLog(name); + }); + mEscButton = AddButton("Cancel", new (evt) => Close()); + mEditWidget = AddEdit(""); + } +} \ No newline at end of file diff --git a/BeefySysLib/BeefySysLib.cpp b/BeefySysLib/BeefySysLib.cpp index bfaf3143..3c6858c9 100644 --- a/BeefySysLib/BeefySysLib.cpp +++ b/BeefySysLib/BeefySysLib.cpp @@ -10,6 +10,7 @@ #include "util/Vector.h" #include "util/PerfTimer.h" #include "util/TLSingleton.h" +#include "util/MemLogger.h" #include "img/ImgEffects.h" #include "util/AllocDebug.h" @@ -926,4 +927,36 @@ BF_EXPORT void BF_CALLTYPE BF_Test() for (int i : iArr) OutputDebugStrF("Hey %d\n", i); -} \ No newline at end of file +} + +BF_EXPORT void* BF_CALLTYPE MemLogger_Create(const char* memName, int size) +{ + MemLogger* memLogger = new MemLogger(); + if (!memLogger->Create(memName, size)) + { + delete memLogger; + return NULL; + } + return memLogger; +} + +BF_EXPORT void BF_CALLTYPE MemLogger_Write(MemLogger* memLogger, void* ptr, int size) +{ + memLogger->Write(ptr, size); +} + +BF_EXPORT void BF_CALLTYPE MemLogger_Delete(MemLogger* memLogger) +{ + delete memLogger; +} + +BF_EXPORT const char* BF_CALLTYPE MemLogger_Get(const char* memName) +{ + MemLogger memLogger; + + String& outString = *gBeefySys_TLStrReturn.Get(); + outString.Clear(); + if (!memLogger.Get(memName, outString)) + return NULL; + return outString.c_str(); +} diff --git a/BeefySysLib/BeefySysLib.vcxproj b/BeefySysLib/BeefySysLib.vcxproj index 24f095c5..5d01f54e 100644 --- a/BeefySysLib/BeefySysLib.vcxproj +++ b/BeefySysLib/BeefySysLib.vcxproj @@ -1950,6 +1950,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + @@ -2189,6 +2190,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + diff --git a/BeefySysLib/BeefySysLib.vcxproj.filters b/BeefySysLib/BeefySysLib.vcxproj.filters index 55154045..5b40e16d 100644 --- a/BeefySysLib/BeefySysLib.vcxproj.filters +++ b/BeefySysLib/BeefySysLib.vcxproj.filters @@ -743,6 +743,9 @@ src\img + + src\util + @@ -1147,6 +1150,9 @@ src\img + + src\util + diff --git a/BeefySysLib/BeefySysLib_static.vcxproj b/BeefySysLib/BeefySysLib_static.vcxproj index ac170baa..9dc0b08d 100644 --- a/BeefySysLib/BeefySysLib_static.vcxproj +++ b/BeefySysLib/BeefySysLib_static.vcxproj @@ -880,6 +880,7 @@ + @@ -1047,6 +1048,7 @@ + diff --git a/BeefySysLib/BeefySysLib_static.vcxproj.filters b/BeefySysLib/BeefySysLib_static.vcxproj.filters index 9bc062c7..cbe3e70f 100644 --- a/BeefySysLib/BeefySysLib_static.vcxproj.filters +++ b/BeefySysLib/BeefySysLib_static.vcxproj.filters @@ -593,6 +593,9 @@ src\img + + src\util + @@ -913,6 +916,9 @@ src\img + + src\util + diff --git a/BeefySysLib/CMakeLists.txt b/BeefySysLib/CMakeLists.txt index f14c5951..86094049 100644 --- a/BeefySysLib/CMakeLists.txt +++ b/BeefySysLib/CMakeLists.txt @@ -294,6 +294,7 @@ file(GLOB SRC_FILES util/MappedFile.cpp util/MathUtils.cpp util/Matrix4.cpp + util/MemLogger.cpp util/PerfTimer.cpp util/Point.cpp util/PolySpline.cpp diff --git a/BeefySysLib/Common.cpp b/BeefySysLib/Common.cpp index a8dcb36c..d37473de 100644 --- a/BeefySysLib/Common.cpp +++ b/BeefySysLib/Common.cpp @@ -660,7 +660,7 @@ void Beefy::ExactMinimalDoubleToStr(double d, char* str) static char* StbspCallback(char *buf, void *user, int len) { - ((String*)user)->Append(buf, len); + ((StringImpl*)user)->Append(buf, len); return buf; } @@ -821,6 +821,12 @@ String Beefy::vformat(const char* fmt, va_list argPtr) BF_stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr); return str; } + +void Beefy::vformat(StringImpl& str, const char* fmt, va_list argPtr) +{ + char buf[STB_SPRINTF_MIN]; + BF_stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr); +} #endif String Beefy::StrFormat(const char* fmt ...) diff --git a/BeefySysLib/Common.h b/BeefySysLib/Common.h index 49a9c4c4..3cfa130c 100644 --- a/BeefySysLib/Common.h +++ b/BeefySysLib/Common.h @@ -195,6 +195,7 @@ uint64 BFGetTickCountMicro(); uint64 BFGetTickCountMicroFast(); extern String vformat(const char* fmt, va_list argPtr); +extern void vformat(StringImpl& str, const char* fmt, va_list argPtr); extern String StrFormat(const char* fmt ...); void ExactMinimalFloatToStr(float f, char* str); void ExactMinimalDoubleToStr(double d, char* str); diff --git a/BeefySysLib/platform/posix/PosixCommon.cpp b/BeefySysLib/platform/posix/PosixCommon.cpp index e108f0a9..4c16148b 100644 --- a/BeefySysLib/platform/posix/PosixCommon.cpp +++ b/BeefySysLib/platform/posix/PosixCommon.cpp @@ -832,6 +832,17 @@ BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetComputerName(char* outStr, int* inOutS // BfpProcess +struct BfpProcess +{ + pid_t mProcessId; + String mImageName; + + BfpProcess() + { + mProcessId = -1; + } +}; + BFP_EXPORT intptr BFP_CALLTYPE BfpProcess_GetCurrentId() { return getpid(); @@ -844,18 +855,87 @@ BFP_EXPORT bool BFP_CALLTYPE BfpProcess_IsRemoteMachine(const char* machineName) BFP_EXPORT BfpProcess* BFP_CALLTYPE BfpProcess_GetById(const char* machineName, int processId, BfpProcessResult* outResult) { +#ifdef BF_PLATFORM_LINUX + pid_t pid = static_cast(processId); + + char proc_dir[64]; + snprintf(proc_dir, sizeof(proc_dir), "/proc/%d", pid); + + if (access(proc_dir, F_OK) != 0) + { + OUTRESULT(BfpProcessResult_NotFound); + return NULL; + } + + BfpProcess* process = new BfpProcess(); + process->mProcessId = pid; + return process; +#else NOT_IMPL; return NULL; +#endif } BFP_EXPORT void BFP_CALLTYPE BfpProcess_Enumerate(const char* machineName, BfpProcess** outProcesses, int* inOutProcessesSize, BfpProcessResult* outResult) { +#ifdef BF_PLATFORM_LINUX + DIR* procDir = opendir("/proc"); + if (!procDir) + { + *inOutProcessesSize = 0; + return; + } + + Beefy::Array processList; + struct dirent* entry; + while ((entry = readdir(procDir)) != NULL) + { + const char* name = entry->d_name; + bool is_pid = name[0] != '\0'; + for (const char* p = name; *p; ++p) + { + if (*p < '0' || *p > '9') + { + is_pid = false; + break; + } + } + + if (is_pid) + { + pid_t pid = static_cast(atoi(name)); + if (pid == 0) + continue; + + BfpProcess* proc = new BfpProcess(); + proc->mProcessId = pid; + processList.Add(proc); + } + } + + closedir(procDir); + + if (static_cast(processList.size()) > *inOutProcessesSize) + { + *inOutProcessesSize = static_cast(processList.size()); + OUTRESULT(BfpProcessResult_InsufficientBuffer); + for (BfpProcess* p_del : processList) + delete p_del; + return; + } + + for (size_t i = 0; i < processList.size(); ++i) + outProcesses[i] = processList[i]; + *inOutProcessesSize = static_cast(processList.size()); + OUTRESULT(BfpProcessResult_Ok); +#else NOT_IMPL; +#endif } BFP_EXPORT void BFP_CALLTYPE BfpProcess_Release(BfpProcess* process) { - NOT_IMPL; + delete process; } BFP_EXPORT bool BFP_CALLTYPE BfpProcess_WaitFor(BfpProcess* process, int waitMS, int* outExitCode, BfpProcessResult* outResult) @@ -865,18 +945,49 @@ BFP_EXPORT bool BFP_CALLTYPE BfpProcess_WaitFor(BfpProcess* process, int waitMS, BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetMainWindowTitle(BfpProcess* process, char* outTitle, int* inOutTitleSize, BfpProcessResult* outResult) { - NOT_IMPL; + String title; + TryStringOut(title, outTitle, inOutTitleSize, (BfpResult*)outResult); } BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char* outName, int* inOutNameSize, BfpProcessResult* outResult) { - NOT_IMPL; + if (process->mImageName.IsEmpty()) + { +#ifdef BF_PLATFORM_LINUX + char path[PATH_MAX]; + snprintf(path, sizeof(path), "/proc/%d/exe", process->mProcessId); + char name_buf[PATH_MAX] = {0}; + ssize_t len = readlink(path, name_buf, sizeof(name_buf) - 1); + if (len != -1) + { + name_buf[len] = '\0'; + process->mImageName.Append(basename(name_buf)); + } + else + { + // Only 15 characters of the process name are returned by this, but it works for all processes. + snprintf(path, sizeof(path), "/proc/%d/comm", process->mProcessId); + FILE* fp = fopen(path, "r"); + if (fp) + { + if (fgets(name_buf, sizeof(name_buf), fp)) { + size_t len = strcspn(name_buf, "\n"); + name_buf[len] = '\0'; + process->mImageName.Append(name_buf, len); + } + fclose(fp); + } + } +#else + NOT_IMPL; +#endif + } + TryStringOut(process->mImageName, outName, inOutNameSize, (BfpResult*)outResult); } BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetProcessId(BfpProcess* process) { - NOT_IMPL; - return 0; + return process->mProcessId; } // BfpSpawn @@ -1822,7 +1933,27 @@ BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Create(const char* path, BfpFileResult BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Rename(const char* oldName, const char* newName, BfpFileResult* outResult) { - NOT_IMPL; + if (rename(oldName, newName) != 0) + { + switch (errno) + { + case EEXIST: + OUTRESULT(BfpFileResult_AlreadyExists); + break; + case ENOTEMPTY: + OUTRESULT(BfpFileResult_NotEmpty); + break; + case EISDIR: + case ENOTDIR: + OUTRESULT(BfpFileResult_InvalidParameter); + break; + default: + OUTRESULT(BfpFileResult_UnknownError); + break; + } + } + else + OUTRESULT(BfpFileResult_Ok); } BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Delete(const char* path, BfpFileResult* outResult) @@ -2372,9 +2503,148 @@ BFP_EXPORT void BFP_CALLTYPE BfpFile_GetFullPath(const char* inPath, char* outPa TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult); } -BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult) +BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPathC, char* outPathC, int* inOutPathSize, BfpFileResult* outResult) { - NOT_IMPL; + String inPath = inPathC; + String outPath; + + // Check for '/../' backtracking - handle those first + { + int i = 0; + int32 lastComponentStart = -1; + + while (i < inPath.mLength) + { + // Skip until path separator + while ((i < inPath.mLength) && (inPath[i] != DIR_SEP_CHAR) && (inPath[i] != DIR_SEP_CHAR_ALT)) + ++i; + + if (lastComponentStart != -1) + { + if ((i - lastComponentStart == 2) && (inPath[lastComponentStart] == '.') && (inPath[lastComponentStart + 1] == '.')) + { + // Backtrack + while ((lastComponentStart > 0) && + ((inPath[lastComponentStart - 1] == DIR_SEP_CHAR) || (inPath[lastComponentStart - 1] == DIR_SEP_CHAR_ALT))) + lastComponentStart--; + while ((lastComponentStart > 0) && (inPath[lastComponentStart - 1] != DIR_SEP_CHAR) && (inPath[lastComponentStart - 1] != DIR_SEP_CHAR_ALT)) + lastComponentStart--; + inPath.Remove(lastComponentStart, i - lastComponentStart + 1); + i = lastComponentStart; + continue; + } + else if ((i - lastComponentStart == 1) && (inPath[lastComponentStart] == '.')) + { + inPath.Remove(lastComponentStart, i - lastComponentStart + 1); + i = lastComponentStart; + continue; + } + } + + ++i; + // Ignore multiple slashes in a row + while ((i < inPath.mLength) && ((inPath[i] == DIR_SEP_CHAR) || (inPath[i] == DIR_SEP_CHAR_ALT))) + ++i; + + lastComponentStart = i; + } + } + + int32 i = 0; + int length = (int)inPath.length(); + + if (length >= 1) + { + // Handle root paths starting with '/' or '\' + if ((inPath[0] == DIR_SEP_CHAR) || (inPath[0] == DIR_SEP_CHAR_ALT)) + { + i++; // start after initial slash + outPath.Append(DIR_SEP_CHAR); + } + else + { + // Relative path - prepend current working directory + char cwd[PATH_MAX]; + if (getcwd(cwd, PATH_MAX) != NULL) + { + outPath.Append(cwd); + if (outPath[outPath.length() - 1] != DIR_SEP_CHAR) + outPath.Append(DIR_SEP_CHAR); + } + } + } + + int32 lastComponentStart = i; + bool addSeparator = false; + String subName; + + while (i < length) + { + // skip until path separator + while ((i < length) && (inPath[i] != DIR_SEP_CHAR) && (inPath[i] != DIR_SEP_CHAR_ALT)) + ++i; + + if (addSeparator) + outPath.Append(DIR_SEP_CHAR); + + subName.Clear(); + subName = inPath.Substring(0, i); + for (int j = 0; j < (int)subName.length(); j++) + if (subName[j] == DIR_SEP_CHAR_ALT) + subName[j] = DIR_SEP_CHAR; + + String parentPath; + String componentName; + int32 lastSep = subName.LastIndexOf(DIR_SEP_CHAR); + + if (lastSep != -1) + { + parentPath = subName.Substring(0, lastSep); + if (parentPath.length() == 0) + parentPath = "/"; + componentName = subName.Substring(lastSep + 1); + } + else + { + parentPath = "."; + componentName = subName; + } + + bool found = false; + DIR* dir = opendir(parentPath.c_str()); + if (dir != NULL) + { + struct dirent* entry = NULL; + while ((entry = readdir(dir)) != NULL) + { + // Linux is case-sensitive, but we do a case-insensitive comparison + // to help find the correct case for the file + if (strcasecmp(entry->d_name, componentName.c_str()) == 0) + { + outPath.Append(entry->d_name); + found = true; + break; + } + } + closedir(dir); + } + + if (!found) + { + // If not found, use the original component name + outPath.Append(inPath.Substring(lastComponentStart, i - lastComponentStart)); + } + + ++i; + // Ignore multiple slashes in a row + while ((i < length) && ((inPath[i] == DIR_SEP_CHAR) || (inPath[i] == DIR_SEP_CHAR_ALT))) + ++i; + + lastComponentStart = i; + addSeparator = true; + } + + TryStringOut(outPath, outPathC, inOutPathSize, (BfpResult*)outResult); } // BfpFindFileData diff --git a/BeefySysLib/platform/win/Platform.cpp b/BeefySysLib/platform/win/Platform.cpp index fe64d26b..09a665b5 100644 --- a/BeefySysLib/platform/win/Platform.cpp +++ b/BeefySysLib/platform/win/Platform.cpp @@ -553,18 +553,46 @@ struct BfpOverlappedFile : BfpOverlapped } }; -struct BfpAsyncData -{ - HANDLE mEvent; +struct OverlappedReadResult : OVERLAPPED +{ + BfpFile* mFile; + intptr mBytesRead; + DWORD mErrorCode; + Array mData; + bool mPending; + bool mAttemptedRead; - BfpAsyncData() + OverlappedReadResult() { + mFile = NULL; + mBytesRead = 0; + mErrorCode = 0; + mPending = false; + mAttemptedRead = false; + } + + void* GetPtr() + { + return mData.mVals; + } +}; + +struct BfpAsyncData +{ + BfpFile* mFile; + Array mQueuedData; + HANDLE mEvent; + OverlappedReadResult mOverlappedResult; + + BfpAsyncData(BfpFile* file) + { + mFile = file; mEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); } ~BfpAsyncData() { - ::CloseHandle(mEvent); + ::CloseHandle(mEvent); } void SetEvent() @@ -587,6 +615,98 @@ struct BfpAsyncData return false; } } + + int ReadQueued(void* buffer, int size, int timeoutMS, bool& didWait) + { + gBfpCritSect.Lock(); + + if (mQueuedData.mSize == 0) + { + if (mOverlappedResult.mPending) + { + gBfpCritSect.Unlock(); + WaitAndResetEvent(timeoutMS); + didWait = true; + gBfpCritSect.Lock(); + } + if (mQueuedData.mSize == 0) + { + gBfpCritSect.Unlock(); + return 0; + } + } + + int readSize = BF_MIN(size, mQueuedData.mSize); + memcpy(buffer, mQueuedData.mVals, readSize); + mQueuedData.RemoveRange(0, readSize); + + gBfpCritSect.Unlock(); + return readSize; + } + + void HandleResult(uint32 errorCode, uint32 bytesRead) + { + AutoCrit autoCrit(gBfpCritSect); + BF_ASSERT(mOverlappedResult.mPending); + mOverlappedResult.mPending = false; + mOverlappedResult.mErrorCode = errorCode; + mOverlappedResult.mBytesRead = bytesRead; + if (mOverlappedResult.mAttemptedRead) // Already tried to read and failed + { + mQueuedData.Insert(mQueuedData.mSize, (uint8*)mOverlappedResult.GetPtr(), bytesRead); + mOverlappedResult.mData.Clear(); + } + SetEvent(); + } + + void AbortOverlapped() + { + AutoCrit autoCrit(gBfpCritSect); + BF_ASSERT(mOverlappedResult.mPending); + mOverlappedResult.mPending = false; + mOverlappedResult.mData.Clear(); + } + + int FinishRead(void* buffer, int size, DWORD& errorCode) + { + AutoCrit autoCrit(gBfpCritSect); + BF_ASSERT(!mOverlappedResult.mAttemptedRead); + mOverlappedResult.mAttemptedRead = true; + if (mOverlappedResult.mPending) + { + return -2; // Still executing + } + + if ((mOverlappedResult.mErrorCode != 0) && (mOverlappedResult.mBytesRead == 0)) + { + mOverlappedResult.mData.Clear(); + errorCode = mOverlappedResult.mErrorCode; + return -1; + } + + BF_ASSERT(size >= mOverlappedResult.mBytesRead); + memcpy(buffer, mOverlappedResult.GetPtr(), mOverlappedResult.mBytesRead); + int bytesRead = (int)mOverlappedResult.mBytesRead; + mOverlappedResult.mData.Clear(); + return bytesRead; + } + + OverlappedReadResult* StartOverlapped(int size) + { + AutoCrit autoCrit(gBfpCritSect); + + BF_ASSERT(!mOverlappedResult.mPending); + BF_ASSERT(mOverlappedResult.mData.IsEmpty()); + + memset(&mOverlappedResult, 0, sizeof(OVERLAPPED)); + mOverlappedResult.mFile = mFile; + mOverlappedResult.mBytesRead = 0; + mOverlappedResult.mPending = true; + mOverlappedResult.mAttemptedRead = false; + mOverlappedResult.mErrorCode = -1; + mOverlappedResult.mData.Resize(size); + return &mOverlappedResult; + } }; struct BfpFile @@ -618,6 +738,12 @@ struct BfpFile } }; +static void WINAPI OverlappedReadComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) +{ + OverlappedReadResult* readResult = (OverlappedReadResult*)lpOverlapped; + readResult->mFile->mAsyncData->HandleResult(dwErrorCode, dwNumberOfBytesTransfered); +} + struct BfpFileWatcher : public BfpOverlapped { String mPath; @@ -2952,7 +3078,7 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateK if (isOverlapped) { - bfpFile->mAsyncData = new BfpAsyncData(); + bfpFile->mAsyncData = new BfpAsyncData(bfpFile); } return bfpFile; @@ -3048,7 +3174,7 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateK bfpFile->mHandle = handle; if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0) - bfpFile->mAsyncData = new BfpAsyncData(); + bfpFile->mAsyncData = new BfpAsyncData(bfpFile); if ((createFlags & BfpFileCreateFlag_Pipe) != 0) bfpFile->mIsPipe = true; @@ -3142,21 +3268,6 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer, return bytesWritten; } -struct OverlappedReadResult : OVERLAPPED -{ - BfpFile* mFile; - intptr mBytesRead; - DWORD mErrorCode; -}; - -static void WINAPI OverlappedReadComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) -{ - OverlappedReadResult* readResult = (OverlappedReadResult*)lpOverlapped; - readResult->mErrorCode = dwErrorCode; - readResult->mBytesRead = dwNumberOfBytesTransfered; - readResult->mFile->mAsyncData->SetEvent(); -} - BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult) { bool forceNormalRead = false; @@ -3208,6 +3319,23 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr } } + if (file->mAsyncData != NULL) + { + bool didWait = false; + int readSize = file->mAsyncData->ReadQueued(buffer, (int)size, timeoutMS, didWait); + if (readSize > 0) + { + OUTRESULT(BfpFileResult_Ok); + return readSize; + } + + if (didWait) + { + OUTRESULT(BfpFileResult_Timeout); + return 0; + } + } + if ((timeoutMS != -1) && (!forceNormalRead)) { if (file->mAsyncData == NULL) @@ -3217,52 +3345,55 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr } while (true) - { - OverlappedReadResult overlapped; - memset(&overlapped, 0, sizeof(OverlappedReadResult)); - overlapped.mFile = file; + { + OverlappedReadResult* overlapped = file->mAsyncData->StartOverlapped((int)size); //TODO: this doesn't set file stream location. It only works for streams like pipes, sockets, etc - if (::ReadFileEx(file->mHandle, buffer, (uint32)size, &overlapped, OverlappedReadComplete)) - { - if (!file->mAsyncData->WaitAndResetEvent(timeoutMS)) + if (::ReadFileEx(file->mHandle, overlapped->GetPtr(), (uint32)size, overlapped, OverlappedReadComplete)) + { + file->mAsyncData->WaitAndResetEvent(timeoutMS); + + DWORD errorCode = 0; + int readResult = file->mAsyncData->FinishRead(buffer, (int)size, errorCode); + if (readResult != -2) // Still executing { - ::CancelIoEx(file->mHandle, &overlapped); - // There's a chance we completed before we were cancelled -- check on that - if (!file->mAsyncData->WaitAndResetEvent(0)) + if (errorCode == 0) + { + OUTRESULT(BfpFileResult_Ok); + return readResult; + } + else if (errorCode == ERROR_OPERATION_ABORTED) { OUTRESULT(BfpFileResult_Timeout); return 0; } + else + { + OUTRESULT(BfpFileResult_Ok); + return 0; + } } - - if (overlapped.mErrorCode == 0) - { - - } - else if (overlapped.mErrorCode == ERROR_OPERATION_ABORTED) - { + else + { OUTRESULT(BfpFileResult_Timeout); - return 0; - } + return 0; + } } else - { + { + file->mAsyncData->AbortOverlapped(); + int lastError = ::GetLastError(); if (lastError == ERROR_PIPE_LISTENING) { - overlapped.hEvent = file->mAsyncData->mEvent; - if (!::ConnectNamedPipe(file->mHandle, &overlapped)) + overlapped->hEvent = file->mAsyncData->mEvent; + if (!::ConnectNamedPipe(file->mHandle, overlapped)) { int lastError = ::GetLastError(); if (lastError == ERROR_IO_PENDING) { if (!file->mAsyncData->WaitAndResetEvent(timeoutMS)) { - ::CancelIoEx(file->mHandle, &overlapped); - // Clear event set by CancelIoEx - file->mAsyncData->WaitAndResetEvent(0); - OUTRESULT(BfpFileResult_Timeout); return 0; } @@ -3277,10 +3408,7 @@ BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr OUTRESULT(BfpFileResult_UnknownError); return 0; } - } - - OUTRESULT(BfpFileResult_Ok); - return overlapped.mBytesRead; + } } } diff --git a/BeefySysLib/util/BeefPerf.cpp b/BeefySysLib/util/BeefPerf.cpp index e1dae1c1..4bd150fb 100644 --- a/BeefySysLib/util/BeefPerf.cpp +++ b/BeefySysLib/util/BeefPerf.cpp @@ -430,6 +430,11 @@ void BpCmdTarget::Enter(const char* name, va_list args) intptr val = va_arg(args, intptr); paramSize += 4; // int32 } + else if (nextC == 'p') + { + intptr val = va_arg(args, intptr); + paramSize += 8; // int64 + } else if (nextC == 's') { const char* str = ToStrPtr(va_arg(args, char*)); @@ -488,6 +493,10 @@ void BpCmdTarget::Enter(const char* name, va_list args) { BPCMD_MEMBER(int32) = (int32)va_arg(args, intptr); } + else if (nextC == 'p') + { + BPCMD_MEMBER(int64) = (int64)va_arg(args, intptr); + } else if (nextC == 's') { const char* str = ToStrPtr(va_arg(args, char*)); @@ -1120,6 +1129,10 @@ void BpManager::ThreadProc() { zoneName->mSize += 4; } + else if (nextC == 'p') + { + zoneName->mSize += 8; + } else if (nextC == 's') { isDyn = true; @@ -1175,6 +1188,10 @@ void BpManager::ThreadProc() { checkDataIn += 4; } + else if (nextC == 'p') + { + checkDataIn += 8; + } else if (nextC == 's') { int len = (int)strlen((const char*)checkDataIn); diff --git a/BeefySysLib/util/MemLogger.cpp b/BeefySysLib/util/MemLogger.cpp new file mode 100644 index 00000000..4ddf235b --- /dev/null +++ b/BeefySysLib/util/MemLogger.cpp @@ -0,0 +1,176 @@ +#include "MemLogger.h" + +USING_NS_BF; + +struct MemLogger_Header +{ +public: + int mHead; + int mTail; + int mSize; +}; + +MemLogger::MemLogger() +{ + mFileMap = NULL; + mMemBuffer = NULL; + mBufferSize = 0; + mTotalWriteSize = 0; + mNoOverflow = false; +} + +MemLogger::~MemLogger() +{ +#ifdef BF_PLATFORM_WINDOWS + if (mMemBuffer != NULL) + ::UnmapViewOfFile(mMemBuffer); + if (mFileMap != NULL) + ::CloseHandle(mFileMap); +#endif +} + +void MemLogger::Write(const void* ptr, int size) +{ + if (mMemBuffer == NULL) + return; + + int dataSize = mBufferSize - sizeof(MemLogger_Header); + void* dataPtr = (uint8*)mMemBuffer + sizeof(MemLogger_Header); + + if (mNoOverflow) + size = BF_MIN(size, dataSize - mTotalWriteSize - 1); + + if (size <= 0) + return; + + MemLogger_Header* header = (MemLogger_Header*)mMemBuffer; + + bool wasWrapped = header->mHead < header->mTail; + + int writeSize = BF_MIN(size, dataSize - header->mHead); + memcpy((char*)dataPtr + header->mHead, ptr, writeSize); + size -= writeSize; + + header->mHead += writeSize; + while (header->mHead >= dataSize) + header->mHead -= dataSize; + + if (size > 0) + { + int writeSize2 = BF_MIN(size, dataSize - header->mHead); + memcpy((char*)dataPtr + header->mHead, (char*)ptr + writeSize, writeSize2); + header->mHead += writeSize2; + while (header->mHead >= dataSize) + header->mHead -= dataSize; + } + + mTotalWriteSize += writeSize; + + if (mTotalWriteSize >= dataSize) + { + header->mTail = header->mHead + 1; + if (header->mTail > dataSize) + header->mTail -= dataSize; + } +} + +bool Beefy::MemLogger::Create(const StringImpl& memName, int size) +{ +#ifdef BF_PLATFORM_WINDOWS + String sharedName = "MemLogger_" + memName; + HANDLE hMapFile = CreateFileMappingA( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + size, // maximum object size (low-order DWORD) + sharedName.c_str()); // name of mapping object + + if (hMapFile == NULL) + return false; + + mMemBuffer = MapViewOfFile(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + size); + + if (mMemBuffer == NULL) + return false; + + mBufferSize = size; + + MemLogger_Header* header = (MemLogger_Header*)mMemBuffer; + header->mHead = 0; + header->mTail = 0; + header->mSize = size; + return true; +#else + return false; +#endif +} + +bool Beefy::MemLogger::Get(const StringImpl& memName, String& outStr) +{ +#ifdef BF_PLATFORM_WINDOWS + String sharedName = "MemLogger_" + memName; + HANDLE hMapFile = ::OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, sharedName.c_str()); + if (hMapFile == NULL) + return false; + + void* memPtr = MapViewOfFile(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + sizeof(MemLogger_Header)); + + MemLogger_Header* header = (MemLogger_Header*)(memPtr); + int size = header->mSize; + UnmapViewOfFile(memPtr); + + memPtr = MapViewOfFile(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + size); + + if (memPtr == NULL) + return false; + + ::CloseHandle(hMapFile); + + header = (MemLogger_Header*)(memPtr); + + int dataSize = header->mSize - sizeof(MemLogger_Header); + void* dataPtr = (uint8*)memPtr + sizeof(MemLogger_Header); + + if (header->mHead >= header->mTail) + { + // Not wrapped around + outStr.Insert(outStr.mLength, (char*)dataPtr + header->mTail, header->mHead - header->mTail); + } + else + { + outStr.Insert(outStr.mLength, (char*)dataPtr + header->mTail, dataSize - header->mTail); + outStr.Insert(outStr.mLength, (char*)dataPtr, header->mHead); + } + + return true; +#else + return false; +#endif +} + +void Beefy::MemLogger::Log(const char* fmt ...) +{ + if (mMemBuffer == NULL) + return; + + StringT<4096> str; + va_list argList; + va_start(argList, fmt); + vformat(str, fmt, argList); + va_end(argList); + + Write(str.c_str(), str.mLength); +} diff --git a/BeefySysLib/util/MemLogger.h b/BeefySysLib/util/MemLogger.h new file mode 100644 index 00000000..f1148e63 --- /dev/null +++ b/BeefySysLib/util/MemLogger.h @@ -0,0 +1,29 @@ +#pragma once + +#include "../Common.h" + +NS_BF_BEGIN + +class MemLogger +{ +public: + HANDLE mFileMap; + void* mMemBuffer; + int mBufferSize; + int mTotalWriteSize; + bool mNoOverflow; + +public: + MemLogger(); + + ~MemLogger(); + + bool Create(const StringImpl& memName, int size); + bool Get(const StringImpl& memName, String& outStr); + void Log(const char* fmt ...); + void Write(const void* ptr, int size); +}; + + + +NS_BF_END diff --git a/BeefySysLib/util/Rect.h b/BeefySysLib/util/Rect.h index 4b38d47a..b8848c99 100644 --- a/BeefySysLib/util/Rect.h +++ b/BeefySysLib/util/Rect.h @@ -119,13 +119,13 @@ typedef Rect RectD; typedef Rect RectF; typedef Rect RectI32; +NS_BF_END; + template <> -struct BeefHash +struct BeefHash { - size_t operator()(RectI32 val) + size_t operator()(Beefy::RectI32 val) { return (size_t)val.x * 4790557 + (size_t)val.y * 6578863 + (size_t)val.width * 6273881 + (size_t)val.height * 9501077; } -}; - -NS_BF_END; \ No newline at end of file +}; \ No newline at end of file diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index e3f09e65..271510cb 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -3461,6 +3461,8 @@ namespace IDE delete mWorkspace.mDir; mWorkspace.mDir = newPath; } + else if (mWorkspace.mDir == null) + mWorkspace.mDir = new String(); List platforms = scope List(); if (IDEApp.sPlatform32Name != null) diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index 8eccab69..13cbea2e 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -1179,14 +1179,18 @@ namespace IDE.ui if (!docString.IsWhiteSpace) { - curY += font.GetLineSpacing() + GS!(4); + if (g != null) { + let docY = curY + font.GetLineSpacing() + GS!(4); + using (g.PushColor(gApp.mSettings.mUISettings.mColors.mAutoCompleteDocText)) - docHeight = g.DrawString(docString, curX, curY, .Left, maxDocWidth, .Wrap); + docHeight = g.DrawString(docString, curX, docY, .Left, maxDocWidth, .Wrap); } else docHeight = font.GetWrapHeight(docString, maxDocWidth); + + curY += docHeight; } extWidth = Math.Max(extWidth, Math.Min(font.GetWidth(docString), maxDocWidth) + GS!(48)); diff --git a/IDE/src/util/GitManager.bf b/IDE/src/util/GitManager.bf index dd845c02..daa33186 100644 --- a/IDE/src/util/GitManager.bf +++ b/IDE/src/util/GitManager.bf @@ -95,6 +95,8 @@ class GitManager if (!File.Exists(gitPath)) gitPath.Clear(); } + + psi.UseShellExecute = false; #endif if (gitPath.IsEmpty) gitPath.Set("git"); @@ -103,7 +105,6 @@ class GitManager psi.SetArguments(mArgs); if (mPath != null) psi.SetWorkingDirectory(mPath); - psi.UseShellExecute = false; psi.RedirectStandardError = true; psi.RedirectStandardOutput = true; psi.CreateNoWindow = true; diff --git a/IDE/src/util/PackMan.bf b/IDE/src/util/PackMan.bf index bb9fc14c..8e47aa78 100644 --- a/IDE/src/util/PackMan.bf +++ b/IDE/src/util/PackMan.bf @@ -179,7 +179,12 @@ namespace IDE.util if (!CheckInit()) return; - String beefBuildPath = scope $"{gApp.mInstallDir}BeefBuild.exe"; +#if BF_PLATFORM_WINDOWS + let ext = ".exe"; +#else + let ext = ""; +#endif + String beefBuildPath = scope $"{gApp.mInstallDir}BeefBuild{ext}"; String args = scope $"-run"; var execInst = gApp.DoRun(beefBuildPath, args, path, .None); execInst?.mAutoDelete = false; diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index 9af28c5f..515eaa97 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -3296,6 +3296,9 @@ void BfAutoComplete::CheckLabel(BfIdentifierNode* identifierNode, BfAstNode* pre String filter; if (identifierNode != NULL) { + if (mModule->mCurMethodState == NULL) + return; + if ((mModule->mCompiler->mResolvePassData != NULL) && (scopeData != NULL)) { auto rootMethodState = mModule->mCurMethodState->GetRootMethodState(); diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index aff318a3..fa985b46 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -7222,15 +7222,33 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mHasRequiredTypes = true; //HashSet internalTypeDefs; + + BfProject* corlibProject = NULL; auto _GetRequiredType = [&](const StringImpl& typeName, int genericArgCount = 0) { - auto typeDef = mSystem->FindTypeDef(typeName, genericArgCount); + BfTypeDef* ambigiousTypeDef = NULL; + auto typeDef = mSystem->FindTypeDef(typeName, genericArgCount, NULL, {}, &ambigiousTypeDef); if (typeDef == NULL) { mPassInstance->Fail(StrFormat("Unable to find system type: %s", typeName.c_str())); mHasRequiredTypes = false; } + + if (ambigiousTypeDef != NULL) + { + mPassInstance->Fail(StrFormat("Found multiple declarations of require type '%s'", typeName.c_str()), typeDef->GetRefNode()); + mPassInstance->MoreInfo("See additional declaration", ambigiousTypeDef->GetRefNode()); + if (typeDef->mProject != corlibProject) + { + auto rootTypeDef = mSystem->FindTypeDef(typeName, genericArgCount, corlibProject); + if (rootTypeDef != NULL) + typeDef = rootTypeDef; + } + } + + if (corlibProject == NULL) + corlibProject = typeDef->mProject; return typeDef; }; diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 8ae94c8d..992cc29d 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -7125,7 +7125,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* if (mDeferCallData != NULL) { - if (mDeferCallData->mFuncAlloca_Orig == func) + if ((func) && (mDeferCallData->mFuncAlloca_Orig == func)) mModule->AddDeferredCall(BfModuleMethodInstance(methodInstance, mDeferCallData->mFuncAlloca), irArgs, mDeferCallData->mScopeAlloc, mDeferCallData->mRefNode, bypassVirtual, false, true); else mModule->AddDeferredCall(BfModuleMethodInstance(methodInstance, func), irArgs, mDeferCallData->mScopeAlloc, mDeferCallData->mRefNode, bypassVirtual); @@ -19701,7 +19701,7 @@ void BfExprEvaluator::DoInvocation(BfInvocationExpression* invocationExpr) { arrSize = constant->mInt32; } - else + else if (constant->mConstType != BfConstType_Undef) mModule->Fail("Non-negative integer expected", indexerExpr->mArguments[0]); } } diff --git a/IDEHelper/Compiler/BfIRCodeGen.cpp b/IDEHelper/Compiler/BfIRCodeGen.cpp index 0a227cf4..4531ad2e 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.cpp +++ b/IDEHelper/Compiler/BfIRCodeGen.cpp @@ -552,6 +552,31 @@ void BfIRCodeGen::FixValues(llvm::StructType* structType, llvm::SmallVector& values) +{ + if (values.size() >= structType->getNumElements()) + return; + + int readIdx = (int)values.size() - 1; + values.resize(structType->getNumElements()); + for (int i = (int)values.size() - 1; i >= 0; i--) + { + if (values[readIdx]->getType() == structType->getElementType(i)) + { + values[i] = values[readIdx]; + readIdx--; + } + else if (structType->getElementType(i)->isArrayTy()) + { + values[i] = llvm::ConstantAggregateZero::get(structType->getElementType(i)); + } + else + { + BF_FATAL("Malformed structure values"); + } + } +} + void BfIRCodeGen::FixIndexer(llvm::Value*& val) { if ((int)val->getType()->getScalarSizeInBits() > mPtrSize * 8) @@ -1285,6 +1310,8 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt } else if (auto structType = llvm::dyn_cast(type->mLLVMType)) { + FixValues(structType, values); + for (int i = 0; i < (int)values.size(); i++) { if (values[i]->getType() != structType->getElementType(i)) diff --git a/IDEHelper/Compiler/BfIRCodeGen.h b/IDEHelper/Compiler/BfIRCodeGen.h index 2b20e8f8..e3afce84 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.h +++ b/IDEHelper/Compiler/BfIRCodeGen.h @@ -178,6 +178,7 @@ public: public: void InitTarget(); void FixValues(llvm::StructType* structType, llvm::SmallVector& values); + void FixValues(llvm::StructType* structType, llvm::SmallVector& values); void FixIndexer(llvm::Value*& val); void FixTypedValue(BfIRTypedValue& typedValue); BfTypeCode GetTypeCode(llvm::Type* type, bool isSigned); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index af827b7a..55e1477d 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -13415,6 +13415,8 @@ BfIRValue BfModule::CastToFunction(BfAstNode* srcNode, const BfTypedValue& targe } if ((mCompiler->mOptions.mAllowHotSwapping) && (!mIsComptimeModule)) bindFuncVal = mBfIRBuilder->RemapBindFunction(bindFuncVal); + if ((bindFuncVal.IsFake()) && (!mBfIRBuilder->mIgnoreWrites)) + return GetDefaultValue(GetPrimitiveType(BfTypeCode_IntPtr)); return mBfIRBuilder->CreatePtrToInt(bindFuncVal, BfTypeCode_IntPtr); } diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 653e5367..0d14a95e 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -1837,6 +1837,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat else { Fail("Sized array type expected", typeRef); + AddErrorNode(typeRef); + return NULL; } } } diff --git a/IDEHelper/Tests/src/Functions.bf b/IDEHelper/Tests/src/Functions.bf index 6bf073b9..820c4509 100644 --- a/IDEHelper/Tests/src/Functions.bf +++ b/IDEHelper/Tests/src/Functions.bf @@ -161,6 +161,7 @@ namespace Tests interface ITest { + void TestFunc(); static void Func(); } @@ -178,6 +179,8 @@ namespace Tests { public static int sVal; + public void TestFunc() => Func(); + public static void Func() { sVal = 123; @@ -189,6 +192,12 @@ namespace Tests if (func != null) defer:: func.Invoke(); } + + public static void TestIFaceDefer() + { + ITest itest = scope Zoop(); + defer itest.TestFunc(); + } } public static int UseFunc0(function int (T this, float f) func, T a, float b) @@ -265,6 +274,10 @@ namespace Tests Zoop.sVal = 0; Zoop.TestDefer(); Test.Assert(Zoop.sVal == 123); + + Zoop.sVal = 0; + Zoop.TestIFaceDefer(); + Test.Assert(Zoop.sVal == 123); } } } diff --git a/IDEHelper/WinDebugger.cpp b/IDEHelper/WinDebugger.cpp index e95f6a96..49fea559 100644 --- a/IDEHelper/WinDebugger.cpp +++ b/IDEHelper/WinDebugger.cpp @@ -2660,25 +2660,33 @@ bool WinDebugger::DoUpdate() } } - BF_ASSERT(foundBreakpoint != NULL); - - DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress); - if (CheckConditionalBreakpoint(foundBreakpoint, subprogram, pcAddress)) + if (foundBreakpoint == NULL) { - if (foundBreakpoint != NULL) - { - mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(foundBreakpoint->mMemoryBreakpointInfo->mMemoryAddress, false).c_str())); - mRunState = RunState_Paused; - } - - mActiveBreakpoint = foundBreakpoint; - mBreakStackFrameIdx = -1; - RemoveTempBreakpoints(); - BfLogDbg("Memory breakpoint hit: %p\n", foundBreakpoint); + BfLogDbg("Unknown memory breakpoint hit %p\n", pcAddress); + mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(pcAddress, false).c_str())); + mRunState = RunState_Paused; + break; } else - ClearCallStack(); - break; + { + DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress); + if (CheckConditionalBreakpoint(foundBreakpoint, subprogram, pcAddress)) + { + if (foundBreakpoint != NULL) + { + mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(foundBreakpoint->mMemoryBreakpointInfo->mMemoryAddress, false).c_str())); + mRunState = RunState_Paused; + } + + mActiveBreakpoint = foundBreakpoint; + mBreakStackFrameIdx = -1; + RemoveTempBreakpoints(); + BfLogDbg("Memory breakpoint hit: %p\n", foundBreakpoint); + } + else + ClearCallStack(); + break; + } } if ((mRunState == RunState_DebugEval) && (mDebugEvalThreadInfo.mThreadId == mDebuggerWaitingThread->mThreadId)) @@ -7507,7 +7515,7 @@ String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, c String symbolName; addr_target offset; DbgModule* dwarf; - static String demangledName; + String demangledName; auto subProgram = mDebugTarget->FindSubProgram(funcPtr); if (subProgram != NULL) { @@ -7524,13 +7532,18 @@ String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, c { auto dbgModule = mDebugTarget->FindDbgModuleForAddress(funcPtr); if (dbgModule != NULL) + { demangledName += dbgModule->GetLinkedModule()->mDisplayName + "!"; - demangledName += StrFormat("0x%@", funcPtr); + demangledName += StrFormat("0x%@", funcPtr); + } } - retVal += " {"; - retVal += demangledName; - retVal += "}"; + if (!demangledName.IsEmpty()) + { + retVal += " {"; + retVal += demangledName; + retVal += "}"; + } retVal += "\n" + origValueType->ToString(language); return retVal;